这是我参与更文挑战的第4天,活动详情查看: 更文挑战html
本文基于 Android 9.0.0的源代码,来分析Handler的用法java
framework/base/core/java/andorid/os/
- Handler.java
- Looper.java
- Message.java
- MessageQueue.java
复制代码
post()
和 send()
等方法来指定某个任务在某个时间执行常见的子线程中更新UI,复现代码,更具体见 Android子线程和更新UI问题android
textView = (TextView) findViewById(R.id.txt);
new Thread(new Runnable() {
public void run() {
SystemClock.sleep(3000);//这句不加不会报错,具体分析见上面连接
textView.setText("from来自子线程");
}
}).start();
复制代码
运行异常信息git
ErrorInfo: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6903)
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1050)
at android.view.View.requestLayout(View.java:19785)
at android.view.View.requestLayout(View.java:19785)
at android.view.View.requestLayout(View.java:19785)
at android.view.View.requestLayout(View.java:19785)
at android.view.View.requestLayout(View.java:19785)
at android.view.View.requestLayout(View.java:19785)
at android.view.View.requestLayout(View.java:19785)
at android.widget.TextView.checkForRelayout(TextView.java:7368)
at android.widget.TextView.setText(TextView.java:4480)
at android.widget.TextView.setText(TextView.java:4337)
at android.widget.TextView.setText(TextView.java:4312)
复制代码
能够看到错误发生在android.view.ViewRootImpl#checkThreadgithub
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
复制代码
可见此处会判断mThread
是否是等于当前线程 看下mThread
究竟是啥,在何处赋值的面试
public ViewRootImpl(Context context, Display display) {
...
mThread = Thread.currentThread();
...
}
复制代码
在构造方法中被赋值的,也就是说是建立ViewRootImpl
时所在的线程 ViewRootImpl
又是在哪里被建立的呢?这里不深刻讲了,是在main线程 更具体的异常分析能够参考这个编程
android.os.Handler handler = new Handler(){//在主线程中获取handler
@Override
public void handleMessage(final Message msg) {
//这里接受并处理消息
}
};
new Thread(() -> {
try {
Thread.sleep(2000);//子线程中执行耗时操做
//发送消息
Message message = Message.obtain();
message.what=1;
message.obj=new Object();
handler.sendMessage(message);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Handler().post(new Runnable() {
@Override
public void run() {
//doSomething
}
});
复制代码
实例化一个 Handler
重写handleMessage
方法 ,而后在须要的时候调用它的 send
以及 post
系列方法就能够了,很是简单易用,而且支持延时消息。(更多方法可查询 API 文档)数组
可是咱们并无看到Handler
是如何与MessageQueue
以及Looper
关联起来的,下面咱们进入源码分析下缓存
从构造函数开始,咱们一般从主线程中建立,先看下Handler的构造函数有哪些安全
Handler()
Handler(Callback callback)
Handler(Looper looper)
Handler(Looper looper, Callback callback)
Handler(boolean async)
Handler(Callback callback, boolean async)
Handler(Looper looper, Callback callback, boolean async)
看最后两个构造方法就行,由于前面的几个也是依次调用到后的方法
先看Handler(Callback callback, boolean async)
public Handler(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(Looper looper, Callback callback, boolean async)
与上面的区别就是Looper
是赋值进去的。
由上面能够看到调用Looper#myLooper
方法获取到Looper对象, 若是mLooper == null的话,会抛出异常
Can't create handler inside thread that has not called Looper.prepare()
这个错误咱们应该也见过。实际上咱们在实例化 Handler
的时候 会去检查当前线程的 Looper
是否存在,若是不存在则会报异常,也就是说在建立 Handler 以前必定须要先建立 Looper
。 咱们平时通常不会遇到这个错,由于咱们大多数都是在主线程建立Handler
的,而为何在主线程就不要本身建立Looper
,咱们待会再看,目前只须要知道若是Looper.myLooper()
没有获取到Looper
对象的话就会报这个错。
咱们跟踪Looper#myLooper
方法进去,解决为何会抛出这个异常。
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
复制代码
只有一行代码,从线程中取出Looper
对象,那么咱们有理由相信,这个ThreadLocal
是经过set方法把Looper
对象设置进去的。关于ThreadLocal
,参考ThreadLocal 源码分析。
想想ThreadLocal在哪里把Looper对象设置进去了呢。回到刚才想要解决的问题:Can’t create handler inside thread that has not called Looper.prepare() 。那会不会是Looper的prepare方法呢?
public static void prepare() {
prepare(true);
}
//调用私有构造方法
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));
}
复制代码
ThreadLocal
确实是在Looper#prepare
方法里把Looper
对象设置进去的,并且从第一行的判断能够知道,一个线程只有一个Looper
对象。
因此,要建立Handler
,那么Looper.myLooper()
就必须非空,上面分析得出要非空,要先调用Looper.prepare()
。
到了这里,Looper
与ThreadLocal
创建起了关联。
接着上面继续看下Looper
的构造方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
复制代码
每当咱们实例化一个 Looper
的时候会调用它的构造方法,并在其中实例化一个 MessageQueue
,相比于 Looper
和 Handler
,MessageQueue
就显得相对复杂一些。由于内部用到了 JNI 编程。初始化、销毁和入队等事件都用到了 native
的方法。能够在 android_os_MessageQueue 查看其源码的定义。更多参考MessageQueue 的实例化
咱们接着看Handle
构造函数里的
mQueue = mLooper.mQueue
咱们知道消息是存放在MessageQueue
消息队列中的,而MessageQueue
就是在上面Looper
构造函数中new出来的,至此Handler
经过Looper
与MessageQueue
也创建起了关联。
总结一下,建立Handler
,他的构造函数中会先调用Looper.myLooper()
获取Looper,也便是从ThreadLocal
中获取,而ThreadLocal
中要想获取到,要先调用Looper.prepare()
来set值,那么问题又来了,咱们写程序时好像没有手动调用Looper.prepare()
吧,也不会抛出异常。其实这是一个特殊状况,咱们一般都是在主线程,也就是UI线程中建立handler的。而在主线程中,系统已经为咱们建立了一个Looper
对象,因此不会抛出异常了,而那些会抛出异常报错的状况,是在子线程中建立的Handler
,可是又没有调用Looper.prepare()
去建立Looper
对象。 继续看,主线程在何时建立了Looper
对象吧。
在ActivityThread
的main方法,这个方法是应用程序的入口。
public static void main(String[] args) {
...
Looper.prepareMainLooper();
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
复制代码
Looper.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();
}
}
复制代码
能够看到第一行仍是调用了prepar(false)
方法的(false表明不可退出)。因此主线程是已经建立了一个Looper
对象的。
Handler
的建立过程分析完毕,如今总算搞明白了。
最后再总结一下,Handler
的建立是依赖于Looper
的。而主线程是默认建立了一个Looper
对象的。每个Looper
会关联一个线程(ThreadLocal
中封装了Looper
)。每个Looper
中又会封装一个消息队列。 这样一来,Handler
,Looper
,MessageQueue
,Thread
四个角色就关联了起来。 Handler
在主线程中建立,是由于要和主线程的消息队列关联起来,那样Handler#handleMessage
方法才会在主线程中执行,那么这样在更新UI就是线程安全的了。
回想开头咱们基础用法里提到 Handler
通常是经过一下2个方法发送的
handler.sendMessage(message); handler.post(runnable);
咱们先从第一个开始分析 handler.sendMessage(message)
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
复制代码
sendMessage
会调用sendMessageDelayed
方法并将message
对象传进去,第二个参数是延时时间,使用sendMessage
方法时默认为0的,最后都会调用sendMessageAtTime
。 上面分析了,在建立Looper
对象的时候,会建立一个MessageQueue
,因此只要Looper
是正常建立的话,消息队列是不为空的。 那么到最后一行的enqueueMessage
方法,源码以下
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
复制代码
将handler
自己赋值给msg.target
msg.setAsynchronous(true
设置message是不是异步的,这是message的一个属性。同一个Thread只有一个Looper,一个MessageQueue,可是能够有不少个Handler,若是Handler
初始化的时候async参数是true,那么这个Handler
所post的全部的message都会带上异步的属性。能够经过MessageQueue``的postSyncBarrier(long when)
来向队列中插入一个同步分割栏,同步分割栏是一个特殊的message,这种message的target=null,就像一个卡子,当他被插入时,会卡住在这以后的全部的同步的message,只会摘取异步的message。固然也能够经过MessageQueue的removeSyncBarrier(int token)来移除这个同步分割栏,token就是postSyncBarrier方法的返回值。可是目前这两个方法都被hide了。因此你们通常用到的都只是普通的Message。(注:摘自从源码去理解Handler)
而后最终调用queue.enqueueMessage
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
//消息是否正在使用
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
//很明显enqueueMessage须要同步,由于存在多个线程往一个Loop线程的MessageQueue中插入消息的场景。
//这里实际上是将Message根据延时插入到特定的地方,先看下关键点1,mMessages其实表明消息队列的头部,若是mMessages为空,说明尚未消息,若是当前插入的消息不须要延时,或者说延时比mMessages头消息的延时要小,那么当前要插入的消息就须要放在头部
//至因而否须要唤醒队列,则须要根据当前的Loop线程的状态来判断,后面讲Loop线程的时候再回过头说;
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//再来看下关键点2,这个时候须要将消息插入到队列中间,其实就是找到第一个Delay事件小于当前Message的非空Message,并插入到它的前面,往队列中插入消息时,若是Loop线程在睡眠,是不该该唤醒的,异步消息的处理会更加特殊一些,先不讨论。
//最后看关键点3,若是须要唤醒Loop线程,经过nativeWake唤醒,以上,就是普通消息的插入。
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
复制代码
Messagequeue
中有一个对象mMessage
用于指向当前传进的msg
,即最新的消息。而刚才的sendMessageAtTime(Message msg, long uptimeMillis)
方法,第二个参数指定了时间,而后在这里按照这个uptimeMillis
来进行消息的排序,这样每个消息都是按照时间的排序关联了起来,排在前面的消息指向了排在后面的消息。
以上是进入消息队列的分析,Handler
调用sendMessage
方法的最终将message
对象传进Messagequeue
。
那么消息是怎么从消息队列出来的呢? 这时咱们要回看ActiviryThread
的main方法,去寻找点线索。源码在上面已贴出。 发现了倒数第二行的Looper.loop()
,简单理解就是消息执行循环操做。 android.os.Looper#loop
public static void loop() {
//确保MessageQueue准备好
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 (;;) {
//for 无限循环,阻塞于消息队列的 next() 方法;
//不断从队列中读取消息并移除,若是队列为空,阻塞等待
Message msg = queue.next(); // might block
if (msg == null) {//跳出循环,looper退出就是利用了这点
// No message indicates that the message queue is quitting.
return;
}
...
try {
msg.target.dispatchMessage(msg);
...
} finally {
...
}
...
//清理,回收到缓存池
msg.recycleUnchecked();
}
}
复制代码
loop方法是个死循环,可是为何不会卡死主线程呢,参考
Android中为何主线程不会由于Looper.loop()里的死循环卡死?
Handler后传篇一: 为何Looper中的Loop()方法不能致使主线程卡死?
loop内容有点复杂,借用一张图来看下
当咱们调用 Looper#loop()
方法以后整个 Looper
循环就开始不断地处理消息了。在上图中就是咱们用绿色标记的一个循环。当咱们在循环中调用 MessageQueue#next()`` 方法来获取下一个消息的时候,会调用 nativePollOnce()
方法,该方法可能会形成线程阻塞和非阻塞,当线程为非阻塞的时候就会从 Native 层回到 Java 层,从 MessageQueuue
中取得一个消息以后给 Looper
进行处理。若是获取的时候形成线程阻塞,那么有两种状况会唤醒阻塞的线程,一个是当一个新的消息被加入到队列中,而且将会早于以前队列的全部消息被触发,那么此时将会从新设置超时时间。若是达到了超时时间一样能够从睡眠状态中返回,也就回到了 Java 层继续处理。因此,Native 层的 Looper
的做用就是经过阻塞消息队列获取消息的过程阻塞 Looper
。
再看下关键的Message msg = queue.next()
深刻分析参见MessageQueue中Message消息的执行以及 MessageQueue 的消息管理
Message next() {
...
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//是否须要阻塞等待,第一次必定不阻塞
// 调用 Native 层的 nativePollOnce() 方法进行精准时间的阻塞。
// 在 Native 层,将进入 pullInner() 方法,使用 epoll_wait 阻塞等待以读取管道的通知。
// 若是没有从 Native 层获得消息,那么这个方法就不会返回。此时主线程会释放 CPU 资源进入休眠状态。
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {//互斥同步
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//是否存在barier
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
//存在同步分隔栏,找到后面异步属性的msg
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
//第一个消息是否须要阻塞等待,并计算出阻塞等待时间
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
//须要无限等待
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
//没有能够即刻执行的Message,查看是否存在须要处理的IdleHandler,若是不存在,则返回,阻塞等待,若是存在则执行IdleHandler
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
// 若是目前没有消息,已经处在空闲状态,则执行 idler.queueIdle
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
//处理完IdleHandler ,须要从新判断Message队列 nextPollTimeoutMillis赋值为0
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
复制代码
上面分析过msg.target
就是handler
,因此loop
循环的时候又把消息取出扔给handler#dispatchMessage
方法了,咱们来看下
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
复制代码
因为这种方法没有传callback
,因此最终调用handleMessage
,咱们来看下
/** * Subclasses must implement this to receive messages. */
public void handleMessage(Message msg) {
}
复制代码
看到这里,相信你们应该很熟悉了,这就是咱们重写的方法。
咱们再看看另外一个发送消息的方法 handler.post(runnable)
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
复制代码
接收一个实现了Runable
接口的对象,而后将其传进getPostMessage()
方法。跟进getPostMessage()
方法看看
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
复制代码
其实就是将Runable
包装成message的callback
嘛。 因此,若是咱们使用post
方法发送消息,在执行dispatchMessage
的时候,callback
字段是不为空的,那么就会执行handleCallback()
方法,而不是执行handleMessage
方法了。
private static void handleCallback(Message message) {
message.callback.run();
}
复制代码
经过上面的分析可知 MessageQueue
经过 next
方法经过死循环获取下一个要处理的 Message, 若当前时刻不存在要处理的消息, 下次循环会进行睡眠操做
public static interface IdleHandler {
/** * Called when the message queue has run out of messages and will now * wait for more. Return true to keep your idle handler active, false * to have it removed. This may be called if there are still messages * pending in the queue, but they are all scheduled to be dispatched * after the current time. */
boolean queueIdle();
}
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
复制代码
经过上述代码能够获得如下的信息
public final class MessageQueue {
// 空闲消息集合
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
// 空闲消息处理者的数组
private IdleHandler[] mPendingIdleHandlers;
Message next() {
......
for (;;) {
......
synchronized (this) {
// 省略获取 msg 的代码
......
// 1. 从空闲消息集合 mIdleHandlers 中获取 空闲处理者 数量
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
// 2 若无空闲处理者, 则进行下一次 for 循环
if (pendingIdleHandlerCount <= 0) {
mBlocked = true;
continue;
}
......
// 3. 将空闲消息处理者集合转为数组
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 4. 处理空闲消息
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];// 获取第 i 给位置的空闲处理者
mPendingIdleHandlers[i] = null; // 置空
boolean keep = false;
try {
// 4.1 处理空闲消息
keep = idler.queueIdle();
} catch (Throwable t) {
......
}
if (!keep) {
synchronized (this) {
// 4.2 走到这里表示它是一次性的处理者, 从 mIdleHandlers 移除
mIdleHandlers.remove(idler);
}
}
}
......
}
}
}
复制代码
好的, 能够看到 MessageQueue.next 在获取不到 msg 时, 会进行一些空闲消息的处理
咱们发现不论是使用post
方法仍是sendMessage
方法来发送消息,最终都会调用sendMessageDelayed
方法。handler
将消息追加到消息队列中的过程都是同样的,而后Looper
不断的从MessageQueue
中取出消息,并由handler
去分发消息,处理消息,这样就构成了完善的Android消息机制体系。
Handler
虽然简单易用,可是要用好它仍是须要注意一点。
因为 Handler
的特性,它在 Android 里的应用很是普遍,好比: AsyncTask、HandlerThread、Messenger、IdleHandler 和 IntentService 等等。
Handler
容许咱们发送延时消息,若是在延时期间用户关闭了 Activity
,那么该 Activity
会泄露。
这个泄露是由于Message
会持有Handler
,而又由于 Java 的特性,内部类会持有外部类,使得 Activity
会被 Handler
持有,这样最终就致使 Activity 泄露。
解决该问题的最有效的方法是:将 Handler 定义成静态的内部类,在内部持有 Activity 的弱引用,并及时移除全部消息。
示例代码以下:
private static class SafeHandler extends Handler {
private WeakReference<HandlerActivity> ref;
public SafeHandler(HandlerActivity activity) {
this.ref = new WeakReference(activity);
}
@Override
public void handleMessage(final Message msg) {
HandlerActivity activity = ref.get();
if (activity != null) {
activity.handleMessage(msg);
}
}
}
复制代码
而且再在 Activity.onDestroy()
前移除消息,加一层保障:
@Override
protected void onDestroy() {
safeHandler.removeCallbacksAndMessages(null);
super.onDestroy();
}
复制代码
这样双重保障,就能彻底避免内存泄露了。
注意:单纯的在 onDestroy 移除消息并不保险,由于 onDestroy 并不必定执行。
在 Handler
的构造方法中有几个 要求传入 Callback
,那它是什么,又能作什么呢?
来看看 Handler.dispatchMessage(msg)
方法:
public void dispatchMessage(Message msg) {
//这里的 callback 是 Runnable
if (msg.callback != null) {
handleCallback(msg);
} else {
//若是 callback 处理了该 msg 而且返回 true, 就不会再回调 handleMessage
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
复制代码
能够看到 Handler.Callback
有优先处理消息的权利 ,当一条消息被 Callback
处理并拦截(返回 true),那么 Handler
的 handleMessage(msg)
方法就不会被调用了;若是 Callback
处理了消息,可是并无拦截,那么就意味着一个消息能够同时被 Callback 以及 Handler 处理。
这个就颇有意思了,这有什么做用呢?
咱们能够利用 Callback 这个拦截机制来拦截 Handler 的消息!
场景:Hook ActivityThread.mH
, 在 ActivityThread
中有个成员变量 mH
,它是个 Handler
,又是个极其重要的类,几乎全部的插件化框架都使用了这个方法。
因为 Handler
极为经常使用,因此为了节省开销,Android 给 Message
设计了回收机制,因此咱们在使用的时候尽可能复用 Message
,减小内存消耗。
方法有二:
Message
的静态方法 Message.obtain();
获取;Handler
的公有方法 handler.obtainMessage();
。咱们能够利用 Looper
的机制来帮助咱们作一些事情:
将 Runnable
post 到主线程执行
Activity.runOnUiThread(Runnable)
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
复制代码
View.post(Runnable)
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
//直接经过handler发送Post消息
return attachInfo.mHandler.post(action);
}
//先加入队列,等attachInfo被赋值时,会经过handler发送消息.
getRunQueue().post(action);
return true;
}
复制代码
利用 Looper
判断当前线程是不是主线程
public final class MainThread {
private MainThread() {
}
private static final Handler HANDLER = new Handler(Looper.getMainLooper());
public static void run(@NonNull Runnable runnable) {
if (isMainThread()) {
runnable.run();
}else{
HANDLER.post(runnable);
}
}
public static boolean isMainThread() {
return Looper.myLooper() == Looper.getMainLooper();
}
}
复制代码
Looper
和 Handler
不须要再一个线程中,默认的状况下会从ThreadLocal
中取当前线程对应的 Looper
,但咱们能够经过显式地指定一个 Looper
的方式来建立 Handler
. 好比,当咱们想要在子线程中发送消息到主线程中,那么咱们能够
Handler handler = new Handler(Looper.getMainLooper());
复制代码
Handler的post()方法
View的post()方法
Activity的runOnUiThread()方法
参见Handler后传篇二: 该如何理解Handler的"异步"?
调用 MessageQueue.next()
方法的时候会调用 Native 层的 nativePollOnce()
方法进行精准时间的阻塞。在 Native 层,将进入 pullInner()
方法,使用 epoll_wait
阻塞等待以读取管道的通知。若是没有从 Native 层获得消息,那么这个方法就不会返回。此时主线程会释放 CPU 资源进入休眠状态。
当咱们加入消息的时候,会调用 MessageQueue.enqueueMessage()
方法,添加完 Message 后,若是消息队列被阻塞,则会调用 Native 层的 nativeWake()
方法去唤醒。它经过向管道中写入一个消息,结束上述阻塞,触发上面提到的 nativePollOnce()
方法返回,好让加入的 Message 获得分发处理。
MessageQueue.enqueueMessage()
使用 synchronized 代码块去进行同步。
资料:Android 中的 Handler 的 Native 层研究
quit() 和 quitSafely() 有什么区别 子线程中建立了 Looper,在使用完毕后,终止消息循环的方法? quit() 和 quitSafely() 的本质是什么?
quit()
和 quitSafely()
的本质就是让消息队列的 next()
返回 null
,以此来退出Looper.loop()
。 quit()
调用后直接终止 Looper
,不在处理任何 Message
,全部尝试把 Message
放进消息队列的操做都会失败,好比 Handler.sendMessage()
会返回 false,可是存在不安全性,由于有可能有 Message
还在消息队列中没来的及处理就终止Looper
了。 quitSafely()
调用后会在全部消息都处理后再终止 Looper
,全部尝试把 Message
放进消息队列的操做也都会失败。
由前文可得出一些知识点,汇总一下,方便记忆。
Handler
的背后有 Looper
、MessageQueue
支撑,Looper
负责消息分发,MessageQueue
负责消息管理Handler
以前必定须要先建立Looper
Looper
有退出的功能,可是主线程的 Looper
不容许退出 Looper
须要本身调用 Looper.myLooper().quit();
退出Runnable
被封装进了 Message
,能够说是一个特殊的 Message
Handler.handleMessage()
所在的线程是 Looper.loop()
方法被调用的线程,也能够说成Looper
所在的线程,并非建立 Handler
的线程Handler
可能会致使内存泄露,即使在 Activity.onDestroy 里移除延时消息,必需要写成静态内部类