Handler机制是Android开发中最常见的机制,可以说贯穿整个Android,在探究Handler机制原理之前,我们先来捋一下用法
1.handler.post(Runnable)
2.handler.postdelayed(Runnable,int)
3.sendMessage(Message)
4.sendDelayMessage(Message,int)
从形式上就可以看出,第一种用法和第二种用法其实是一样的,只不过一个立即发送一个延迟发送而已,同理三四也是一样
我们先探究handler.post,post具体用法是
handler = new Handler();
hander.post(new runnable(){run(//具体逻辑){}});
首先我们找到Handler源码
public Handler() { this(null, false); }
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()); } } 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; }
代码不多,一个一个解析,当我们handler = new Handler();时,实际上传入了两个参数进入Handler,一个是null,另一个是false
callback是一个接口,里面只有一个方法
public interface Callback { public boolean handleMessage(Message msg); }
这里传入的是null,暂且放下不谈。这个函数主要过程是初始化Handler里的变量mLooper,mQueue,mCallback以及mAsynchronous
首先mLooper = Looper.myLooper();从这里拿到一个looper,为什么这里会拿到一个looper对象?从这里就要引出ActivityThread.main方法
一个app启动开始,最先调用的方法是ActivityThread.main方法,如同java里的main方法一样,这个方法是程序的入口,那么这个方法到底干了什么
public static void main(String[] args) { SamplingProfilerIntegration.start(); // CloseGuard defaults to true and can be quite spammy. We // disable it here, but selectively enable it later (via // StrictMode) on debug builds, but using DropBox, not logs. CloseGuard.setEnabled(false); Environment.initForCurrentUser(); // Set the reporter for event logging in libcore EventLogger.setReporter(new EventLoggingReporter()); Security.addProvider(new AndroidKeyStoreProvider()); // Make sure TrustedCertificateStore looks in the right place for CA certificates final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId()); TrustedCertificateStore.setDefaultUserDirectory(configDir); Process.setArgV0("<pre-initialized>"); Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
多余的东西我们不看,我们只看Looper.prepareMainLooper();以及Looper.loop();
/** * Initialize the current thread as a looper, marking it as an * application's main looper. The main looper for your application * is created by the Android environment, so you should never need * to call this function yourself. See also: {@link #prepare()} */ 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)
private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }
这里先通过sThreadLocal.get()去查找looper,如果发现线程中以及有looper了就抛出异常,Only one Looper may be created per thread每一个线程中只能有一个loop
这里我们是程序第一次启动,当然没有looper,于是接下来调用new Looper创建一个looper
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
looper里包含了一个mQueue和mThread,将当前线程传入,这里我们是主线程传入
然后将looper放入sThreadLocal中,ThreadLocal是一个数据内部储存类,它的神奇之处就在于,使用ThreadLocal储存的对象在不同线程访问会得出不同的对象
打个比方,我们用static进行变量储存的时候,就像我们玩游戏时的公会银行,谁都可以取,谁也可以存,不管谁取出来都是一样的东西,因此在我们不同线程取出来static变量其实是一样的。而使用ThreadLocal进行储存的话,就像我们现实中的银行,一人一个账户,我不可以取你存进去的东西。这里有什么好处呢,这里就可以保证了我(Thread)取出来的东西(looper)一定是我的。其实说了这么说,ThreadLocal里面的实现也很简单,只不过将当前的Thread作为key传进去,本质上还是键值对的方式进行存储。
好,说了这么多让我们回到主线,prepare方法我们已经执行完了,在里面我们新建了looper并存储在ThreadLocal中
程序继续走下去,走到sMainLooper = myLooper();
public static Looper myLooper() { return sThreadLocal.get(); }
这里设置了主线程的looper。
综上分析,mLooper = Looper.myLooper();拿到的是主线程的looper。这里就要注意一点,如果我们是在子线程中调用Handler,我们必须给它传入一个looper,因为在子线程中是没有looper的,那我们在子线程中就应该通过Looper.prepare()拿到一个新的looper,将这个looper传入Handler的构造函数就好了
OK,我们回到handler的构造函数中,我们已经拿到了一个looper对象,接下来我们就可以从Looper中取出mQueue,然后的初始化就没有什么好分析了。
我们来看看handler.post函数为什么明明在子线程中,而它的run方法却是在主线程中
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }
这里我们进入getPostMessage看看
private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }
Message.obtain返回一个新的Message对象
public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; m.flags = 0; // clear in-use flag sPoolSize--; return m; } } return new Message(); }
然后将Runnable塞进m.callback里
然后我们接着调用sendMessageDelayed(getPostMessage(r), 0);
public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); }
public boolean sendMessageAtTime(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); }
最后将mQueue,msg,uptimeMillis一起传入
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
这里的target是一个Handler变量,这里它传入了主线程的Handler
接着走下去
boolean enqueueMessage(Message msg, long when) { if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } if (msg.isInUse()) { 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("MessageQueue", 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) { // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; needWake = mBlocked; } 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(); Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; }
这里我们可以看到一个死循环,我们不必拘泥于代码本身,这段代码的意思将manager插入mQueue
代码走到这里仿佛走死了,说好的调用切换线程呢?
不急,还记得我们ActivityThread.main函数吗?里面我们还有一个方法没讲呢,那就是looper.loop();
这就是Handler之所以能切换线程的关键所在,让我们走进方法
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */ public static void loop() { 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(); } }
进入queue.next()方法
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; // -1 only during first iteration 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 (false) Log.v("MessageQueue", "Returning message: " + msg); 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; } // If first time idle, then get the number of idlers to run. // Idle handles only run if the queue is empty or if the first message // in the queue (possibly a barrier) is due to be handled in the future. if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount <= 0) { // No idle handlers to run. Loop and wait some more. mBlocked = true; continue; } if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } // Run the idle handlers. // We only ever reach this code block during the first iteration. for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; // release the reference to the handler boolean keep = false; try { keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf("MessageQueue", "IdleHandler threw exception", t); } if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } // 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; } }
这里的next方法是一个无线循环的方法,如果消息队形中没有消息,那么next方法会一直阻塞在这里,当有新消息时,next方法会返回这条消息并将消息删除
其实当初看书看到这里还是有疑问的,作者本身爱较真,奈何能力实在有限,等到很长时间才反应过来。作者是这么理解的,我的方法虽然在子线程调用,但是我将子线程中执行的方法打包成一个Message,然后发送到一个共同的Messagequeue,提醒主线程告诉它我已经发消息了,主线程收到我的提醒以后就去Messagequeue中拿出Message,把Message中的方法调用出来执行,这才是Handler消息机制的原理。很多Handler详解都没有告诉作者原来还有提醒这一步,导致作者原来对Handler机制十分模糊。
提醒主线程这一步是在native层实现的,翻了翻源码,发现只有在enqueueMessage方法中if (needWake) nativeWake(mPtr);比较可疑,只能靠一些边角东西推理了
private long mPtr; // used by native code
而在next方法中有这么一个方法
nativePollOnce(ptr, nextPollTimeoutMillis);
作者猜测这个方法应该是阻塞这个方法并接收nativeWake的了。
扯了这么多,还是回到Looper.loop方法中,我们已经取出Message了,接下来就调用msg.target.dispatchMessage(msg);
/** * Handle system messages here. */ public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
这就是消息处理的方法,不同的消息发送处理方式是有先后之分的,一开始我们调用的方法中是将runnable放入Message中,所以msg.callback != null
这个方法实现就更简单了
private static void handleCallback(Message message) { message.callback.run(); }
直接就调用runnable.run方法,简单粗暴我喜欢,到这里通过handler.post(Runnable)的流程已经梳理完了
接下来我们来看看sendMessage(Message),其实两种方式原理都是一样的,只不过一开始走的路不太一样
如果我们要用sendMessage方法,我们需要重写Handler.handleMessage方法,让我们进入源码
public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); }
public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); }
public boolean sendMessageAtTime(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); }
剩下的源码都和post是一模一样的,只有结尾是不一样的,那我再贴一遍好了
/** * Handle system messages here. */ public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
因为我们没有传入Runnable,所以msg.callback == null,而mcallback是Handler初始化时传入的参数,我们调用的不是这个构造方法,所以mcallback也是空,最后调用我们重写的handleMessage方法。
其实Handler使用方法还不止四种,作者只是举出了几个常见的,例如还有view.post方法,或者new一个实现了callback接口的对象,将callback传入Handler一样能异步调用,这里就不再多说了。当然,最常见的还是直接在子线程中调用runOnUIThread方法,该方法里的逻辑代码全部运行在主线程中。
最后再说一点其他的,毕竟文章写了是详解
1.假如我们需要消息回传怎么办呢,我不满足子线程光发送消息给主线程,我还想主线程发消息给子线程?
我们回看当初子线程给主线程发送消息的机制,其实是子线程拿到了主线程的looper,从Looper中取出了mqueque,然后两个线程通过mqueue进行通信
所以如果主线程要想发消息给子线程,就一定要拿到子线程的looper。子线程的looper怎么创建在上文已经说了,最后还要调用Looper.loop方法,不然光主线程做好发消息的准备了,子线程还没做好接收消息的准备。
2.Handler中还有其他构造方法,比如说传入looper对象进行构造,当主线程给子线程Thread进行通信时,调用Thread.loop可能拿不到子线程的loop,因为这个时候子线程的loop可能还没创建,这时候又该怎么办?
换个构造方法!!!!好吧,你不想换怎么办,那就使用HandlerThread,HandlerThread是一个扩展了Thread的类,专门用于Handler通信,在HandlerThread的run方法里自带创建looper和loop.loop
public class HandlerThread extends Thread
HandlerThread中有一个getloop方法,我们来看一下
public Looper getLooper() { if (!isAlive()) { return null; } // If the thread has been started, wait until the looper has been created. synchronized (this) { while (isAlive() && mLooper == null) { try { wait(); } catch (InterruptedException e) { } } } return mLooper; }
如果Thread还没有开始,那么就会调用wait方法,那么何时被唤醒呢
@Override public void run() { mTid = Process.myTid(); Looper.prepare(); synchronized (this) { mLooper = Looper.myLooper(); notifyAll(); } Process.setThreadPriority(mPriority); onLooperPrepared(); Looper.loop(); mTid = -1; }
在HandlerThread的notifyAll中,调用这个方法后线程就不再处于wait状态,于是主线程的Handler就可以获取到子线程的loop
3.非UI线程真的不能更新ui吗,平常我们都是用Handler来更新UI的,可是真的如此吗?
并不是,我们之所以不让子线程更新UI,是因为有一个并发的问题,Android为了解决这个问题,就在View类里更新UI的方法进行了判断,判断该线程是不是主线程,可是View的实现类ViewRootImp是在OnResume中初始化的额,于是,当我们在oncrate中创建线程并更新UI的时候就有可能在OnResume方法之前完成,因此凡事都不是绝对的