android 消息机制

一、Android应用程序的主线程主要用于更新UI界面,并且主线程不能做耗时操作,否则会引起ANR;这种情况下需要开一个子线程来进行耗时操作,动作完成之后,子线程发消息给主线程通知其更新UI显示,常见方法有:

  • Activity.runOnUiThread(Runnable);
  • View.post(Runnable);
  • View.postDelayed(Runnable, long);
  • Handler消息机制。

注:经过看源码,会发现Activity.runOnUiThread(Runnable),View.post(Runnable),View.postDelayed(Runnable, long)最终本质上会调用到Handler发送消息的方法,如下代码:

Activity.runOnUiThread(Runnable);

 1 /**
 2      * Runs the specified action on the UI thread. If the current thread is the UI
 3      * thread, then the action is executed immediately. If the current thread is
 4      * not the UI thread, the action is posted to the event queue of the UI thread.
 5      *
 6      * @param action the action to run on the UI thread
 7      */
 8     public final void runOnUiThread(Runnable action) {
 9         if (Thread.currentThread() != mUiThread) {
10             mHandler.post(action);
11         } else {
12             action.run();
13         }
14     }

View.Post(Runnable),View.PostDelayed(Runnable); 请忽略attachInfo判断。

 1 public boolean post(Runnable action) {
 2         final AttachInfo attachInfo = mAttachInfo;
 3         if (attachInfo != null) {
 4             return attachInfo.mHandler.post(action);
 5         }
 6         // Assume that post will succeed later
 7         ViewRootImpl.getRunQueue().post(action);
 8         return true;
 9     }
10 
11 public boolean postDelayed(Runnable action, long delayMillis) {
12         final AttachInfo attachInfo = mAttachInfo;
13         if (attachInfo != null) {
14             return attachInfo.mHandler.postDelayed(action, delayMillis);
15         }
16         // Assume that post will succeed later
17         ViewRootImpl.getRunQueue().postDelayed(action, delayMillis);
18         return true;
19     }

 二、弄清消息机制之前,提一个问题:如何在子线程实例化一个Handler发送message呢?代码很简单,如下

 1 new Thread(){
 2 
 3             @Override
 4             public void run() {
 5                 super.run();
 6                 Looper.prepare();
 7                 new Handler().sendEmptyMessage(1);
 8                 Looper.loop();
 9             }
10             
11 }.start();

接下来,问题又来了:

1.发消息的时候为什么要调用Looper.prepare();

2.消息发送后为什么要调用Looper.loop();

3.为什么在主线程里发送信息,我们没有调用 这两个方法呢?

在解答这三个问题之前,先看一张图,大致了解一下消息机制是如何运行的?如下图:

总结一下上面那个图片:在UI线程中有一个消息队列MessageQueue,其它线程do something之后,在UI线程中的消息队列MessageQueue插入Message,而Looper负责轮循消息队列MessageQueue。然后来回答上面第1个问题,看代码:

 1 public static void prepare() {
 2         prepare(true);
 3  }
 4 
 5 private static void prepare(boolean quitAllowed) {
 6         if (sThreadLocal.get() != null) {
 7             throw new RuntimeException("Only one Looper may be created per thread");
 8         }
 9         sThreadLocal.set(new Looper(quitAllowed));
10  }
11 
12 //初始化Looper,并且初始化消息队列
13 private Looper(boolean quitAllowed) {
14         mQueue = new MessageQueue(quitAllowed);
15         mThread = Thread.currentThread();
16  }

通过上图可得知,接收一个消息的前提是,该线程得拥有自己的消息队列MessageQueue,而第1个问题的答案就是创建一个该线程接收消息的一个消息队列。然后第二个问题请看以下代码:

 1 public static void loop() {
 2         final Looper me = myLooper();
 3         if (me == null) {
 4             throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
 5         }
 6         final MessageQueue queue = me.mQueue;
 7 
 8         // Make sure the identity of this thread is that of the local process,
 9         // and keep track of what that identity token actually is.
10         Binder.clearCallingIdentity();
11         final long ident = Binder.clearCallingIdentity();
12 
13         for (;;) {
14             Message msg = queue.next(); // might block
15             if (msg == null) {
16                 // No message indicates that the message queue is quitting.
17                 return;
18             }
19 
20             // This must be in a local variable, in case a UI event sets the logger
21             Printer logging = me.mLogging;
22             if (logging != null) {
23                 logging.println(">>>>> Dispatching to " + msg.target + " " +
24                         msg.callback + ": " + msg.what);
25             }
26 
27             msg.target.dispatchMessage(msg);
28 
29             if (logging != null) {
30                 logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
31             }
32 
33             // Make sure that during the course of dispatching the
34             // identity of the thread wasn't corrupted.
35             final long newIdent = Binder.clearCallingIdentity();
36             if (ident != newIdent) {
37                 Log.wtf(TAG, "Thread identity changed from 0x"
38                         + Long.toHexString(ident) + " to 0x"
39                         + Long.toHexString(newIdent) + " while dispatching to "
40                         + msg.target.getClass().getName() + " "
41                         + msg.callback + " what=" + msg.what);
42             }
43 
44             msg.recycleUnchecked();
45         }
46  }

通过上图可知消息机制中除了消息队列MessageQueue,还得有轮循,第二个问题就是执行轮循的操作。第三个问题请往下看代码:

 1 public final class ActivityThread {
 2     ......
 3 
 4     public static final void main(String[] args) {
 5         ......
 6         //将当前线程初始化为Looper线程。最终会调用Looper.prepare()
 7         Looper.prepareMainLooper();
 8 
 9         ......
10         // 开始循环处理消息队列
11         Looper.loop();
12 
13         ......
14     }
15 }

第三个问题的答案:android应用程序启动过程中,会在进程中执行ActivityThread中main方法,来初始化该应用中UI线程的Looper。这也就是为什么主线程里可以直接调用Handler,而子线程不能直接调用Handler发送消息。

参考:http://3dobe.com/archives/74/

原文地址:https://www.cnblogs.com/fanglove/p/4672255.html