1 Handler基本介绍
1.1 什么是Handler?
Handler是Android的消息通信机制,用于线程间通信。Handler通信方案是内存共享。Handler可以从子线程向主线程发消息,也可以从主线程向子线程发消息。
在Android开发中,经常会在子线程中进行一些耗时操作,等到操作完成后,就会用Handler将数据发送给主线程,通知主线程去做相应的操作。比如请求到网络数据之后更新UI等。子线程、Handler、主线程构成了一个消费者-生产者模式。生产者和消费者在同一时间段内共用同一个存储空间,生产者(子线程)向存储空间中添加数据(消息),消费者(主线程)向存储空间中取出数据(消息)。
1.2 Handler的相关类
Message
Message
是消息实体。
MessageQueue
MessageQueue
是一个由单链表实现的优先级消息队列。
Handler
Handler
是消息处理类,可以发送消息、获取消息、处理消息、移除消息。
子类实现这个方法来处理从队列中取出的消息。
Looper
用来轮询消息队列。
1.3 Handler消息机制架构图
Handler
将消息插入MessageQueue
,然后Looper
从MessageQueue
轮询中取出消息交给Handler去处理。
2 Handler的使用
2.1 创建Handler
2.1.1 在主线程中创建Handler
2.1.1.1 匿名内部类的形式
匿名内部类创建Handler
是一种常用的方式,简单快捷。但是这样会有内存泄漏的问题,Message
持有Handler
,而Java中内部类会持有外部类,所以Handler
会持有Activity
,这样就会可能发生内存泄漏。
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if (msg.what == 0) {
Log.d(TAG, "handleMessage: 收到了消息");
}
}
};
2.1.1.2 静态内部类继承Handler
可以将Handler
定义成静态内部类,内部使用WeakReference
持有Activity
的弱引用,然后onDestroy()
的时候即使移除所有回调和消息。
public class HandlerActivity02 extends AppCompatActivity {
private static final String TAG = "HandlerActivity02";
private MyHandler mHandler;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler02);
mHandler = new MyHandler(this);
findViewById(R.id.button).setOnClickListener(v ->
new Thread(() -> mHandler.sendEmptyMessage(0)).start()
);
}
@Override
protected void onDestroy() {
//移除所有回调以及消息
mHandler.removeCallbacksAndMessages(null);
super.onDestroy();
}
private static class MyHandler extends Handler {
private WeakReference<Activity> weakReference;
public MyHandler(Activity activity) {
this.weakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
Activity activity = weakReference.get();
if (activity != null && msg.what == 0) {
Log.d(TAG, "handleMessage: 收到了消息");
}
}
}
}
2.1.2 在子线程中创建Handler
2.1.2.1 方式1
子线程中添加Looper.prepare()
和Looper.loop()
。
new Thread(() -> {
//方式1
//实例化Handler之前加上 Looper.prepare()
Looper.prepare();
mHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if (msg.what == 0) {
Log.d(TAG, "handleMessage: 收到了消息");
}
}
};
mHandler.sendEmptyMessage(0);
//最后加上Looper.loop()
Looper.loop();
}).start());
2.1.2.2 方式2
使用Handler
的有参构造方法Handler(Looper looper)
,传入Looper.getMainLooper()
。
new Thread(() -> {
//方式2
//使用有参构造方法Handler(Looper looper),传入Looper.getMainLooper()
mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if (msg.what == 0) {
Log.d(TAG, "handleMessage: 收到了消息");
}
}
};
mHandler.sendEmptyMessage(0);·
}).start());
2.2 生成Message
生成消息的方式有三种,分别是new Message()
、Message.obtain()
、mHandler.obtainMessage()
。
//生成消息方式1
Message message1 = new Message();
message1.what = 0;
message1.arg1 = 1;
message1.obj = null;
mHandler.sendMessage(message1);
//生成消息方式2
Message message2 = Message.obtain();
message2.what = 0;
message2.arg1 = 2;
message2.obj = null;
mHandler.sendMessage(message2);
//生成消息方式3
Message message3 = mHandler.obtainMessage();
message3.what = 0;
message3.arg1 = 3;
message3.obj = null;
mHandler.sendMessage(message3);
2.3 发送消息
sendMessage(Message msg)
发送消息,将一条消息添加到消息队列。
sendMessageDelayed(Message msg, long delayMillis)
在以系统开机时间的相对时间(当前时间+delayMillis)之前的所有挂起的消息之后,将一条消息放入消息队列。
sendMessageAtTime(Message msg, long uptimeMillis)
在以系统开机时间的绝对时间(以毫秒为单位)uptimeMillis之前,在所有挂起的消息之后将消息放入消息队列。
sendMessageAtFrontOfQueue(Message msg)
将消息放入消息队列的前端,在消息循环的下一次迭代中进行处理。
sendEmptyMessage(int what)
发送空消息,将一条空消息添加到消息队列。
sendEmptyMessageDelayed(int what, long delayMillis)
在以系统开机时间的相对时间(当前时间+delayMillis)之前的所有挂起的消息之后,将一条空消息放入消息队列。
sendEmptyMessageAtTime(int what, long uptimeMillis)
在以系统开机时间的绝对时间(以毫秒为单位)uptimeMillis之前,在所有挂起的消息之后将空消息放入消息队列。
mHandler.sendMessage(message);
2.4 处理消息
mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
//处理消息
}
};
3 Handler源码分析
3.1 核心流程图
Handler流程图如下所示,展示了整个消息机制的核心流程。
3.2 Looper
在使用Handler
发送以及处理消息之前,必须先创建Looper
,才能从消息队列中轮询消息。
在主线程中,创建Handler
的时候并没有手动去调用Looper.prepare()
和Looper.loop()
,这是因为APP启动过程中在ActivityThread.main()
中,已经调用了Looper.prepareMainLooper()
,Looper.prepareMainLooper()
会调用Looper.prepare()
。
为什么在ActivityThread.main()
中调用了进行了Looper
的创建,在主线程也就是Activity
中就不用再创建Looper
了呢?这就涉及到了APP的启动流程,APP的启动流程如下图所示:
从流程图中可以看到在APP进程创建后,会走到ActivityThread.main()
中,ActivityThread.main()
调用了Looper.prepareMainLooper()
和Looper.loop()
,所以在主线程中就有了Looper
。
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
...
//实例化Looper和MessageQueue
Looper.prepareMainLooper();
...
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
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()
方法调用Looper.prepare()
。会传入一个boolean
型的参数quitAllowed
,表示能否退出队列。这里传入的是false,所以在主线程中队列不能退出和销毁。
@Deprecated
public static void prepareMainLooper() {
//调用Handler.prepare(),传入false,表示消息队列不可退出
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
//如果主线程中Looper已创建,会抛出异常。
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
在Looper.prepare()
中new
出了一个Looper
,并且将其存入ThreadLocal
中。这里会判断ThreadLocal
中是否已经有Looper
存在,如果已经存在,会抛出异常,如果不存在,会new
出一个Looper
。所以,一个线程中只能有一个Looper
存在。
private static void prepare(boolean quitAllowed) {
//这里会判断ThreadLocal中是否已经有Looper存在,如果已经存在,会抛出异常。
//一个线程中只能有一个Looper存在。
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//new出一个Looper。
sThreadLocal.set(new Looper(quitAllowed));
}
prepare还有一个无参的重载方法,调用该方法,传入的quitAllowed
是true,表示可以退出队列。
public static void prepare() {
//消息队列可以退出
prepare(true);
}
而在Looper
的构造方法中,创建了一个MessageQueue
,这也同时说明,MessageQueue
也是唯一的。
private Looper(boolean quitAllowed) {
//实例化MessageQueue
mQueue = new MessageQueue(quitAllowed);
//Looper与当前线程绑定
mThread = Thread.currentThread();
}
在ActivityThread.main()
中,最后调用了Looper.loop()
。
public static void loop() {
final Looper me = myLooper();//调用sThreadLocal.get(),从中取出Looper
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
if (me.mInLoop) {
Slog.w(TAG, "Loop again would have the queued messages be executed"
+ " before this one completed.");
}
me.mInLoop = true;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
// Allow overriding a threshold with a system prop. e.g.
// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
final int thresholdOverride =
SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);
me.mSlowDeliveryDetected = false;
for (;;) {
//死循环,调用loopOnce方法
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}
Looper.loop()
中有一个死循环,调用Looper.loopOnce()
,从MessageQueue
中取消息。
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
//调用MessageQueue的next()方法取消息
Message msg = me.mQueue.next(); // might block
if (msg == null) {
//消息为空,会退出消息队列
// No message indicates that the message queue is quitting.
return false;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what);
}
...
try {
msg.target.dispatchMessage(msg);//msg.target是绑定的Handler
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
}
...
msg.recycleUnchecked();
return true;
}
3.3 Handler
创建Handler
的时候调用Handler
的构造函数。
public Handler(@NonNull Looper looper) {
this(looper, null, false);
}
接着调用3个参数的构造方法。
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
//传入了一个Looper
mLooper = looper;
//从looper中获取一个MessageQueue
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
Handler提供了很多发送消息的方法,常用的是sendMessage(Message msg)
。
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
sendMessage(Message msg)
调用了sendMessageDelayed(Message msg, long delayMillis)
,不管是sendMessage(Message msg)
还是sendEmptyMessage(int what)
,最后调用了sendMessageAtTime(Message msg, long uptimeMillis)
。
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull 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);
}
接着调用了Handler.enqueueMessage()
,Handler.enqueueMessage()
调用了MessageQueue.enqueueMessage()
。
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//调用MessageQueue.enqueueMessage()
return queue.enqueueMessage(msg, uptimeMillis);
}
3.4 MessageQueue
MessageQueue
是Handler
中的核心类,这个类是所有消息的存储仓库,消息管理有两点,入库enqueueMessage()
和出库next()
,这两个方法是确保线程安全的关键节点。这里主要涉及线程同步和消息屏障两大核心问题。
3.4.1 线程同步
MessageQueue.enqueueMessage()
synchronized
是一个内置锁,也就是有系统去控制lock和unlock的时机。synchronized (this)
表示对所有调用MessageQueue
对象的线程来说,都是互斥的。由上面源码分析可知,一个线程对应一个Looper
,一个Looper
只会创建一个MessageQueue
,所以所有子线程向主线程发消息时,主线程一次都只会处理一条消息,其他的都处于等待状态,这样就保证了消息队列的有序性。
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
//加锁
synchronized (this) {
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
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) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// 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.next()
每次取消息都是从队头去取,但是还是要使用synchronized (this)
去加锁,以保证MessageQueue.enqueueMessage()
和MessageQueue.next()
是互斥的,这样消息队列的操作才能有序进行。
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
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;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
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.
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.
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.
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;
}
}
3.4.2 消息机制的同步屏障
由上面分析可知,线程的消息都是放到同一个MessageQueue
里面,取消息的时候是互斥的,而且只能从队列头部取消息,而添加消息是按照消息的执行的先后顺序进行的排序。假如同一个时间范围内的某个消息需要立刻执行的,那就要给紧急需要执行的消息开通一个绿色通道,这个绿色通道就是同步屏障的相念。
屏障的意思即为阻碍,顾名思义,同步屏障就是阻碍同步消息,只让异步消息通过。开启同步屏障调用的是MessageQueue.postSyncBarrier()
。
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
//获取消息
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
//如果开启同步屏障的时间不为0,且当前的同步消息里有时间小于同步屏障时间的
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
//prev不为null,将消息按照时间顺序插入消息队列的合适位置
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
开启同步屏障之后,异步消息在MessageQueue.next()
中如何处理?
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
//nextPollTimeoutMillis = -1 一直阻塞不会超时
//nextPollTimeoutMillis = 0 不会阻塞,立刻返回
//nextPollTimeoutMillis > 0 最长阻塞nextPollTimeoutMillis
int nextPollTimeoutMillis = 0;
//死循环
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
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;//当前链接头结点
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
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.
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.
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.
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;
}
}
3.5 Message
Message
可以直接new
,但是更推荐使用Message.obtain()
和mHandler.obtainMessage()
,因。mHandler.obtainMessage()
调用了Message.obtain(this)
,最终都是调用的Message.obtain()
。Message.obtain()
中检查了是否有可以复用的Message
,避免过多的创建和销毁Message
,达到了内存优化的目的。
public final Message obtainMessage()
{
return Message.obtain(this);//调用Message.obtain(this)
}
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;//这里将Message和Handler绑定
return m;
}
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
//检查是否有可复用的Message
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
//没有可复用的Message,再创建新的
return new Message();
}
3.6 Message和Handler绑定
直接调用mHandler.obtainMessage()
或者Message.obtain(handler)
的时候,传入了一个Handler
,使用m.target = h
进行Message
和Handler
的绑定。
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;//将Message和传入的Handler进行绑定
return m;
}
如果是直接new Message()
,则会在将消息插入队列的时候调用Handler.enqueueMessage()
的时候进行绑定。
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;//将Message和当前Handler进行绑定
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
4 Handler相关问题
4.1 一个线程有几个Handler?
可以有无数个,Handler
可以随便new
。
4.2 一个线程有几个Looper?
只有1个,Looper
的创建最终调用的是Looper.prepare()
,进行了判断,如果已经创建了Looper
,就不会再创建。
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));
}
4.3 Handler内存泄漏原因?
由于Java特性,内部类持有外部类的对象,而Handler
是通过内部类的形式创建,所以会持有Activity
,而Message又持有Handler,如果一条消息需要等待较长时间才能发送,而这时Activity却被销毁,就有可能发生内存泄漏。
而在RecyclerView
中,Adapter持有ViewHolder
,ViewHolder
也是内部类,但是他们的生命周期一致,所以不会发生内存泄漏。
要解决改问题,可以将Handler
定义成静态内部类,内部使用WeakReference
持有Activity
的弱引用,然后onDestroy()
的时候即使移除所有回调和消息。
4.4 为何主线程可以new Handler()?如果想要在子线程new Handler()应该怎么做?
在上面源码分析可知,APP启动过程中,在ActivityThread.main()
中已经调用了Looper.prepareMainLooper()
和Looper.loop()
,Activity
中已经创建了Looper
,所以主线程中可以直接new Handler()
。
想要在子线程new Handler()
,有两种方式。
方式1:在子线程中添加Looper.prepare()
,Looper.loop()
。
new Thread(() -> {
//方式1
//实例化Handler之前加上 Looper.prepare()
Looper.prepare();
mHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if (msg.what == 0) {
Log.d(TAG, "handleMessage: 收到了消息");
}
}
};
mHandler.sendEmptyMessage(0);
//最后加上Looper.loop()
Looper.loop();
}).start());
方式2:new Handler()
的时候传入Looper.getMainLooper()
。
new Thread(() -> {
//方式2
//使用有参构造方法Handler(Looper looper),传入Looper.getMainLooper()
mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if (msg.what == 0) {
Log.d(TAG, "handleMessage: 收到了消息");
}
}
};
mHandler.sendEmptyMessage(0);
}).start());
4.5 子线程中维护的Looper,消息队列无消息的时候处理方案是什么?有什么用?
无消息的时候调用Looper.quit()
或Looper.quitSafely()
。
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
Looper.quit()
和Looper.quitSafely()
调用MessageQueue.quit()
。
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
//mQuitting赋值为true
mQuitting = true;
//移除所有消息
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);//调用native方法唤醒之前被阻塞的nativePollOnce(),继续执行MessageQueue.next()
}
}
然后在MessageQueue.next()
中判断mQuitting
为true,退出循环。
Message next() {
...
for (;;) {
...
nativePollOnce(ptr, nextPollTimeoutMillis);
...
synchronized (this) {
...
//mQuitting为true,退出循环
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
...
}
...
}
}
4.6 既然可以存在多个Handler往MessageQueue中添加数据(发消息时各个Handler可能处于不同线程),那它内部是如何让保证线程安全的?
使用内置锁synchronized
,synchronized (this)
表示对所有调用MessageQueue
对象的线程来说,都是互斥的。一个线程对应一个Looper
,一个Looper
只会创建一个MessageQueue
,所以所有子线程向主线程发消息时,主线程一次都只会处理一条消息,其他的都处于等待状态,这样就保证了消息队列的有序性。
4.7 如何创建Message?
3中方式,new Message()
、Message.obtain()
、mHandler.obtainMessage()
。推荐使用后两种,mHandler.obtainMessage()
调用了Message.obtain(this)
,最终都是调用的Message.obtain()
。Message.obtain()
中检查了消息池中是否有可以复用的Message
,避免过多的创建和销毁Message
,达到了内存优化的目的。
4.8 Looper死循环为什么不会导致ANR?
ANR产生的原因有以下几种:
- Service Timeout:比如前台服务在20s内未执行完成;
- BroadcastQueue Timeout:比如前台广播在10s内未执行完成
- ContentProvider Timeout:内容提供者,在publish过超时10s;
- InputDispatching Timeout: 输入事件分发超时5s,包括按键和触摸事件。
所有的Activity和Service都是运行在Looper.loop()
函数中,以消息的方式存在,所以在没有消息产生的时候,Looper
会被block(阻塞),主线程会进入休眠,一旦有输入事件或者Looper
添加消息的操作后主线程就会被唤醒,从而对事件进行响应,所以不会导致ANR。
4.9 MessageQueue会填满吗?
跟内存有关,内存多大,就能存多少消息。
作者:木水Code
链接:https://juejin.cn/post/7109048202099163167