Handler 的基本使用、常见问题的源码解析以及运行机制源码讲解

子曰:温故而知新,可以为师矣。 《论语》-- 孔子


一、定义

  • 一套Android消息传递机制/异步通信机制。

二、作用

  • 多线程场景下,子线程需要将更新UI操作信息传递到主线程,实现异步消息的处理。

三、使用

使用的方式有 2 种:

  • 一种是通过 sendMessage() 的方式来实现异步通信。
  • 一种是通过 mHandler.post() 的方式来实现异步通信。

3.1 sendMessage() 方式

1. 创建 Handler对象,下面列举 3 种方式。
  • 匿名内部类
// 在主线程中通过匿名内部类创建Handler类对象。
private Handler mHandler = new Handler(){
// 通过复写handlerMessage方法更新UI
    @Override
    public void handleMessage(@NonNull Message msg) {
        super.handleMessage(msg);
        // ...... 执行更新UI操作
        }
};
  • 实现 Callback 接口
// 主线程中通过实现Callback 接口创建 handler 类对象。
private Handler mHandler  = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            return false;
        }
});
  • 在子线程中创建 Handler 类对象 (比较少见,基本不用)
 new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                Handler mHandler = new Handler();
                Looper.loop();
            }
        }).start();

2. 创建 Message 对象,同时 handler 发送消息。
  1. 创建 Message 对象
// 方式一:
Message message1 = new Message();
// 方式二:
Message message2 = Message.obtain();
// 方式三:
Message message3 = mHandler.obtainMessage();
  1. Message 对象携带数据
  Message message = new Message();
  message.what = 1; // 消息的标识
  
  // 方式一:通过arg1,arg2 传递
  message.obj = "啦啦啦"; // object
  message.arg1 = 1; // int 类型
  message.arg2 = 2; // int 类型
  
  // 方式二:通过Bundle 传递
  Bundle bundle = new Bundle();
  bundle.putInt("1",1);
  message.setData(bundle);
  1. handler 发送消息
// 方式一:发送普通消息
mHandler.sendMessage(message);

// 方式二:发送空消息,传入what标识值,便于在接收消息中判断。 
mHandler.sendEmptyMessage(0);

// 方式三:延时发送消息
mHandler.sendEmptyMessageDelayed(0,1000);

3. handler 接收消息并处理。
private Handler mHandler = new Handler() {
    @Override
    public void handleMessage(@NonNull Message msg) {
        super.handleMessage(msg);
        // 通过发送消息时的what标识,对不同消息做判断处理
        switch (msg.what) {
            case 0:
                System.out.println("接收到的消息为空消息");
                break;
            case 1:
                System.out.println(msg.obj.toString());
                break;
            default:
                break;
            }
       }
};

3.2 mHandler.post() 方式

  • 1.创建 Handler 对象。
  • 2.在子线程通过调用实例对象的 post() 方法,传入 runnable 对象,重写 run() 方法,在重写的 run() 方法中更新 UI
private Handler mHandler = new Handler();
new Thread(new Runnable() {
            @Override
            public void run() {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                       // 更新UI操作
                    }
                });
            }
        });


好了,关于 Handler 的基本使用就说完了,下面通过通过一个个的问题以及相应问题的源码解释来加深我们对 Handler 的理解,做到知其然知其所以然。



四、常见问题及源码解答

1. 主线程创建 Handler 与子线程创建 Handler 有什么不同 ?

主线程创建 Handler:

private Handler mHandler  = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            return false;
        }
});

子线程创建 Handler

 new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                Handler mHandler = new Handler();
                Looper.loop();
            }
        }).start();

通过对比 ,我们可以看到在子线程创建 Handler 多了两行代码,那么为什么在主线程创建时不需要呢?我们通过源码来解释一下:

应用在启动的时候,会调用 ActivityThread 这个类,在这个类的 main() 方法中,调用了 Looper.prepareMainLooper()

// ActivityThread 类中的 main 方法
 public static void main(String[] args) {
        // ....
        // 创建主线程的Looper
        Looper.prepareMainLooper();
        //...
        Looper.loop();
    }

Looper.prepareMainLooper();

  • 这行代码的含义就是给当前线程初始化一个 Looper,所以并不是主线程不需要调用 Looper.prepare() 方法,而是系统已经做了操作。

Looper.loop();

  • 这行代码的含义就是开启主线程的轮询,这是一个死循环,会一直轮询。

读到这儿,可能有人就想问,主线程创建时,系统自动帮我们加了这两行代码,如果我们在子线程创建 Handler 时不加入这两行代码,行不行?答案是否定的,不行,会报异常:

 throw new RuntimeException("Only one Looper may be created per thread");

这个时候我们就会思考,为什么系统会抛出异常?还是需要我们从源码中找答案。这个时候我们就需要看看 new Handler()的时候,在源码中具体做了什么?

 public Handler(@Nullable Callback callback, boolean async) {
     
       //.....
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
       //....
    }

从源码中可以看到会给成员变量 mLooper 赋值,一旦没有获取到值,就会抛出我们之前提到的异常,所以我们要看看 Looper.myLooper() 这个操作做了什么?

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
 
 //......
 public static @Nullable Looper myLooper() {
      return sThreadLocal.get();
  }

从源码看出,这个方法的返回值就是 Looper 对象,接着我们看一下 get() 方法,到底这个 Looper 对象是如何获取的?

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

这么一看,我们就知道了,获取当前线程的对象作为 key 值,然后从 ThreadLocalMap 中获取 value 值,从结果来看是没获取到,为什么呢?,我们重新回到 ActivityThread 类中 main() 方法中,点击 Looper.prepareMainLooper()prepareMainLooper() 方法:

public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

再点击此方法中的 prepare() 方法:

 private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

下面是 sThreadLocal.set()方法:

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

我们将这两个方法对着看,可以看出在应用启动时,ThreadLocalMap 里面会创建唯一一条数据,key 的值就是 主线程,value 值就是主线程的 Looper 对象,而当我们在子线程创建 Handler 的时候,是需要获取 Looper 对象的,那么此时传入的 key 的值是子线程,当然从 ThreadLocalMap 中获取不到 Looper 对象了,因为 key 的值不一样。所以取出的 Looper 对象为 null,也就抛出了上面的异常。


通过这个问题 可以做以下总结:

  • 子线程默认是没有 Looper的,如果需要使用 Handler 就必须为线程创建Looper
  • 一个线程中必须要有一个 Looper ,也就是说在一个线程中,如果想使用 Handler,必须要有一个 Looper。想要正确在子线程中创建 handler 对象,做法如上面创建 handler 对象第 3 种方法。
  • Handler创建的时候会采用当前线程的Looper来构造消息循环系统,因为应用启动时,ActivityThread类会初始化Looper,所以在主线程中默认可以使用 handler,但不能说主线程没有创建 Looper对象。

2. 更新控件内容真的只能在主线程运行 ?

首先给出结论,这句话太过绝对,不一定。不相信的童鞋可以在子线程中更新 UI,结果可能会让你惊讶,怀疑自我,哈哈,我们以 TextView.setText()为例,从源码的角度去分析一波:

一路点击 setText() 方法到底,找到最后一个 setText 方法里面的 checkForRelayout() 方法 。点击此方法,直接看到最后面的代码,可以看到不管 if 里面的代码还是 else里面的代码,最终都会走 requestLayout() (请求布局) 和 invalidate()(刷新布局)这两行代码。接着我们就看看 requestLayout() 里面做了什么事?

public void requestLayout() {
        if (mMeasureCache != null) mMeasureCache.clear();

        if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
            // Only trigger request-during-layout logic if this is the view requesting it,
            // not the views in its parent hierarchy
          
            //  ------ ViewRootImpl 实现了 mParent 接口 --------
            ViewRootImpl viewRoot = getViewRootImpl();
            if (viewRoot != null && viewRoot.isInLayout()) {
                if (!viewRoot.requestLayoutDuringLayout(this)) {
                    return;
                }
            }
            mAttachInfo.mViewRequestingLayout = this;
        }

        mPrivateFlags |= PFLAG_FORCE_LAYOUT;
        mPrivateFlags |= PFLAG_INVALIDATED;

        if (mParent != null && !mParent.isLayoutRequested()) {
            //  ---------mParent 是一个接口---------
            mParent.requestLayout();
        }
        if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
            mAttachInfo.mViewRequestingLayout = null;
        }
    }

ViewRootImpl 恰恰实现了 mParent 接口,也就是会调用 ViewRootImpl 里面的 requestLayout() 方法,那么我们看一下源码:

@Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }

里面有一个检查线程的方法 checkThread(),点击进入:

void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }

此段代码会检查当前线程是否是主线程,不是则抛出异常。

由此可以看出来,我们在子线程中更新 UI,如果 requestLayout() 这个检查线程的执行速度 慢于 invalidate() 绘制界面的执行速度,不会抛出异常。

我们可以做一个操作来验证一下,在子线程中延迟一秒,更新 UI,结果报错信息如下:

 Process: com.example.handlerdemo, PID: 10569
    android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
        at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:7753)
        at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1225)
        at android.view.View.requestLayout(View.java:23093)
        at android.view.View.requestLayout(View.java:23093)
        at android.view.View.requestLayout(View.java:23093)
        at android.view.View.requestLayout(View.java:23093)
        at android.view.View.requestLayout(View.java:23093)
        at android.view.View.requestLayout(View.java:23093)
        at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:360)
        at android.view.View.requestLayout(View.java:23093)
        at android.widget.TextView.checkForRelayout(TextView.java:8908)
        at android.widget.TextView.setText(TextView.java:5730)
        at android.widget.TextView.setText(TextView.java:5571)
        at android.widget.TextView.setText(TextView.java:5528)
        at com.example.handlerdemo.MainActivity$3.run(MainActivity.java:75)
        at java.lang.Thread.run(Thread.java:764)

可以看出来指向的报错地方就是我们阅读源码看到的 ViewRootImpl.requestLayout这个检查线程的地方。

所以才说更新 UI的操作只能在主线程,这句话太过绝对了,不一定就是的,要看情况分析。


3. 主线程创建 Handler 两种写法有何区别?

方式一:

 private Handler mHandler1 = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            return false;
        }
    });

方式二:

    private Handler mHandler2 = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);

            mTextView.setText(msg.obj.toString());
        }
    };

用过的人都知道 方式二 有黄色警告,这是谷歌备胎的 api,不推荐使用。

有了前面的分析,我们都知道在 AcitivityThread 类的 main() 方法中会除了会创建 Looper 对象, 还会调用 Looper.loop() 方法不断的循环消息队列,点击此方法:

// Looper 类

 for (;;) {
 //....
 try {
                msg.target.dispatchMessage(msg);
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } 
 }
 //...

在这个循环中,会调用 msg.targetdispatchMessage() 方法去分发消息,那么这个 msg.target 是什么呢?msgMessage 对象,我们进入 Message 类的源码,看到以下代码:

// Message 类

@UnsupportedAppUsage
/*package*/ Handler target;

由此看出 msg.target 就是 Handler 对象,所以其实调用的是 HandlerdispatchMessage() 方法。那么我们就看看 Handler 类中的 dispatchMessage() 方法:

// Handler 类

public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

if里面的判断是给 mHandler.post() 方法准备的,我们先不看,直接看到 else 里面的方法, 首先判断 mCallback 是否为空,mCallbackHandler 类中的一个成员变量,它是一个接口:

// Handler 类

final Callback mCallback;
public interface Callback {
        /**
         * @param msg A {@link android.os.Message Message} object
         * @return True if no further handling is desired
         */
        boolean handleMessage(@NonNull Message msg);
    }

我们在方式一中的写法就是传入了一个新建的Handler的callback 对象,所以判断不为空,就会走 mCallback.handleMessage(); 方法,也就是我们实现 callBack 接口中的 handleMessage() 方法,在我们重写的方法中,return true 或者 return false 其实并没有区别,为什么这么说 ?

如果我们返回 true,那么源码中直接 return, 如果我们返回false ,在源码中就会走下面的 handleMessage(), 这个是 Handler 类中暴露出来的可以重写的公开方法,但是它的方法名和接口中的方法名一样,我们重写不了,在方式一的 Handler 类中是无法重写 handleMessage() 方法,所以才说写 return true 或者 return false 并没有区别。

如果我们用的是方式二的写法,那么就会走下面的 handleMessage() 方法。也就是走我们在方式二中重写的 Handler 类中暴露出来的可以重写的公开方法 handleMessage()

好了,这两种方式的区别就说完了,现在来说一下之前要忽略的 dispatchMessage()方法里面的 if 里面的代码 为什么说是给 mHandler.post()准备的呢?

我们看一下自己写的 mHandler.post():

new Thread(new Runnable() {
            @Override
            public void run() {
                mHandler1.post(new Runnable() {
                    @Override
                    public void run() {
                          //.....
                    }
                });
            }
        });

我们再看一下 Handler 类中的 post() 源码:

// Handler 类

public final boolean post(@NonNull Runnable r) {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

可以看到我们传入的 Runnable 的对象, 会调用 getPostMessage(r) 方法将传入的Runnable 的对象进行一个封装。那么这个方法中做了什么事情呢?

// Handler 类

private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

可以看到源码中将传入的 Runnable 对象赋值给了 Message.callback, 而这个 Message.callbackMessage 类中的 callback 属性,它就是是一个 Runnable 对象:

 // Message 类中的
 
 @UnsupportedAppUsage
 /*package*/ Runnable callback;

上面这个方法返回的 Message 对象,它的 callback 属性的值就是我们执行 post() 方法传入的 Runnable 对象。

那么我们再回过头来看一下 Handler 类中 dispatchMessage() 方法中的 if里面的代码:

// Handler 类

   public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
}

mHandler.post() 方式发送的消息,在接收消息时,会执行 if里面的代码,因为 msg.callback 就是传入的 Runnable 对象,不为空,所以会执行 handleCallback() 方法:

// Handler 类

private static void handleCallback(Message message) {
        message.callback.run();
    }

可以看到这个 handleCallback() 方法里面执行的是 message.callbackrun() 方法,也就是我们传入的 Runnable 接口重写的 run 方法。


4. 创建 Message 两种方法有何区别?

方式一:

Message message = Message.obtain();

方式二:

Message message = mHandler.obtainMessage();

我们先来看一下 方式一Message 类中的 obtain() 方法的源码:

    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

从源码中看出,Android会为 Message 提供一个缓存池,把使用过的消息缓存起来,方便下次使用。我们用 Message.obtain() 方法获取一个消息时,会先从缓存池获取,如果缓存池没有消息,才会去创建消息。这样做可以优化内存。同时会将缓存池 sPool 指向该 Message 的下一个。


现在看一下 方式二 中的 obtainMessage() 的源码:

 public final Message obtainMessage()
    {
        return Message.obtain(this);
    }

从源码中看出,实际上还是调用了 Messageobtain() 方法,将当前的 handler对象作为参数传递过去了,再看一下 obtain() 方法内的源码:

 public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;

        return m;
    }

从源码中看到,第一行调用的 obtain() 方法,就是和第一种方式源码里面的调用 Message.obtain() 调用的方法,只不过多了第二行代码,将 handler 赋值给了message.target。那么有了这一操作,就可以不用写 handler.sendMessage() 方法了,直接写 message.sendToTarget() 方法,为什么这么说,我们看一下 sendToTarget() 方法的源码:

public void sendToTarget() {
        target.sendMessage(this);
    }

从源码中看出,target 就是之前赋值过的 handlerthis 就是指当前调用的 message ,就相当于 handler.sendMessage(message)


5. Handler 使用不当为什么会造成内存泄漏?

还记得我们使用第二种方式创建 Handler 的时候,会有黄色警告,内容大意就是此 Handler 没有设置为静态的,最终的内存泄漏会发生在持有该 Handler 类的外部类上,例如 MainActivity

那么我们就要思考了,为什么第二种方式创建的 Handler 没有设置为静态的,就有可能发生内存泄漏呢??

首先我们要知道的有两点:

  • Handler 创建的时候会获取主线程的 Looper 对象,而这个 Looper 对象的生命周期是和应用的生命周期一样的。
  • Java 基础知识点:非静态内部类 或者 匿名内部类 都是会默认持有外部类的引用

基于以上两点理论知识,我们设想一个场景,如果 Handler 里面的从主线程获取的消息队列(MessageQueue)中还有未处理的消息(Message)等待处理,那么这个时候,消息队列中的 message 默认持有 Handler 的引用,而 Handler 又默认持有外部类例如 MainActivity 的引用,假设 Handler 是在 MainActivity 中创建的,那么如果我们销毁 MainActivity 的话,垃圾回收期 GC 回因为上面的引用关系而无法回收 MainActivity,从而导致内存泄漏。


既然我们知道了内存泄漏发生的原因,那么怎么解决呢?当然是从原因下手:

  • 静态内部类 + 弱引用。
  • 外部类即将销毁时,清空 Handler 的消息队列,同时将 Handler 置为 null

第一种方法的原理是静态内部类不默认持有外部类的引用,同时弱引用的对象拥有短暂的生命周期。在垃圾回收器线程扫描时,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。

第二种方法的原理是消息队列的清空使得引用关系不存在,同时使得 MainActivityLooper 这两个对象的生命周期同步。


第一种方式的做法:

public class HandlerActivity extends AppCompatActivity {

    private final MyHandler mHandler = new MyHandler(this);

    private static final Runnable mRunnable = new Runnable() {
        @Override
        public void run() {
            // 操作
        }
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fourth);

        mHandler.postDelayed(mRunnable, 1000*10);
        
        finish();   
    }


    private static class MyHandler extends Handler {
        WeakReference<HandlerActivity> mWeakActivity;

        public MyHandler(HandlerActivity activity) {
            this.mWeakActivity = new WeakReference<HandlerActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            final HandlerActivity mActivity = mWeakActivity.get();
            if (mActivity != null) {
                // 处理消息
            }
        }
    }

}

第二种方式的做法:

 @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.e("TAG  >>>>","onDestroy");

        // 正确处理内存泄漏
        mHandler.removeCallbacksAndMessages(null);
        mHandler = null;
    }


五、Handler 机制源码讲解

有了前面这些知识点的讲解,下面我们通过阅读源码的方式,来整体把握一下 Handler 的运行机制,看一下 Handler 到底是如何发送消息以及处理消息的。

先来一张价值千万 的图:
Hanlder 机制原理图

对于 Hanlder 的异步消息机制,我们分为 4 步:

1 步:

应用程序启动,ActivityThread 类中的 main() 方法中,会调用Looper.prepareMainLooper() 方法,而在 prepareMainLooper() 里面又会调用 prepare() 方法,在这个方法里面,会创建全局唯一的主线程 Looper 对象,为什么这么说,看一下 prepare()方法的源码:

 private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

通过 sThreadLocal.set() 方法,将新建的 Looper 塞到 ThreadLocalMap 里面,key的值为当前线程,也就是主线程,value 的值为主线程的 Looper,我们看一下 Looper 的构造器,是私有的:

// 私有的
private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

所以只能通过 Looper 类暴露在外面的静态方法 prepare() 去创建 Looper 对象,所以说创建了全局唯一的主线程 Looper 对象,同时在 Looper 的构造器中,同时创建了 MessageQueue 对象,也是全局唯一的主线程的消息队列。


2 步:
当我们在主线程创建 Handler 对象时,也就是 new Handler 时,查看源码:

public Handler(@Nullable Callback callback, boolean async) {
      //..
       mLooper = Looper.myLooper();
       if (mLooper == null) {
           throw new RuntimeException(
               "Can't create handler inside thread " + Thread.currentThread()
                       + " that has not called Looper.prepare()");
       }
       mQueue = mLooper.mQueue;
       mCallback = callback;
       mAsynchronous = async;
   }

可以看到 Handler 里面的成员变量 mLooper 以及 mQueue 都赋了初始值,主线程创建的 Looper 对象赋值给了 Handler 类里的成员变量 mLooper,主线程里的 Looper对象里面的消息队列 MessageQueue 赋值给 Handler 类里的成员变量 mQueue


3 步:

Handler 发送消息时,我们知道 Handler 发送消息的方式有很多种:
Handler发送消息
但是不管有多少种发送消息的方法,通过查看源码,最终都会调用 enqueueMessage() 方法:

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

上面的源码中有一点值得关注,就是 msg.target = this; 这行代码,this 也就是调用此方法的 Handler 对象,将 Handler 对象赋值给了传入的 Meessage 对象的 target 属性。

我们看到此方法又调用了 queue.enqueueMessage 方法,我们找到 MessageQueue 类中的 enqueueMessage 方法:

boolean enqueueMessage(Message msg, long when) {
       //...

        synchronized (this) {
            //...
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                //...
            }
            //....
        }
        return true;
    }

我们看到其中有这么一行代码 mMessages = msg; , 这个 mMessages 就是 MessageQueue 消息队列类中的成员变量 Message,那么我们就明白了,Handler发送消息的时候,其实是将要发送的消息(msg)传入到主线程的消息队列(MessageQueue 类)中,同时将msg 的值赋值给主线程消息队列(MessageQueue 类)中的 mMessages 属性。


4 步:

Handler 是如何接收并处理消息的呢?其实在我们应用启动后,ActivityThread 中就会调用 Looper.loop() 一直循环的读取消息,查看一下 loop() 方法的源码:

 public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;
         //....
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            //...
            try {
                msg.target.dispatchMessage(msg);
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } catch (Exception exception) {
                if (observer != null) {
                    observer.dispatchingThrewException(token, msg, exception);
                }
                throw exception;
            } finally {
                ThreadLocalWorkSource.restore(origWorkSource);
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
             //.....
        }
    }

我们看到有这么一行代码 msg.target.dispatchMessage(msg);,这就是 Looper 这个消息泵一直从消息队列( MessageQueue)中取消息,一旦从 MessageQueue 中取到消息,就会调用 dispatchMessage() 方法将消息分发出去。那么这个 msg.target 是什么,还记得之前无论 Handler 调用哪种发送消息方法,最终都会调用 enqueueMessage() 方法吗?在那个方法中,将调用者 Handler 赋值给了 Message.target 属性。所以这里的分发消息的方法,就是调用了 Handler 自身的 dispatchMessage() 方法。那么对于 Handler 自身的 dispatchMessage() 方法,我们之前已经分析过了:

 public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

如果你是调用 mHandler.post() 方法发送的消息,那么处理消息时会走上面的 if 里面的代码,执行 mHandler.post(Runnable r) 传入的 Runnable 接口实现类的重新的抽象的 run()方法。

如果你是通过 实现 callback 接口创建的 Handler对象 ,那么就走 else 里面的 if 里面的代码,调用实现callback 接口的 handleMessage() 方法 。

如果你是通过匿名对象类并重写 Handler 类中公开的 handleMessage() 方法创建的 Handler对象,那么就走最后的 handleMessage() 方法。



写在文末

纸上得来终觉浅,绝知此事要躬行。 《冬夜读书示子聿》-- 陆游

好了,关于 Handler 你想要知道的知识基本上介绍到位了,各位看官食用愉快。


码字不易,如果本篇文章对您哪怕有一点点帮助,请不要吝啬您的点赞,我将持续带来更多优质文章。

发布了3 篇原创文章 · 获赞 4 · 访问量 1612

猜你喜欢

转载自blog.csdn.net/wild_onlyking/article/details/104170662
今日推荐