前言
转载至:https://github.com/francistao/LearningNotes/blob/master/Part1/Android/%E7%BA%BF%E7%A8%8B%E9%80%9A%E4%BF%A1%E5%9F%BA%E7%A1%80%E6%B5%81%E7%A8%8B%E5%88%86%E6%9E%90.md
Android的线程间通信主要Handler、Looper、Message、MessageQueue这四个。
1. Looper
在 Looper 中维持一个 Thread 对象以及 MessageQueue,通过 Looper 的构造函数可以知道:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Looper 在构造函数里干了两件事:
- 将线程对象指向了创建 Looper 的线程
- 创建了一个新的 MessageQueue
Looper 主要有两个方法:
- looper.loop()
- looper.prepare()
1.1 looper.loop()
在当前线程启动一个Message loop机制,此段代码将直接分析出Looper、Handler、Message、MessageQueue的关系:
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;//获得与Looper绑定的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();
//进入死循环,不断地去取对象,分发对象到Handler中消费
for (;;) {
Message msg = queue.next(); // 不断的取下一个Message对象,在这里可能会造成堵塞。
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
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
//在这里,开始分发Message了
//至于这个target是神马?什么时候被赋值的?
//我们一会分析Handler的时候就会讲到
msg.target.dispatchMessage(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);
}
//当分发完Message之后,当然要标记将该Message标记为 *正在使用* 啦
msg.recycleUnchecked();
}
}
分析了上面的源代码,我们可以意识到,最重要的方法是:
- queue.next()
- msg.target.dispatchMessage(msg)
- msg.recycleUnchecked()
Looper 中最重要的部分都是由 Message、MessageQueue 组成的!这段最重要的代码中涉及到了四个对象,他们与彼此的关系如下:
- MessageQueue :装食物的容器
- Message :被装的食物
- msg.target :食物消费者
- Looper :负责分发食物的人
1.2 looper.prepare()
在当前线程关联一个Looper对象。
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//在当前线程绑定一个Looper
sThreadLocal.set(new Looper(quitAllowed));
}
以上代码只做了两件事:
判断当前线程有没有 Looper,如果有就抛出异常(Android 规定一个线程只能拥有一个与自己关联的 Looper)。
如果没有就设置一个新的 Looper 到当前线程
2. Handler
我们通常使用 Handler 的通常方法是:
Handler handler = new Handler(){
//这个方法是在哪里被回调的?
@Override
public void handleMessage(Message msg) {
//Handler your Message
}
};
还是先看一下 Handler 的构造方法:
//空参数的构造方法与之对应,这里只给出主要的代码,具体大家可以到源码中查看
public Handler(Callback callback, boolean async) {
//打印内存泄露提醒log
....
//获取与创建Handler线程绑定的Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//获取与Looper绑定的MessageQueue
//因为一个Looper就只有一个MessageQueue,也就是与当前线程绑定的MessageQueue
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
带上问题:
- Looper.loop() 死循环中的 msg.target 是什么时候被赋值的?
- handler.handlerMessage(msg) 在什么时候被回调的?
2.1 Looper.loop() 死循环中的msg.target是什么时候被赋值的?
要分析这个问题,很自然的我们想到从发送消息开始,无论是handler.sendMessage(msg) 还是 handler.sendEmptyMessage(what),我们最终都可以追溯到以下方法:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
//引用Handler中的MessageQueue
//这个MessageQueue就是创建Looper时被创建的MessageQueue
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
//将新来的Message加入到MessageQueue中
return enqueueMessage(queue, msg, uptimeMillis);
}
接下来分析 enqueueMessage(queue, msg, uptimeMillis):
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//显而易见,在此给msg.target赋值handler!
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
2.2 handler.handleMessage(msg) 在什么时候被回调的?
通过以上的分析,我们很明确的知道 Message 中的 target 是在什么时候被赋值的了,我们先来分析在 Looper.loop() 中出现过的过的 dispatchMessage(msg) 方法:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//在这里回调
handleMessage(msg);
}
}
加上以上分析,我们将之前分析结果串起来,就可以知道了某些东西: Looper.loop() 不断地获取 MessageQueue 中的 Message,然后调用与 Message 绑定的 Handler对象的 dispatchMessage 方法,最后,我们看到了 handleMessage 就在 dispatchMessage 方法里被调用的。
3. Handler内存泄漏分析及解决
首先,请浏览下面这段 handler 代码:
public class SampleActivity extends Activity {
private final Handler mLeakyHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// ...
}
}
}
在使用 handler 时,这是一段很常见的代码。但是,它却会造成严重的内存泄漏问题。在实际编写中,我们往往会得到如下警告:
⚠️:In Android, Handler classes should be static or leaks might occur.
3.1 分析
3.1.1 Android 角度
当 Android 应用程序启动时,framework 会为该应用程序的主线程创建一个 Looper 对象。这个 Looper 对象包含一个简单的消息队列Message Queue,并且能够循环的处理队列中的消息。这些消息包括大多数应用程序 framework 事件,例如 Activity 生命周期方法调用、button 点击等,这些消息都会被添加到消息队列中并被逐个处理。
另外,主线程的 Looper 对象会伴随该应用程序的整个生命周期。
然后,当主线程里,实例化一个 Handler 对象后,它就会自动与主线程 Looper 的消息队列关联起来。所有发送到消息队列的消息 Message 都会拥有一个对 Handler 的引用,所以当 Looper 来处理消息时,会据此回调 [Handler#handleMessage(Message)] 方法来处理消息。
3.1.2 Java角度
在 java 里,非静态内部类 和 匿名类 都会潜在的引用它们所属的外部类。但是,静态内部类却不会。
3.2 泄漏来源
请浏览下面一段代码:
public class SampleActivity extends Activity {
private final Handler mLeakyHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// ...
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Post a message and delay its execution for 10 minutes.
mLeakyHandler.postDelayed(new Runnable() {
@Override
public void run() { /* ... */ }
}, 1000 * 60 * 10);
// Go back to the previous Activity.
finish();
}
}
当 activity 结束 (finish) 时,里面的延时消息在得到处理前,会一直保存在主线程的消息队列里持续10分钟。而且,由上文可知,这条消息持有对 handler 的引用,而 handler 又持有对其外部类(在这里,即 SampleActivity)的潜在引用。这条引用关系会一直保持直到消息得到处理,从而,这阻止了SampleActivity 被垃圾回收器回收,同时造成应用程序的泄漏。
⚠️:上面代码中的Runnable类--非静态匿名类--同样持有对其外部类的引用。从而也导致泄漏。
3.3 解决方案
首先,上面已经明确了内存泄漏来源:
只要有未处理的消息,那么消息会引用 handler,非静态的 handler 又会引用外部类,即 Activity,导致 Activity 无法被回收,造成泄漏;
Runnable 类属于非静态匿名类,同样会引用外部类。
为了解决遇到的问题,我们要明确一点:静态内部类不会持有对外部类的引用。所以,我们可以把 handler 类放在单独的类文件中,或者使用静态内部类便可以避免泄漏。
另外,如果想要在 handler 内部去调用所在的外部类 Activity,那么可以在 handler 内部使用弱引用的方式指向所在 Activity,这样统一不会导致内存泄漏。
对于匿名类 Runnable,同样可以将其设置为静态类。因为静态的匿名类不会持有对外部类的引用。
public class SampleActivity extends Activity {
/**
* Instances of static inner classes do not hold an implicit
* reference to their outer class.
*/
private static class MyHandler extends Handler {
private final WeakReference mActivity;
public MyHandler(SampleActivity activity) {
mActivity = new WeakReference(activity);
}
@Override
public void handleMessage(Message msg) {
SampleActivity activity = mActivity.get();
if (activity != null) {
// ...
}
}
}
private final MyHandler mHandler = new MyHandler(this);
/**
* Instances of anonymous classes do not hold an implicit
* reference to their outer class when they are "static".
*/
private static final Runnable sRunnable = new Runnable() {
@Override
public void run() { /* ... */ }
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Post a message and delay its execution for 10 minutes.
mHandler.postDelayed(sRunnable, 1000 * 60 * 10);
// Go back to the previous Activity.
finish();
}
}
原文链接
4. 总结
通过以上的分析,我们可以很清晰的知道 Handler、Looper、Message、MessageQueue 这四者的关系以及如何合作的了。
当我们调用 handler.sendMessage(msg) 方法发送一个 Message 时,实际上这个 Message 是发送到与当前线程绑定的一个 MessageQueue 中,然后与当前线程绑定的 Looper 将会不断的从 MessageQueue 中取出新的 Message,调用 msg.target.dispathMessage(msg) 方法将消息分发到与 Message 绑定的 handler.handleMessage() 方法中。
一个 Thread 对应多个 Handler, 一个 Thread 对应一个 Looper 和 MessageQueue,Handler 与 Thread 共享 Looper 和 MessageQueue。 Message 只是消息的载体,将会被发送到与线程绑定的唯一的 MessageQueue 中,并且被与线程绑定的唯一的 Looper 分发,被与其自身绑定的 Handler 消费。
申明:开始的图片来源网络,侵删