Handler的使用
我们知道,Android中,不允许应用程序在子线程中更新UI,UI的处理必须在UI线程中进行,这样Android定制了一套完善的线程间通信机制——Handler通信机制。Handler作为Android线程通信方式,高频率的出现在我们的日常开发工作中,我们常用的场景包括:使用异步线程进行网络通信、后台任务处理等,Handler则负责异步线程与UI线程(主线程)之间的交互。
我们来看Handler的使用示例:
public static final int LOAD_COM = 1;//加载任务的id标志
private Handler mHandler = new MyHandler(MainActivity.this);
private static class MyHandler extends Handler{
private final WeakReference<MainActivity> mActivity;
private MyHandler(MainActivity activity) {
this.mActivity = new WeakReference(activity);
}
@Override
public void handleMessage(@NonNull Message msg) {//ui线程中,负责消息返回的处理逻辑
super.handleMessage(msg);
switch (msg.what){
case LOAD_COM:
Log.d("TestHandler", msg.obj.toString());
MainActivity mainActivity = mActivity.get();
if (mainActivity != null){
mainActivity.mTextView.setText(msg.obj.toString());
}
break;
}
}
};
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.start_load:
new Thread(){
@Override
public void run() {//后台线程中执行逻辑
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message message = Message.obtain();
message.what = LOAD_COM;
message.obj = "加载完成";
mHandler.sendMessage(message);//从后台线程中,发送消息给UI线程
}
}.start();
break;
}
}
demo逻辑解析:
- 在Activity中,创建了一个Handler对象。
- 当按钮start_load点击时,启动一个后台线程,模拟一个后台加载过程(线程休眠1秒)。
- 后台任务完成后,使用Handler对象的sendMessage方法发送消息(一个Messaage对象)给UI线程。
- UI线程中,Handler对象的handleMessage方法负责处理消息的返回。
Demo中的例子是我们在Android开发中经常使用到的方式,Handler非常简单的帮助我们实现了UI线程和后台线程之间的通信。那么Handler是如何做到线程间通信的呢?
接下来我们以源码来分析Handler的实现机制。
Handler的实现机制&源码分析
Handler机制介绍
Handler机制是Android通信机制的一个重要组成部分。在Android中,应用程序是消息驱动的,每个应用程序的UI线程(主线程)都会维护一个消息队列,并且会不断的从这个消息队列中取出消息进行处理。Handler机制实现了消息在线程之间的通信。
Handler消息处理机制包括了3个组成部分:消息循环、消息发送、消息处理,这其中涉及到几个重要类:Handler、Message、Looper和MessageQueue。
一个线程,想要处理Hander发送的消息,必须做好以下几点准备:
- 首先要有一个消息循环,也就是要创建一个Looper对象。
- 将Looper对象绑定到当前线程上,也就是必须要调用Looper.prepare()方法。
- 开启Looper的循环,也就是要调用Looper的loop方法。
我们接下来以Handler创建、消息的发送、消息循环、消息处理的顺序来展开分析。
Handler对象的创建
要想使用Handler进行通信,首先要创建一个Handler对象。
Handler的构造函数:
public Handler() {//版本1
this(null, false);
}
public Handler(@Nullable Callback callback) {//版本2
this(callback, false);
}
public Handler(@NonNull Looper looper) {//版本3
this(looper, null, false);
}
public Handler(@NonNull Looper looper, @Nullable Callback callback) {//版本4
this(looper, callback, false);
}
@UnsupportedAppUsage
public Handler(boolean async) {//版本5。不支持App使用
this(null, async);
}
public Handler(@Nullable Callback callback, boolean async) {//版本6。主要实现逻辑在这里
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
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;
}
@UnsupportedAppUsage
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {//版本7。不支持App直接使用,系统内部使用
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
Handler的构造函数有多个重载版本,但逻辑实现都在版本6和版本7中。在版本7中,只是简单的属性赋值,这里我们来分析版本6的实现。
逻辑解析:
- Handler有多个重载版本,但可供我们使用版本的async参数都是默认值false。
- 首先根据布尔值FIND_POTENTIAL_LEAKS判断是否需要进行泄漏的检测,默认是false。
- 将Looper.myLooper()赋值给mLooper对象。
- 将looper.mQueue赋值给mQueue对象。
- 将参数callback、async赋值给相应属性。
Looper.myLooper()返回一个Looper对象,我们来看它的实现。
Looper.myLooper()方法
myLooper方法(位置:android.os.Looper.java):
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
这里直接返回了当前线程的一个线程本地对象(线程本地对象的变量,在每个线程中都是独立存储的)。
我们来看sThreadLocal的初始化:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();//线程的本地对象,存储当前线程对应的Looper
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {//prepare方法只能调用一次
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));//创建Looper对象并绑定到当前线程。
}
逻辑解析:
- sThreadLocal是一个线程本地对象,存储了当前线程对应的Looper对象。
- Looper类对外提供了prepare()静态方法来初始化Looper,prepare无参方法调用了prepare的有参方法。
- prepare方法的参数quitAllowed:表示是否允许MessageQueue退出循环,默认参数是true,表示允许退出。
- 每个线程中只能调用一次prepare方法。
- prepare方法最终会创建一个Looper对象,并使用线程本地对象sThreadLocal,绑定到当前线程中。
prepare方法负责Looper对象的创建以及将Looper对象绑定到当前线程,所以要想使用Handler机制,Looper.prepare方法必须在使用前调用。
Looper的构造函数:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
构造函数做了2件事:
- 初始化MessageQueue对象,MessageQueue负责管理消息队列。MessageQueue使用一个单链表的数据结构来管理消息的添加和删除。
- 获取当前线程的引用。
小结:
- Handler有多个重载版本,但可供我们使用版本的async参数都是默认值false。
- Handler机制要想正常使用,必须在初始化时,调用Looper.prepare()方法。
- Handler构造函数完成后,我们也就准备好了Looper对象,以及MessageQueue对象。
消息的发送
在Demo中,我们创建完成了Handler对象之后,实现了handleMessage方法来接收后台线程发来的消息。消息的发送是由Handler对象的sendMessage方法完成的,我们先来分析它的实现。
sendMessage方法:
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
这里直接调用了sendMessageDelayed方法,第二个参数为0,表示不作延迟。sendEmptyMessage等发送消息的方法,最终也会调用sendMessageDelayed方法。
sendMessageDelayed方法:
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {//矫正延迟时间
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
这里调用了sendMessageAtTime方法,参数1:msg;参数2:SystemClock.uptimeMillis() + delayMillis,其中SystemClock.uptimeMillis()表示从开机到现在的毫秒数。
这里将延迟时间delayMillis,转换为了绝对时间,传递给了sendMessageAtTime方法。
sendMessageAtTime方法:
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);
}
逻辑解析:
- 这里有2个参数,msg表示要发送的消息,uptimeMillis表示要处理消息的时间(这里是绝对时间)。
- 获取消息队列,消息队列不允许未初始化。
- 调用enqueueMessage方法,让消息进入消息队列中。
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属性。
- 将调用者的uid赋给msg的workSourceUid属性。
- 判断是否是异步消息,如果是异步,则调用setAsynchronous方法。我们在Handler初始化中得知,我们通常的应用使用的都是默认同步的,最后调用queue.enqueueMessage。
MessageQueue的enqueueMessage方法:
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {//msg.target不能为null。
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {//msg不能是正在使用中
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) {//如果队列头消息为空,或者当前消息是插队的消息(time值为0时),或者当前消息的执行时间比队列头的要早。这里会优先执行当前消息。
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;//表示是否需要阻止唤醒loop循环
} 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();//如果队列头的消息是异步的,并且target为空,并且当前线程处理是阻塞的,则表示需要唤醒。
Message prev;
for (;;) {//这里将当前消息插入到队列中的合适位置
prev = p;
p = p.next;
if (p == null || when < p.when) {//如果消息为空或者未到执行时间,则退出循环,找到了合适的位置了
break;
}
if (needWake && p.isAsynchronous()) {//如果需要唤醒属性是true并且队列头消息是异步的,则不需要唤醒。
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;//插入当前消息
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {//如果需要唤醒,则调用nativeWake方法
nativeWake(mPtr);
}
}
return true;
}
逻辑解析:
- 消息的target为空,或者msg正在处理,则抛出error错误。
- 接下来进行同步块的处理。
- 当消息循环正在退出时,将当前消息回收,并退出。
- 对msg进行处理,设置msg的when,将msg状态置位正在使用。
- 将队列头消息取出。
- 如果队列头消息为空,或者当前消息是插队的消息(time值为0时),或者当前消息的执行时间比队列头的要早,将消息插入到队列头。
- 如果消息不需要立即执行,则需要插入到消息队列中。
- for循环中,将当前消息插入到队列中的合适位置。
- 如果需要唤醒线程,则调用nativeWake方法(nativeWake是一个native方法)。
小结:
- 我们使用Handler对象的sendMessage()等方法进行消息的发送。
- 最终消息会发送到MessageQueue的enqueueMessage方法中。
- 如果消息不需要立即执行,则把消息添加到MessageQueue的消息队列中的合适位置。消息队列的顺序是按执行时间(绝对时间)顺序排列的。
- 如果消息需要立即执行,则将消息添加到队列头,并赋值给mMessages(队列头指针)。
- 判断线程是否需要唤醒,如果需要,则调用nativeWake(mPtr)执行唤醒。
消息循环
在消息的发送部分,消息发送后,会将消息添加到消息队列中,如果满足立即执行条件,则唤醒线程执行。
那么到了这里,你也许会问,消息的处理在哪进行的呢?将消息添加到消息队列之后,接下来怎么处理呢?
不要着急,消息的处理逻辑不在Handler里面,而是在Looper的loop方法中进行处理的,Looper和MessageQueue负责我们的消息循环部分。
Looper的loop方法:
消息循环是在调用了Looper.loop方法之后开始的,我们来看:
public static void loop() {
final Looper me = myLooper();//获取当前线程的Looper对象
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;//获取MessageQueue对象
// 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();//清空远程调用端的uid和pid,用当前本地进程的uid和pid替代
……
for (;;) {
Message msg = queue.next(); // 从消息队列中获取消息
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// 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);
}
// Make sure the observer won't change while processing a transaction.
final Observer observer = sObserver;
final long traceTag = me.mTraceTag;
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
if (thresholdOverride > 0) {
slowDispatchThresholdMs = thresholdOverride;
slowDeliveryThresholdMs = thresholdOverride;
}
final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
final boolean needStartTime = logSlowDelivery || logSlowDispatch;
final boolean needEndTime = logSlowDispatch;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
Object token = null;
if (observer != null) {
token = observer.messageDispatchStarting();
}
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
try {
msg.target.dispatchMessage(msg);//执行消息处理,将消息发送给Handler对象进行处理。这里的target,是当前消息的Handler对象
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);
}
}
if (logSlowDelivery) {
if (slowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
slowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
msg)) {
// Once we write a slow delivery log, suppress until the queue drains.
slowDeliveryDetected = true;
}
}
}
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
逻辑解析:
- 获取当前线程的Looper对象。
- 清空远程调用端的uid和pid。
- 消息处理是在一个无限循环的for循环中处理的。
- 在for循环中,首先从消息队列中获取消息。
- 若消息为null,则表示消息循环队列已退出,则结束循环。
- 否则,执行性能相关的日志输出,我们通过logging可以实现message处理的性能检测,我们将在后续文章中分析它的实现。
- 调用msg.target.dispatchMessage(msg)执行消息处理,将消息发送给Handler对象进行处理。这里的target,是当前消息的Handler对象。
- 最后将消息对象进行回收处理。
loop方法调用Handler对象的dispatchMessage进行消息的处理,消息处理部分我们稍后分析,我们先来看消息获取的queue.next()方法。
MessageQueue的next方法
MessageQueue是一个消息队列,内部是使用单向链表来实现的,Message对象的next属性保存列表中的下一个,MessageQueue对象的mMessages属性是链表头,也就是当前要处理的消息。
MessageQueue的next方法:
@UnsupportedAppUsage
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; // 闲置任务的数量
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;
}
……
//进行闲置时间的任务处理。
// 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;
}
}
逻辑解析:
MessageQueue的next方法负责了从消息队列中取出消息,如果没有要执行的消息,则进行休眠,直到有消息需要执行为止。
-
for循环内首先调用nativePollOnce(ptr, nextPollTimeoutMillis),这是一个native方法,通过Native层的MessageQueue实现休眠。
nextPollTimeoutMillis表示休眠时间:
- nextPollTimeoutMillis == -1,一直休眠不会超时。
- nextPollTimeoutMillis == 0,不会休眠。
- nextPollTimeoutMillis > 0,最长休眠时间不超过nextPollTimeoutMillis毫秒,如果期间有程序唤醒会立即返回。
-
如果msg.target为null,则表示添加了一个消息屏障。这个屏障之后的所有同步消息都不会被执行,即使时间已经到了也不会执行。
-
如果存在消息屏障,则该循环会忽略同步消息,查找到异步第一个异步消息,并进行处理。
-
如果消息存在,并且当前消息没到执行时间,则设置休眠时间。否则消息需要处理,则返回将要处理的消息。
-
如果队列中已经没有消息需要处理了,可以进行无限休眠,等待唤醒,而唤醒操作是调用native方法nativeWake()来实现的。
-
如果当前队列中没有任务需要处理,则可以利用空闲资源处理一些非紧急任务(闲置任务),以执行一些非紧急的任务。
消息处理
我们来分析消息的处理,消息处理逻辑是在Looper中的loop方法调用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);
}
}
如果Message对象的callback属性存在,则执行callback的run方法,否则,调用handleMessage进行消息处理。
Handler的handleMessage方法
public void handleMessage(@NonNull Message msg) {
}
Handler的handleMessage方法是一个空方法,需要我们使用时,在子类中实现,就像demo中一样。
UI线程Handler消息机制的初始化
经过Handler的原理分析,我们了解到Handler机制在初始化阶段,需要调用Looper的prepare方法进行Looper初始化操作,并且需要调用Lopper.loop()开启消息循环。
但是我们在UI线程中使用时,并没有进行这些初始化操作,那么UI线程中,Handler机制是如何进行初始化的呢?
在Looper的方法中,我们发现有一个叫prepareMainLooper()的方法,貌似是执行UI线程Handler初始化的地方?
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()方法,并且参数为false,表示不允许消息队列循环退出;然后进行了线程校验,如果sMainLooper已经存在,则会抛出Error;最后将Looper对象赋值给sMainLooper。
那么该方法是在什么地方?什么时候调用的呢?
ActivityThread的main方法
我们来看源码:
public static void main(String[] args) {
……
Looper.prepareMainLooper();
……
Looper.loop();
……
}
ActivityThread的main方法是在一个进程被创建的时候调用的,也即是说,进程创建过程中,会执行Handler的相关初始化工作。
这里调用了Looper.prepareMainLooper()方法,初始化了Looper,并关联了UI线程。
调用了Looper.loop()开启了Looper的循环。
Handler的性能问题
在使用Handler的时候,我们通常会因为使用时的不小心,造成一些内存泄漏等方面的性能问题。
下面来进行原因分析及如何来避免。
Handler的内存泄漏
原因
Handler的内存泄漏问题,通常是因为对象存活的生命周期不一致问题导致的,具体原因如下:
- Handler持有了外部Activity的引用。
- 在Activity中启动了一个后台线程,执行耗时任务,后台线程持有Handler的引用,当任务执行时间很长时。
- Activity在耗时任务执行期间结束执行了,这时,因为后台线程中持有了Handler对象,Handler对象持有Activity的引用,所以就会造成Activity在GC时,回收不掉的问题。
- 造成了内存泄漏。
思路
内存泄漏的原因是因为我们在执行异步任务时,持有了Activity的引用,从而导致内存泄漏问题。
那么如果我们的Handler不持有外部类的引用不就可以了吗?那么我们如何做到不持有外部Activity的引用呢?
解决方法一
解决方法参考本章开头的Demo:
- Handler一定要实现一个自定义的静态内部类,这里为什么是静态内部类呢?因为非静态内部类会持有外部类的引用,而静态内部类是不持有外部类引用的。
- Handler的handleMessage方法中,如果需要用到Activity的属性或对象,这时我们不得不持有Activity的引用,怎么办呢?那就是像上面我们的Demo一样,使用一个弱引用来保存Activity,原理就是,弱引用在GC时,当内存紧张时会进行回收。
- Activity在Handler中使用时,一定要判断是否为null(是否已经回收)。
解决方法二
如果我们在Activity进行退出时,把相应的异步消息都进行注销,这样Handler都成为可回收状态后,我们的Activity自然也就是可回收的了,也就不会内存泄漏了。
- 我们可以在Activity的onDestory中调用Handler对象的removeCallbacksAndMessages()方法,参数一定要传null。
- 注意,需要每一个Handler对象都要执行该方法。
Handler的removeCallbacksAndMessages()方法
public final void removeCallbacksAndMessages(@Nullable Object token) {
mQueue.removeCallbacksAndMessages(this, token);
}
这里调用了MessageQueue的removeCallbacksAndMessages方法。
MessageQueue的removeCallbacksAndMessages方法
void removeCallbacksAndMessages(Handler h, Object object) {
if (h == null) {
return;
}
synchronized (this) {
Message p = mMessages;
// Remove all messages at front.
while (p != null && p.target == h
&& (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycleUnchecked();
p = n;
}
// Remove all messages after front.
while (p != null) {
Message n = p.next;
if (n != null) {
if (n.target == h && (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
该方法会清除掉MessageQueue队列中的所有消息,并且对Message进行回收。
Message的recycleUnchecked方法
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;//移除了handler的引用
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
该方法对Message对象进行资源回收。
总结
- Android中的线程通信机制使用了Handler来实现的。
- Android机制的初始化,需要调用Looper.prepare()方法进行Looper的创建及关联到当前线程,然后调用Looper.loop()方法实现消息的循环。
- 消息的发送是通过Handler的sendMessage()或post()等方法执行的,然后调用了MessageQueue的enqueueMessage()方法来把消息添加到消息队列中,如果需要唤醒操作,则调用native方法nativeWake进行唤醒。
- MessageQueue是消息队列,内部采用单链表的数据结构来存储消息列表,链表的头是mMessages对象。
- MessageQueue实现了消息队列的添加、消息的获取、消息循环的休眠、消息循环的唤醒等。
- 消息循环的启动是通过Looper.loop()来实现的,它启动了一个无限循环来进行消息的处理。
- Looper.loop()每次循环都会调用MessageQueue的next()方法来实现消息的获取,当然如果当前没有消息需要执行,则会在next方法中调用native方法nativePollOnce()进行休眠。
- MessageQueue的next()方法中实现了消息屏障机制,可以通过添加消息屏障来阻塞消息队列中同步消息的执行。
- MessageQueue的next()方法中也实现了空闲任务执行机制,当线程空闲时,我们可以执行一些非紧急任务。
- 我们可以调用Looper.quit()或Looper.quitSafely()方法来退出消息循环,但是UI线程不可退出消息循环。
- UI线程是在ActivityThread的main方法中执行的Handler机制的初始化工作,调用了Looper.prepareMainLooper()方法进行初始化和Looper.loop()方法开启消息循环。
- Handler使用不当常常会导致内存泄漏等性能问题。Handler内存泄漏的原因通常是因为引用的持有及生命周期不一致导致的。
- 我们可以通过弱引用+静态内部类的方式来解决Handler内存泄漏的问题,或者在Activity结束时,调用Handler的removeCallbacksAndMessages(null)方法来回收Handler及消息。
- 另外,Message对象过多的创建也会导致内存过大,GC频繁等问题,我们可以通过Message类提供的obtain()方法来获取Message对象,obtain内部使用了一个对象池来减少新对象的创建。
通过上面的分析,我们已经对如何使用Handler消息机制和它的原理有了很清晰的了解。但是,在Looper循环的时候,loop()方法中的无限循环是如何做到不占用系统资源的呢?它的休眠和唤醒又是什么原理实现的呢?我们将在下一章进行分析~