[TOC]
android的消息处理有三个核心类:Looper,Handler和Message,
主要接受子线程发送的数据, 并用此数据配合主线程更新UI。
部分图片来至CodingMyWorld博客,3Q
public class LooperThread extends Thread {
@Override
public void run() {
// 将当前线程初始化为Looper线程
Looper.prepare();
// ...其他处理,如实例化handler
// 开始循环处理消息队列
Looper.loop();
}
}
通过上面两行核心代码,你的线程就升级为Looper线程了,就具备消息处理的功能!
Looper.prepare()
通过上图可以看到,现在你的线程中有一个Looper对象,它的内部维护了一个消息队列MQ。注意,一个Thread只能有一个Looper对象,以下源码使用到ThreadLoacal,可以想象成一个线程的属性/变量,想了解更多请点击链接
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
//获取当前线程对应线程变量:Looper,重复执行此方法会有如下报错提示
//
//"Only one Looper may be created per thread"
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//将当前初始化的Looper对象保存到当前线程变量中
sThreadLocal.set(new Looper(quitAllowed));
}
Looper.loop()
调用loop方法后,Looper线程就开始真正工作了,它不断从自己的MQ中取出队头的消息(也叫任务)执行。
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
//获得当前线程的Looper对象
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;
// 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();
//循环消息体
for (;;) {
//去出单个消息体
Message msg = queue.next(); // might block
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);
}
//向目标对象中分发当前循环到的消息体,下面将会具体讲解
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);
}
//使用完消息之后需要对它进行回收
msg.recycleUnchecked();
}
}
msg.target.dispatchMessage(msg)
解释去Message
类中查找可以发现
/*package*/ Handler target;
其实target就是handler对象,那handler是如何和一个Message发生联系的,稍等?下面移步Handler源码分析
public Handler(Callback callback, boolean async) {
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());
}
}
//获取同一线程的Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//获取到同一线程的消息队列
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
在我们sendMessage()
的时候都是先用obtainMessage
来获取一个Message
public final Message obtainMessage()
{
return Message.obtain(this);
}
移步Message的方法看详细
public static Message obtain(Handler h) {
Message m = obtain();
//是不是so easy,真正的联系在这里
m.target = h;
return m;
}
如果你是使用post(Runnable r)
来发送消息的,那应该构造一个Message来发出去,不信可以看源码
Handler类中:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
/**
*构造一个Message
*/
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
//与PostMessage不同的是这个runable是给了callback属性
m.callback = r;
return m;
}
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
//处理Runable消息
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//处理具体Message,这个是你在消息处理的时候重写的方法实现
handleMessage(msg);
}
}
/*package*/ ActivityThread mMainThread;
ActivityThread源码中查看:
final Looper mLooper = Looper.myLooper();
private Handler mLeakHandler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Logger.d(msg.toString());
}
};
@Override
protected void onResume() {
super.onResume();
//延迟10s来模拟场景
mLeakHandler.sendEmptyMessageDelayed(0x1,10000);
finish();
}
//省略其他代码
由于这个Handler作为内部类声明在Activity内部,普通的内部类对象隐式地保存了一个指向外部类对象的引用,所以这个Handler对象保存了一个指向Activity对象的引用。而这个Handler对象的生命周期可能比Activity生命周期长,比如当有一个后台线程持有该Handler,且该线程在执行一个长时间任务。Handler通过发送Message与主线程交互,Message发出之后是存储在MessageQueue中的,有些Message也不是马上就被处理的。在Message中存在一个 target,是Handler的一个引用,如果Message在Queue中存在的时间越长,就会导致Handler无法被回收。如果Handler是非静态的,则会导致Activity不会被回收。但是注意这个泄漏时临时的!当这个消息处理完引用关系也就不存在了,下次GC的时候也就能回收啦
修改方法:
private static class MyHandler extends Handler {
private WeakReference reference;
public MyHandler(Activity activity) {
reference = new WeakReference(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
LeakActivity activity = (LeakActivity) reference.get();
if (activity != null) {
Logger.d("activity != null"+activity.toString());
} else {
Logger.d("activity = null");
}
}
}
private final Handler mHandler = new MyHandler(this);
同时你需要在调用一下方法,避免不必要的回调(虽然不会报错了)
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}