子曰:温故而知新,可以为师矣。 《论语》-- 孔子
一、定义
- 一套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
发送消息。
- 创建
Message
对象
// 方式一:
Message message1 = new Message();
// 方式二:
Message message2 = Message.obtain();
// 方式三:
Message message3 = mHandler.obtainMessage();
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);
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.target
的 dispatchMessage()
方法去分发消息,那么这个 msg.target
是什么呢?msg
是 Message
对象,我们进入 Message
类的源码,看到以下代码:
// Message 类
@UnsupportedAppUsage
/*package*/ Handler target;
由此看出 msg.target
就是 Handler
对象,所以其实调用的是 Handler
的 dispatchMessage()
方法。那么我们就看看 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
是否为空,mCallback
是 Handler
类中的一个成员变量,它是一个接口:
// 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.callback
是Message
类中的 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.callback
的 run()
方法,也就是我们传入的 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
就是之前赋值过的 handler
,this
就是指当前调用的 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
。
第一种方法的原理是静态内部类不默认持有外部类的引用
,同时弱引用的对象拥有短暂的生命周期
。在垃圾回收器线程扫描时,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
第二种方法的原理是消息队列的清空使得引用关系不存在,同时使得 MainActivity
和 Looper
这两个对象的生命周期同步。
第一种方式的做法:
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
的异步消息机制,我们分为 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
发送消息的方式有很多种:
但是不管有多少种发送消息的方法,通过查看源码,最终都会调用 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 你想要知道的知识基本上介绍到位了,各位看官食用愉快。