Android 异步消息处理机制Looper、Handler、Message三者关系
Looper,Handler,Message三者是我们常常用来再子线程跟新UI的,我们把Message发给Handler,然后,handler调用HandlerMessage()方法,我们在这个方法里面更新UI。那么Looper呢,又是什么,下面我来给大家介绍一下三者的关系。
首先要说明的是,每个线程最多只有一个Looper,在线程里面调用Looper.prepare()就是为这个线程设置了一个Looper,所以在子线程,我们使用这个三者前,一定要调用Looper.prepare()方法,而主线程其实已经替我们调用过这个方法了,所以我们不必重复调用。
另外我们还要主动调用Looper.loop()方法,下面说一下为什么。
Looper里面包含一个MessageQueue,这个就是一个消息队列,handler发送的消息Message都会到这个队列里面来,Looper.loop()不断从MessageQueue取出Message,没有则阻塞,有则调用Message.target的dispatchMessage(msg)方法,这个方法Message也传了过去。dispatchMessage(msg)方法里面又调用handleMessage()方法,这方法就是我们的具体实现。
那么通过刚才的描述也很清楚了,Message.target其实就是Handler。
OK,那么我再来总结一下三者之间的引用关系
Looper:拥有一个MessageQueue,里面包含所有Message
Message:每个Message都持有一个Handler引用
Handler:handler持有Looper
也就是说任意一个可以获取其他两个的引用,从而相互关联。
前面的可能表达得不是很好,下面通过源码来说明
先看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是一个java类库提供的一个类,用于不同线程中维护同一个变量。这个比较难解释,也就是说一个变量,本来应该是多个线程共享的,所以我们多线程操作的时候,要考虑同步的问题,但是使用了ThreadLocal以后,系统为每个线程分配一个空间来存储这个变量,也就是说每个线程只要维护好自己的变量就可以了,这个变量也就不是共享的了,从而避免同步的问题。
我们可以看到,prepare()通过sThreadLocal.get()方法,只需要存在一个Looper实例,如果重复prepare()就会抛出异常(类似单例模式,不同的是重复创建的结果是抛出异常)。
然后如果没有Looper实例,就去new一个,并且保存在sThreadLocal中。
下面看new Looper()方法
private Looper() { mQueue = new MessageQueue(); mRun = true; mThread = Thread.currentThread(); }可以看到这个构造方法是私有的(所以说像单例模式),并且创建了一个消息队列mQueue,还保存了创建这个Looper的线程的引用(每个线程都只有一个Looper)
prepare()以后,我们就要主动调用静态的loop()方法,从mQueue消息队列中不断取出消息了
下面看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(); } } }myLooper()方法就是一个get方法,用于获取looper对象,前几行代码说明没有prepare(),是不可以调用loop()的
然后调用进入了一个死循环,queue.next()不断从队列中获取message对象,一旦获取到messgae,就调用msg.target.dispatchMessage(msg)方法。
ok,loop()基本上就做了这些事情,那么有几个疑问,message是什么时候添加到queue里面的,然后msg.target对象是什么,dispatchMessage(msg)方法又做了什么呢。
接下来我们看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; }
从构造函数可以看出,handler获取了Looper对象的引用,并且获取了mQeueu消息队列的引用
那么我们这么把msg添加到队列中的呢,一般我们都是调用handler的sendMessage()方法来发送消息的,但是这些方法最后都要调用同一个方法,sendMessageAtTime
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为handler本身,然后把msg加入queue消息队列。
原来我们sendMessage的时候就把msg放到消息队列里面了。
最后我们再看一遍Loop()方法里面的
msg.target.dispatchMessage(msg);也就是说调用了handler的dispatchMessage()方法,所以总的来说是这样的,handler把msg加入队列,loop不断取出msg,然后调用handler的dispatchMessage()方法去处理msg
接着看dispatchMessage()方法
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
这个方法里面,我们调用了handleMessage方法,其实就是一个回调,这个方法里面,我自己写对msg的处理
从上面源码总结基本上可以理清楚Looper、Handler、Message三者关系的关系了,另外再提一下在子线程中进行UI操作的其他方法:
1. Handler的post()方法
2. View的post()方法
3. Activity的runOnUiThread()方法
我们先来看下Handler中的post()方法,代码如下所示:public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }
原来这里还是调用了sendMessageDelayed()方法去发送一条消息啊,并且还使用了getPostMessage()方法将Runnable对象转换成了一条消息,我们来看下这个方法的源码:
private final Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }
注意,这里讲callback设置为runnable对象里面,还记得我们的dispatchMessage()方法吗
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }里面首先对callback是否为空进行了判断,不为空,要调用handleCallback()
private final void handleCallback(Message message) { message.callback.run(); }这里主动调用了runnable的run()方法,所以这不是开启子线程的操作哦!(注意,主动调用run()方法是不会开启线程的)
然后再来看一下View中的post()方法,代码如下所示:
public boolean post(Runnable action) { Handler handler; if (mAttachInfo != null) { handler = mAttachInfo.mHandler; } else { ViewRoot.getRunQueue().post(action); return true; } return handler.post(action); }
原来就是调用了Handler中的post()方法
最后再来看一下Activity中的runOnUiThread()方法,代码如下所示:
public final void runOnUiThread(Runnable action) { if (Thread.currentThread() != mUiThread) { mHandler.post(action); } else { action.run(); } }
如果当前的线程不等于UI线程(主线程),就去调用Handler的post()方法,否则就直接调用Runnable对象的run()方法。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。