在上一篇中我们通过handler的发送消息方法实现了计时器的功能。在子线程中发送更新消息,主线程中来处理消息。那么是不是只能是主线程处理消息呢?其他线程要想处理消息又该如何实现呢?

实际上:消息发送和计划任务提交之后,它们都会进入某线程的消息队列中,我们可以把这个线程称之为目标线程。不论是主线程还是子线程都可以成为目标线程。上例中之所以在主线程中处理消息,是因为我们要更新UI,按照android中的规定我们必须由主线程更新UI。所以我们让主线程成为了目标线程。

那么如何控制让某个线程成为目标线程呢?

这就引出了Looper的概念。Android系统中实现了消息循环机制,Android的消息循环是针对线程的,每个线程都可以有自己的消息队列和消息循环。Android系统中的通过Looper帮助线程维护着一个消息队列和消息循环。通过Looper.myLooper()得到当前线程的Looper对象,通过Looper.getMainLooper()得到当前进程的主线程的Looper对象。如Looper中的部分源码:

public class Looper {    private static final boolean DEBUG = false;    private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;    // sThreadLocal.get() will return null unless you've called prepare().    private static final ThreadLocal sThreadLocal = new ThreadLocal();    final MessageQueue mQueue;    volatile boolean mRun;    Thread mThread;    private Printer mLogging = null;    private static Looper mMainLooper = null;         /** Initialize the current thread as a looper.      * This gives you a chance to create handlers that then reference      * this looper, before actually starting the loop. Be sure to call      * {@link #loop()} after calling this method, and end it by calling      * {@link #quit()}.      */    public static final void prepare() {        if (sThreadLocal.get() != null) {            throw new RuntimeException("Only one Looper may be created per thread");        }        sThreadLocal.set(new Looper());    }public static final void loop() {        Looper me = myLooper();        MessageQueue queue = me.mQueue;        while (true) {            Message msg = queue.next(); // might block            //if (!me.mRun) {            //    break;            //}            if (msg != null) {                if (msg.target == null) {                    // No target is a magic identifier for the quit message.                    return;                }                if (me.mLogging!= null) me.mLogging.println(                        ">>>>> Dispatching to " + msg.target + " "                        + msg.callback + ": " + msg.what                        );                msg.target.dispatchMessage(msg);                if (me.mLogging!= null) me.mLogging.println(                        "<<<<< Finished to    " + msg.target + " "                        + msg.callback);                msg.recycle();            }        }    }


前面提到每个线程都可以有自己的消息队列和消息循环,然而我们自己创建的线程默认是没有消息队列和消息循环的(及Looper),要想让一个线程具有消息处理机制我们应该在线程中先调用Looper.prepare()来创建一个Looper对象,然后调用Looper.loop()进入消息循环。如上面的源码所示。示例代码如下:

class LooperThread extends Thread {       public Handler mHandler;              public void run() {           Looper.prepare();                      mHandler = new Handler() {              public void handleMessage(Message msg) {                   // process incoming messages here               }           };                      Looper.loop();       }

当我们用Handler的构造方法创建Handler对象时,指定handler对象与哪个具有消息处理机制的线程(具有Looper的线程)相关联,这个线程就成了目标线程,可以接受消息和计划任务了。Handler中的构造方法如下:

 public Handler() {        if (FIND_POTENTIAL_LEAKS) {            final Class<? extends Handler> klass = getClass();            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&                    (klass.getModifiers() & Modifier.STATIC) == 0) {                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +                    klass.getCanonicalName());            }        }        mLooper = Looper.myLooper();        if (mLooper == null) {            throw new RuntimeException(                "Can't create handler inside thread that has not called Looper.prepare()");        }        mQueue = mLooper.mQueue;        mCallback = null;    } public Handler(Looper looper) {        mLooper = looper;        mQueue = looper.mQueue;        mCallback = null;    }


在上述的计时器的例子中,之所以可以在主线程中处理消息而我们自己并没有调用Looper.prepare()等方法,是因为Android系统在Activity启动时为其创建一个消息队列和消息循环,当我们用无参的Handler构造方法创建对象时又用了当前线程的Looper对象,及将handler与主线程中的Looper对象进行了关联。

android中是使用Looper机制来完成消息循环的,但每次创建线程时都先初始化Looper比较麻烦,因此Android为我们提供了一个HandlerThread类,他封装了Looper对象,是我们不用关心Looper的开启和释放问题。

不管是主线程还是其他线程只要有Looper的线程,别的线程就可以向这个线程的消息队列中发送消息和任务。

我们使用HandlerThread类代替上一篇文章中的子线程,并用HandlerThread类中的Looper对象构造Handler,则接受消息的目标线程就不是主线程了,而是HandlerThread线程。代码如下:

public class clockActivity extends Activity {    /** Called when the activity is first created. */private String TAG="clockActivity";private Button endButton;private TextView textView;private int timer=0;private boolean isRunning=true;private Handler handler;    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);        endButton=(Button)findViewById(R.id.endBtn);        textView=(TextView)findViewById(R.id.textview);        endButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubisRunning=false;}});        HandlerThread thread=new HandlerThread("myThread");        handler=new Handler(thread.getLooper());//与HandlerThread中的Looper对象关联        thread.start();        Runnable r=new Runnable(){@Overridepublic void run() {// TODO Auto-generated method stubif(isRunning){textView.setText("走了"+timer+"秒");timer++;handler.postDelayed(this, 1000);//提交任务r,延时1秒执行}}        };        handler.postDelayed(r, 1000);        }}

此时处理任务会在handlerThread线程中完成。当然这个例子会出线异常:依然是因为在非主线程中更新了UI。这样做只是为了大家能够理解这种机制。

深入理解Android消息处理机制对于应用程序开发非常重要,也可以让我们对线程同步有更加深刻的认识,希望这篇文章可以对朋友们有所帮

更多相关文章

  1. SpringBoot 2.0 中 HikariCP 数据库连接池原理解析
  2. 在android中使用Get方式提交数据
  3. 2018 Android面试心得,已拿到offer
  4. Android用AsyncTask来下载图片及用AsyncTask的好处
  5. 如何实现服务器给android客户端主动推送消息
  6. Android高仿微信之mvp实现(二)
  7. 线程池.(Executors,ThreadPoolExecutor,BlockingQueue,RejectedE
  8. 手机服务器Android消息推送(二)--基于MQTT协议实现的推送功能
  9. BlockCanary — 轻松找出Android(安卓)App界面卡顿元凶

随机推荐

  1. Google工程师解析Android系统架构
  2. Android那些疑惑(1)-Application中setThe
  3. android 休眠唤醒机制分析(二) — early_
  4. Android(安卓)am 指令的使用
  5. Android(安卓)路由框架ARouter最佳实践
  6. Evernote Android(安卓)版更新,给您超便捷
  7. 【Android教程】Android(安卓)Studio找不
  8. Android滚动多TAB悬浮头效果
  9. [原创]Android中LocationManager的简单使
  10. Android学习:资源管理