为什么要使用Handler
-
因为在Android中访问UI只能在主线程中进行,如果在子线程中运行,则程序会抛出异常。
// ViewRootImpl.java void checkThread() { if (mThread != Thread.currentThread()) { throw new CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views."); } }
-
为什么不允许在子线程中访问UI?
因为Android的UI并不是线程安全的,如果在多线程中执行UI的操作,那么UI的状态是不可控的,这个时候就会出现各种问题。
那么最好的办法就是只能在一个线程中执行UI的操作。
而Android主线程中又不能执行耗时操作,因为那样就会导致程序的ANR。
所以Android提供了Handler这样的机制,用来在子线程中执行耗时操作之后,发送消息给主线程,主线程再执行UI的更新操作。
Handler机制原理分析
这里我们以Android UI线程的Handler来分析。
Handler的创建
Android的应用入口是ActivityThread.main()
函数,UI线程的Handler就是在这个函数中创建的。
public static void main(String[] args) {
Looper.prepareMainLooper();
thread.attach(false);
ActivityThread thread = new ActivityThread();
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop();
}
当main函数执行之后,应用就启动了,从这个时候开始,Looper就会一直从消息队列中取出消息并处理。而应用会通过Handler来不断的添加消息给消息队列。通过不断的添加消息和取出消息,整个消息机制就被运转起来了。
Looper
从上面可以看到在Handler创建前后,通过Looper的prepare
和loop
方法,创建了Looper对象和开启消息循环。
-
构造函数
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
- 创建了MessageQueue对象,这个对象是消息队列,主要用来存放我们的消息,会在下面具体分析。
- 获取当前的线程并保存起来
-
prepare
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)); }
- 判断ThreadLocal中在当前线程是否已经存在了Looper对象,如果存在,则抛出异常,所以可以得出在同一个线程中只能存在一个Looper对象。
- 在ThreadLocal存入当前线程的Looper对象。
- ThreadLocal对象是一个线程内部的数据存储类,通过它可以在指定的线程存储数据,当存储数据之后,就只能在指定的线程中获取到存储的数据,其他线程是不能获取到数据的。
-
loop
public static void loop() { final Looper me = myLooper(); // 获取消息队列 final MessageQueue queue = me.mQueue; // 死循环来轮询的从消息队列中获取消息 for (;;) { Message msg = queue.next(); // might block if (msg == null) { return; } try { msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } msg.recycleUnchecked(); } }
- loop函数会不断的从消息队列中取出消息,当queue.next()为空的时候就直接阻塞住
- msg不为空,调用
msg.target.dispatchMessage(msg)
处理消息,而msg.target
其实就是Handler对象,取出消息之后就交给Handler来处理发送的消息了。
-
quit
public void quit() { mQueue.quit(false); } public void quitSafely() { mQueue.quit(true); }
调用MessageQueue的quit方法来退出Looper。其中上面的是直接退出,下面的是安全的退出,也就是只标记一下,当消息队列中的消息全部执行完成之后安全的退出。
当我们在子线程中创建Looper和Handler的时候,如果我们不调用quit方法的话,子线程就会一直处于等待状态,所以我们需要调用quit方法退出Looper。
MessageQueue
在Looper中大量的使用到了MessageQueue,而MessageQueue主要就是两个操作插入消息和读取消息并删除消息,我们来一起分析下:
-
构造函数
MessageQueue(boolean quitAllowed) { mQuitAllowed = quitAllowed; mPtr = nativeInit(); }
在构造函数中,调用了
nativeInit
方法在Native层创建了NativeMessageQueue和Looper,并将Looper设置给当前的线程。这个时候Java层和Native层都有了MessageQueue和Looper。
在Native层的Looper中,创建了一个管道,本质上就是一个文件,一个管道中有读和写两个文件操作符,通过读和写来将消息写入和读取。 -
enqueueMessage 插入消息
boolean enqueueMessage(Message msg, long when) { synchronized (this) { 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 { 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; }
enqueueMessage其实就是构建好msg,并插入到单链表中。
-
next 读取消息并删除消息
Message next() { for (;;) { nativePollOnce(ptr, nextPollTimeoutMillis); } }
next方法是一个无限循环方法,当消息队列中没有消息的时候,next方法会被阻塞在这里,当有新的消息的时候,next方法会最终返回这条消息并从单链表中移除。
Handler
-
构造函数
public Handler(Callback callback, boolean async) { 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; }
- 首先获取Looper对象,如果Looper对象为空,则抛出异常,所以当我们在子线程中创建Handler的时候,首先要通过
Looper.prepare()
来创建子线程的Looper对象。 - 通过Looper获取到MessageQueue
- 首先获取Looper对象,如果Looper对象为空,则抛出异常,所以当我们在子线程中创建Handler的时候,首先要通过
-
发送消息
Handler的发送消息主要是post的方法和send的方法,而他们最终调用的都是:
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); } private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
Handler主要就是向消息队列中插入了一条消息,这个时候MessageQueue就会调用插入的方法来插入消息。
-
读取消息
当Handler发送消息之后,MessageQueue就会通过next方法读取出这个消息,那么Looper的loop就不会被阻塞住,取出消息之后就调用了
msg.target.dispatchMessage(msg)
方法,最终交给Handler的dispatchMessage
来执行。/** * 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); } }
-
检查Message的callback是否为空,不为空则通过
handleCallback
来处理,而callback就是通过post传递过来的Runnable对象。直接调用run方法执行。private static void handleCallback(Message message) { message.callback.run(); }
-
检查mCallback是否为空,不为空则调用
mCallback.handleMessage
方法处理消息。而mCallback
就是我们在创建Handler的时候传递过去的callback。public interface Callback { public boolean handleMessage(Message msg); } public Handler(Callback callback) { this(callback, false); }
-
调用
handleMessage(msg)
处理消息。
-
网友评论