这是 面试系列 的第五期。本期咱们未来探讨一下 Android 异步消息处理线程 —— Handler。前端
往期内容传递:
Android 面试(一):说说 Android 的四种启动模式
Android 面试(二):如何理解 Activity 的生命周期
Android 面试(三):用广播 BroadcastReceiver 更新 UI 界面真的好吗?
Android 面试(四):Android Service 你真的能应答自如了吗?java
Android 的消息机制,也就是 Handler
机制,相信各位都已是烂熟于心了吧。即建立一个 Message
对象,而后借助 Handler
发送出去,以后在 Handler
的 handleMessage()
方法中获取刚才发送的 Message
对象,而后在这里进行 UI 操做就不会出现崩溃了。面试
嗯,对,在 Android 开发中,咱们确实常常用到它,对于基本代码流程天然也是滚瓜烂熟,但了解它的原理的人却不是不少,因此面试官一般会考验你对 Handler
源码机制的理解,毕竟只有知己知彼,才能百战不殆嘛。微信
Handler 主要由如下部分组成。app
HandlerHandler
是一个消息辅助类,主要负责向消息池发送各类消息事件Handler.sendMessage()
和处理相应的消息事件Handler.handleMessage()
。异步
MessageMessage
即消息,它能容纳任意数据,至关于一个信息载体。ide
MessageQueueMessageQueue
如其名,消息队列。它按时序将消息插入队列,最小的时间戳将被优先处理。oop
LooperLooper
负责从消息队列读取消息,而后分发给对应的 Handler
进行处理。它是一个死循环,不断地调用 MessageQueue.next()
去读取消息,在没有消息分发的时候会变成阻塞状态,在有消息可用时继续轮询。post
首先天然是在工做线程中建立本身的消息队列必需要调用 Looper.prepare()
,而且在一个线程中只能调用一次。固然,仅仅建立了 Looper
还不行,还必须使用 Looper.loop()
开启消息循环,要否则要 Looper
也没用。ui
咱们平时在开发中不用调用是由于默认会调用主线程的 Looper。
此外,一个线程中只能有一个 Looper 对象和一个 MessageQueue 对象。
大概的标准写法是这样。
Looper.prepare();
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.i(TAG, "在子线程中定义Handler,并接收到消息。。。");
}
};
Looper.loop();复制代码
另一个比较常考察的就是 Handler 可能引发的内存泄漏了。
咱们常常会写这样的代码。
private final Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};复制代码
当你这样写的时候,你必定会收到编译器的黄色警告。
In Android, Handler classes should be static or leaks might occur, Messages enqueued on the application thread’s MessageQueue also retain their target Handler. If the Handler is an inner class, its outer class will be retained as well. To avoid leaking the outer class, declare the Handler as a static nested class with a WeakReference to its outer class
在 Java 中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用,而静态的内部类不会持有外部类的引用。
要解决这样的问题,咱们在继承 Handler
的时候,要么是放在单独的类文件中,要么直接使用静态内部类。当须要在静态内部类中调用外部的 Activity 的时候,咱们能够直接采用弱引用进行处理,因此咱们大概修改后的代码以下。
private static final class MyHandler extends Handler{
private final WeakReference<MainActivity> mWeakReference;
public MyHandler(MainActivity activity){
mWeakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
MainActivity activity = mWeakReference.get();
if (activity != null){
// 开始写业务代码
}
}
}
private MyHandler mMyHandler = new MyHandler(this);复制代码
其实在咱们实际开发中,不止一个地方可能会用到内部类,咱们都须要在这样的状况下尽可能使用静态内部类加弱引用的方式解决咱们可能出现的内存泄漏问题。
HandlerThread
是 Android API 提供的一个便捷的类,使用它可让咱们快速地建立一个带有 Looper
的线程,有了 Looper
这个线程,咱们又能够生成 Handler
,本质上也就是一个创建了内部 Looper
的普通 Thread
。
咱们在上面提到的子线程创建 Handler 大概代码是这样。
new Thread(new Runnable() {
@Override public void run() {
// 准备一个 Looper,Looper 建立时对应的 MessageQueue 也会被建立
Looper.prepare();
// 建立 Handler 并 post 一个 Message 到 MessageQueue
new Handler().post(new Runnable() {
@Override
public void run() {
MLog.i("Handler in " + Thread.currentThread().getName());
}
});
// Looper 开始不断的从 MessageQueue 取出消息并再次交给 Handler 执行
// 此时 Lopper 进入到一个无限循环中,后面的代码都不会被执行
Looper.loop();
}
}).start();复制代码
而采用 HandlerThread
能够直接把步骤简化为这样:
// 1. 建立 HandlerThread 并准备 Looper
handlerThread = new HandlerThread("myHandlerThread");
handlerThread.start();
// 2. 建立 Handler 并绑定 handlerThread 的 Looper
new Handler(handlerThread.getLooper()).post(new Runnable() {
@Override
public void run() {
// 注意:Handler 绑定了子线程的 Looper,这个方法也会运行在子线程,不能够更新 UI
MLog.i("Handler in " + Thread.currentThread().getName());
}
});
// 3. 退出
@Override public void onDestroy() {
super.onDestroy();
handlerThread.quit();
}复制代码
其中必须注意的是,workerThread.start() 是必需要执行的。
至于如何使用 HandlerThread
来执行任务,主要是调用 Handler
的 API。
postAtFrontOfQueue()
将任务加入到队列前端, postAtTime()
指定时间提交任务, postDelayed()
延后提交任务。sendMessage()
方法能够发送消息,sendMessageAtFrontOfQueue()
将该消息放入消息队列前端,sendMessageAtTime()
指定时间发送消息, sendMessageDelayed()
延后提交消息。两个方法做用都是结束 Looper
的运行。它们的区别是,quit()
方法会直接移除 MessageQueue
中的全部消息,而后终止 MesseageQueue
,而 quitSafety()
会将 MessageQueue
中已有的消息处理完成后(再也不接收新消息)再终止 MessageQueue
。