用了handler这么久,一直没有深入了解过,用了些时间认真看了一下,决定写下来,对需要的人是一种分享,更是对自己的记录吧,先看一段平时handler处理异步消息的代码,应该都很熟悉,如下:
public class MainActivity extends Activity {
TextView mTextView = null;
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
mTextView.setText("get msg arg1 value is " + msg.arg1);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.textview);
new Thread(new Runnable() {
@Override
public void run() {
Message msg = new Message();
msg.arg1 = 1;
handler.sendMessage(msg);
}
}).start();
}
}
很简单,运行一下程序,会得到mTextView控件的内容显示"get msg arg1 value is 1", 那么下面我们就来从源码角度来看一下这个简单的例子的调用流程。
首先我们要知道android程序启动时,系统自动为我们在主线程当中调用了Looper类的prepare()方法和loop()方法,具体参考源码中android.app包下的ActivityThread.java中的
main()方法,那么首先先从Looper类的prepare()方法入手,源码位置在android.os包下面,包括下文涉及到的Handler.java, Message.java, MessageQueue.java等类都在这个包下面。
Looper的prepare()方法源码如下:
public static void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
使用sThreadLocal对象中来set,get一个Looper对象, 方法中调用get()方法时不为空则抛出异常,可见每个线程只能set()一次Looper对象,由此可知每个线程最多且只有一个Looper对象。
我们看到set()方法中new了一个Looper对象,看下Looper的构造函数的源码,如下:
private Looper() {
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}
可见调用Looper的构造方法时,会new一个消息队列对象mQueue,所以可以看到一个Looper对象对应一个MessageQueue对象,此mQueue对象会在下面new Handler()时用到,prepare()方法分析到这先。
下面来看Looper类的loop()方法,源码如下:
public static void loop() {
Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
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();
while (true) {
Message msg = queue.next(); // might block
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
long wallStart = 0;
long threadStart = 0;
// 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);
wallStart = SystemClock.currentTimeMicro();
threadStart = SystemClock.currentThreadTimeMicro();
}
msg.target.dispatchMessage(msg);
if (logging != null) {
long wallTime = SystemClock.currentTimeMicro() - wallStart;
long threadTime = SystemClock.currentThreadTimeMicro() - threadStart;
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
if (logging instanceof Profiler) {
((Profiler) logging).profile(msg, wallStart, wallTime,
threadStart, threadTime);
}
}
// 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.recycle();
}
}
}
loop()方法中首先调用了myLooper()方法,先来看下myLooper()方法:
public static Looper myLooper() {
return sThreadLocal.get();
}
可见myLooper()方法就是此线程对应的那个Looper对象,也就是调用在prepare()方法时new出来的那个Looper对象,接着继续看loop()方法,其中有一个死循环while(true), 在死循环中使用局部变量queue的next()方法来一个一个的取出Message对象,queue其实就是Looper对象对应的那个MessageQueue对象,也就是在new Looper对象时new 出来的那个MessageQueue对象,然后接着向下看,有一句msg.target.dispatchMessage(msg); 这里其实就是将取出来的Message对象分发消息到handleMessge()方法,此处后面再说(to be continued...)
有了上面的关于Looper的铺垫,下面我们来看文章开头例子程序的子线程中代码,关注在子线程中sendMessage()怎么样最终执行了主线程中handler对象的handleMessage()方法。
首先来看new Handler()中做了什么,像分析Looper时一样,来看Handler()的构造函数,源码如下:
public Handler() {
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 that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = null;
}
同样调用了Looper的myLooper()方法来获取Looper在prepare()时new 出来的那个Looper对象,然后同样得到 new Looper时new 出来的 mQueue这个消息队列对象,上面已经说到mQueue这个对象会在new Handler()时用到,Handler对象和Looper对象中保持的是一个mQueue引用, 也就是说一个线程对应一个消息队列对象。
接下里我们来看程序例子子线程当中做了事情,首先 Message msg = new Message() 这一句,Message构造函数的源码如下:
public Message() {
}
可见是空的,简单,但我们此时要注意一点,Message类中有声明了一个Handler 类型的成员变量叫做 target,后面在Looper中会用到这个target来分发消息,后面会写到。
接着程序往下走,下一句执行了msg.arg1 = 1 这一句是将msg对象的arg1变量设为 1,
然后程序中执行了 handler.sendMessage()方法,sendMessage源码如下:
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
接着看sendMessageDelayed((Message msg, long delayMillis)方法,如下:
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
接着看sendMessageAtTime(Message msg, long uptimeMillis)方法,如下:
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this;
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
此方法中重要的一句为 msg.target = this, 上面在new Message()对象的时候已经提醒大家注意Message类中target这个成员变量,
继续向下调用了MessageQueue的enqueueMessage(Message msg, long when)方法,现在去看看这个方法,如下:
final boolean enqueueMessage(Message msg, long when) {
if (msg.isInUse()) {
throw new AndroidRuntimeException(msg
+ " This message is already in use.");
}
if (msg.target == null && !mQuitAllowed) {
throw new RuntimeException("Main thread not allowed to quit");
}
final boolean needWake;
synchronized (this) {
if (mQuiting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
} else if (msg.target == null) {
mQuiting = true;
}
msg.when = when;
//Log.d("MessageQueue", "Enqueing: " + msg);
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked; // new head, might need to wake up
} else {
Message prev = null;
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
msg.next = prev.next;
prev.next = msg;
needWake = false; // still waiting on head, no need to wake up
}
}
if (needWake) {
nativeWake(mPtr);
}
return true;
}
代码比较长,主要的工作就是将消息对象按照时间顺序入列到消息队列里面,综合上一步来讲,就是加入到消息队列当中的这个消息对象的target属性是上文中的handler对象。
现在将消息加到消息队列以后,消息队列有了消息,那么我们回过头接着看上面的to be continued处的loop()方法,loop()方法会将此消息对象取出,在上面分析到了loop()方法中执行的这一句代码msg.target.dispatchMessage(msg), 那么由以上的分析可知,此处的msg.target即为上文的handler对象,那么此处即是调用handler对象的dispatchMessage(Message msg)方法,那来看一下Handler类中的这个方法源码做了什么事情:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
第一个if判断是使用post()方法发送消息请求时会用到的,由于我们new Handler()对象时将mCallback初始化为空,所以第二个if判断也用不到,所以可见最后终于调用了这个代码块中最后一句handleMessage(msg)方法,这个方法则是我们例子程序中在new Handler时重写的方法,所以执行了此方法后例子程序中的mTextView才得以显示"get msg arg1 value is 1", 到此整个流程得以走通。
第一次写博客,以上整个流程分析难免有些啰嗦或者阐述不清的地方,关于handler这块,建议还是要认真多看两遍源码,比看帖子容易理解。