Android Handler揭秘(一)

简述:

Handler在Android里面到处可见。一般用于多线程消息交互、主线程刷新、延时/定时处理等。今天来揭开Handler背后的秘密。

PS: 如果有想替代Handler用开源库的话,大名鼎鼎的RxJava就是干这事的。

相关代码:

  1. frameworks/base/core/java/android/os/Handler.java
  2. frameworks/base/core/java/android/os/MessageQueue.java
  3. frameworks/base/core/java/android/os/Looper.java

介绍:

从Handler默认构造函数入手:

110      /**
111       * Default constructor associates this handler with the {@link Looper} for the
112       * current thread.
113       *
114       * If this thread does not have a looper, this handler won't be able to receive messages
115       * so an exception is thrown.
116       */
117      public Handler() {
118          this(null, false);
119      }

从注释可以看出,Handler绑定了Looper,而Looper又记录了当前线程等信息,且认为是Handler跟线程强相关。

找到参数为(null, false)的方法,

175      /**
176       * Use the {@link Looper} for the current thread with the specified callback interface
177       * and set whether the handler should be asynchronous.
178       *
179       * Handlers are synchronous by default unless this constructor is used to make
180       * one that is strictly asynchronous.
181       *
182       * Asynchronous messages represent interrupts or events that do not require global ordering
183       * with respect to synchronous messages.  Asynchronous messages are not subject to
184       * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
185       *
186       * @param callback The callback interface in which to handle messages, or null.
187       * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
188       * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
189       *
190       * @hide
191       */
192      public Handler(Callback callback, boolean async) {
193          if (FIND_POTENTIAL_LEAKS) {
194              final Class<? extends Handler> klass = getClass();
195              if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
196                      (klass.getModifiers() & Modifier.STATIC) == 0) {
197                  Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
198                      klass.getCanonicalName());
199              }
200          }
201  
202          mLooper = Looper.myLooper();
203          if (mLooper == null) {
204              throw new RuntimeException(
205                  "Can't create handler inside thread " + Thread.currentThread()
206                          + " that has not called Looper.prepare()");
207          }
208          mQueue = mLooper.mQueue;
209          mCallback = callback;
210          mAsynchronous = async;
211      }

关键代码mLooper = Looper.myLooper()继续进去看

70      // sThreadLocal.get() will return null unless you've called prepare().
71      static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
...

250      /**
251       * Return the Looper object associated with the current thread.  Returns
252       * null if the calling thread is not associated with a Looper.
253       */
254      public static @Nullable Looper myLooper() {
255          return sThreadLocal.get();
256      }

从sThreadlocal里面取出Looper。sThreadLocal,看名字意思就可以大概知道线程本地记录,他可以记录了一个Looper对象(要详细了解ThreadLocal,可以搜下)。既然是get,一般对应的可以找到set的地方。找遍Looper代码可以发现只有一个set的地方:

91      /** Initialize the current thread as a looper.
92        * This gives you a chance to create handlers that then reference
93        * this looper, before actually starting the loop. Be sure to call
94        * {@link #loop()} after calling this method, and end it by calling
95        * {@link #quit()}.
96        */
97      public static void prepare() {
98          prepare(true);
99      }
100  
101      private static void prepare(boolean quitAllowed) {
102          if (sThreadLocal.get() != null) {
103              throw new RuntimeException("Only one Looper may be created per thread");
104          }
105          sThreadLocal.set(new Looper(quitAllowed));
106      }

    从代码可以看出,每个线程只会有一个Looper。prepare只能调用一次,如果调用第二次,就会丢出RuntimeException。继续看Looper的构造函数:

267      private Looper(boolean quitAllowed) {
268          mQueue = new MessageQueue(quitAllowed);
269          mThread = Thread.currentThread();
270      }

    Looper创建了一个MessageQueue,同时引用了当前线程对象。到这里,Handler多线程消息交互,就可以推测出,应该就是靠的这个mQueue了(参数quitAllowed用于区别当前线程是否是主线程)。

    我们回到Handler的构造函数往下看,203行会有一个判断mLooper是否为空。如果是空,就会丢出异常。这里是为了保证在Handler对象初始化的时候,这个mLooper一定绑定过一个线程,也就是一定调用过Looper的prepare方法。在Looper里面的类介绍里面也给出了示例用法:

27  /**
28    * Class used to run a message loop for a thread.  Threads by default do
29    * not have a message loop associated with them; to create one, call
30    * {@link #prepare} in the thread that is to run the loop, and then
31    * {@link #loop} to have it process messages until the loop is stopped.
32    *
33    * <p>Most interaction with a message loop is through the
34    * {@link Handler} class.
35    *
36    * <p>This is a typical example of the implementation of a Looper thread,
37    * using the separation of {@link #prepare} and {@link #loop} to create an
38    * initial Handler to communicate with the Looper.
39    *
40    * <pre>
41    *  class LooperThread extends Thread {
42    *      public Handler mHandler;
43    *
44    *      public void run() {
45    *          Looper.prepare();
46    *
47    *          mHandler = new Handler() {
48    *              public void handleMessage(Message msg) {
49    *                  // process incoming messages here
50    *              }
51    *          };
52    *
53    *          Looper.loop();
54    *      }
55    *  }</pre>
56    */

继续构造函数的第208行,这里有个关键变量。mQueue的赋值:

208          mQueue = mLooper.mQueue;

到这里,可以总结出:

      同一个线程里面,可以有多个Handler,最多只有一个Looper对象,多个Handler用的是同一个MessageQueue。

写段测试代码证实下:

package com.fy.platfromdebug;

import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.MessageQueue;
import android.util.Log;

public class TestHandler {
    private static final String TAG = "TestHandler";


    public void test() {
        Handler mainHandler = new Handler(Looper.getMainLooper());
        MessageQueue queue = mainHandler.getLooper().getQueue();
        Log.d(TAG, "mainHandler=" + mainHandler + " looper=" + mainHandler.getLooper() + " queue=" + queue);

        Handler mainHandler2 = new Handler(Looper.getMainLooper());
        MessageQueue queue2 = mainHandler2.getLooper().getQueue();
        Log.d(TAG, "mainHandler2=" + mainHandler2 + " looper=" + mainHandler2.getLooper() + " queue=" + queue2);
    }

    public void test2() {
        HandlerThread handlerThread = new HandlerThread("Test");
        handlerThread.start();
        Handler handler = new Handler(handlerThread.getLooper());
        Log.d(TAG, "handler=" + handler + " looper=" + handler.getLooper() + " queue=" + handler.getLooper().getQueue());
        Handler handler2 = new Handler(handlerThread.getLooper());
        Log.d(TAG, "handler2=" + handler2 + " looper=" + handler2.getLooper() + " queue=" + handler2.getLooper().getQueue());
    }

}

从log输出,可以证实我们的分析。

2019-01-07 10:26:30.492 22032-22032/com.fy.platfromdebug D/TestHandler: mainHandler=Handler (android.os.Handler) {459885c} looper=Looper (main, tid 2) {4d34765} queue=android.os.MessageQueue@ad45c3a
2019-01-07 10:26:30.493 22032-22032/com.fy.platfromdebug D/TestHandler: mainHandler2=Handler (android.os.Handler) {f6120eb} looper=Looper (main, tid 2) {4d34765} queue=android.os.MessageQueue@ad45c3a
2019-01-07 10:26:30.496 22032-22032/com.fy.platfromdebug D/TestHandler: handler=Handler (android.os.Handler) {524ce48} looper=Looper (Test, tid 1812) {b35f4e1} queue=android.os.MessageQueue@d9c0206
2019-01-07 10:26:30.496 22032-22032/com.fy.platfromdebug D/TestHandler: handler2=Handler (android.os.Handler) {aaee0c7} looper=Looper (Test, tid 1812) {b35f4e1} queue=android.os.MessageQueue@d9c0206

这里有个疑问,平时我们实例化Handler时,也没有传过任何参数,那Handler是绑定的哪个线程呢?我们也没有调用过Looper.prepare(),代码也没丢过异常。

    Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            ...
        }
    };

绑定的线程很好理解,就是实例化代码运行在哪个线程,就绑定的哪个线程。

Looper.prepare()虽然我们没调用过,不代表别人没调用过。上面的代码一般是运行在主线程,那我们肯定可以找到调用parpare的地方。在Looper里面搜索调用prepare的地方,发现Looper里面还有一个方法:

108      /**
109       * Initialize the current thread as a looper, marking it as an
110       * application's main looper. The main looper for your application
111       * is created by the Android environment, so you should never need
112       * to call this function yourself.  See also: {@link #prepare()}
113       */
114      public static void prepareMainLooper() {
115          prepare(false);
116          synchronized (Looper.class) {
117              if (sMainLooper != null) {
118                  throw new IllegalStateException("The main Looper has already been prepared.");
119              }
120              sMainLooper = myLooper();
121          }
122      }

这里调用了prepare方法,而这个prepareMainLooper在ActivityThread的main方法里面就调用了。所以前面的Handler绑定的是主线程。

6804      public static void main(String[] args) {
              ...
6822  
6823          Looper.prepareMainLooper();
6824  
              ...
6853      }

到现在为止,我们已经搞清楚Handler跟线程之前的关系了。

下次我们再来揭秘Handler背后的另一个boss, MessageQueue。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值