Handler消息机制源码学习记录
在开发中为了避免在主线程执行耗时任务而产生ANR,我们通常会把耗时任务放到子线程中其处理(网络请求,IO操作等),当子线程在处理完某件任务需要更新UI的时候(比如完成网络图片的读取)就要使用到Android中Handler消息传递机制了。
从上面的这一段描述中可以看出,消息机制其实就是不同线程之间的通信。
这里有个疑问就要抛出来了,为什么不能在子线程中更新UI?
Android的UI更新是单线程模型的,因为如果支持多线程更新View的话就会不可避免的产生线程同步和线程安全的问题,解决起来是比较繁琐的,所以Android就直接规定了只能Ui线程也就是Main线程中更新View。
这里记录一下ANR在那些情况下会被触发:
在UI线程中被一个事件阻塞5秒中而不能继续响应下一个事件
BroadcastReceive超过10秒未响应
下面来创建一个Handler的使用实例:
public class MainActivity extends AppCompatActivity {
private TextView textView;
private Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView)findViewById(R.id.textView);
mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
int what = msg.what;
textView.setText("收到从子线程发来的消息了==>"+what);
}
};
new Thread(new Runnable() {
@Override
public void run() {
//模拟执行耗时操作
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message message = mHandler.obtain();
message.what = 1;
mHandler.sendMessage(message);
}
}).start();
}
}
OK!接下来我们就通过上面的这段Handler的示例代码的执行顺序来进行我们的源码学习。
先贴一个生动形象的Handler消息机制原理图以助于大家理解 :
在正式开始学习Handler消息机制前先来一个小小的总结,为什么还没开始就要先总结呢?当然时为了防止在下面源码学习的过程中抓不到侧重点,所以在这里提前介绍一下Handler消息机制所需要依赖的类以及它们的作用:
Handler就不提了,肯定是重中之重
Message: Message对象用来存储我们需要通过Handler发送给Ui线程的数据
MessageQueue: 消息队列,通过Handler发送的Message对象会被放置到MessageQueue中,会以队列的形式提供插入和删除的操作
Looper: 轮询器,它的作用就是不断的轮询MessageQueue,如果有新的消息就交给Handler处理,如果轮询不到新的消息,就会处于阻塞状态
了解了以上这些,下面我们带着着重点开始学习源码:
首先我们线程10行的创建Handler开始。
mHandler = new Handler()
点开Handler的构造方法,让我们看一下在创建Handler的时候它内部做了那些事情
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());
}
}
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;
}
关注第11行代码:
mLooper = Looper.myLooper();
我们点进去看一下 Looper.myLooper()做的事情
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
咦,怎么只调用了一句 sThreadLocal.get() 就获取到Looper对象了呢?我好像没有创建Looper 对象呀!还有 sThreadLocal是什么东东啊?
Ok,下面让我们逐一解释上面所遇到的困惑:
Looper对象是谁创建的?
主线程(ActivityThread)被创建的时候,Looper对象就跟着被创建了,并调用了Looper.loop()方法开始轮询MessageQueue。因此当我们在主线程中创建Handler的时候是不需要手动调用Looper.prepare()和Looper.loop()方法的因为ActivityThread创建的时候已经被调用过了,但如果是在子线程中创建就一定要调用前面提到的这两个方法,不然会出现下面的错误:
Can't create handler inside thread that has not called Looper.prepare()
说了不算,下面我们来看一下ActivityThread的源码:
(推荐给大家一个可以查看安卓源码的网站:http://grepcode.com)
ActivityThread
public static void main(String[] args) {
SamplingProfilerIntegration.start();
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
EventLogger.setReporter(new EventLoggingReporter());
Security.addProvider(new AndroidKeyStoreProvider());
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");
}
}
可以看到在第13行的时候调用了Looper.prepareMainLooper()方法,下面我们来看一下Looper中的prepareMainLooper()方法:
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的方法中创建Looper对象并通过 sThreadLocal.set(new Looper(quitAllowed))将其设置给了sThreadLocal
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));
}
这就说通了在Handler中调用 mLooper = Looper.myLooper();为什么能拿到Looper对象了,这里需要说明一下ThreadLocal是什么东东:
ThreadLocal使用来解决多线程程序的并发问题,ThreadLocal并不是一个Thread,而是Thread中的一个局部变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每个线程都可独立改变自己的副本,而不会影响其他线程所对应的副本。
ThreadLocal是如何做到为一每一个线程维护变量的副本的呢?其实思路很简单,在ThreadLoacl类中有一个Map,用于存储每一个线程变量的副本,Map中元素的健为线程对象,而值对应线程变量的副本。
通过ThreadLocal的set方法存放在线程中的Looper对象是当前线程自己使用的对象,其他线程是不需要访问的,当然也访问不到。
在前面的Handler的构造方法中不知大伙有没有注意到这一句:
mQueue = mLooper.mQueue;
(⊙o⊙)? Looper中持有MessageQueue的引用,那么MessageQueue这个对象是在什么时候被创建的呢,我们跟着上面的步伐继续看一下Looper的构造方法:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
好了,是不是很清楚了!MessageQueue对象是在Looper被创建的时候就也跟着被创建了。
看到这里Handler = new Handler()这一句代码所涉及到的源码已经分析的差不多了。
我们下面接着分析,线程中进行通过Handler发送消息的这几行代码:
获取一个Message对象:
Message message = mHandler.obtain();
下面通过一张图来看一下mHandler.obtain()都做了那些事情:
接着到了 mHandler.sendMessage(message):
sendMessage又做了什么呢:
(这里忽略了两个方法)
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);
}
在这里又调用了一次msg.target = this进行Message和Handler的绑定,因为通过new Message()创建Message的时候其内部就不会进行绑定(建议使用Handler.obtain()的方式创建Message对象),此处就是为了防止出现前面所提的到的情况,以确保万无一失。
最后调用enqueueMessage方法将消息添加到消息队列中去
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;
}
在前面我们知道ActivityThread中已经调用过Looper.loop()进行不断的从MessageQueue中轮询消息,只不过在队列为空的时候会处于阻塞状态,那么loop()轮询到消息之后是怎么把Message交给Handler处理的呢?
下面我们从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
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
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();
}
}
在代码走到第14行的时候有可能会被阻塞住,继续往下看到第32行,当从消息队列中获取到Message对象时调用了msg.target.dispatchMessage(msg)方法,而msg.target就是前面代码中通过Handler.obtain()获取Message对象时和handler.sendMessage()内部Message和Handler的绑定,绑定之后Message内部就持有了能够处理当前Message的Handler的引用,这样Message就能找到处理事件的Handler并将消息回调给Handler进行处理。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
到这里Handler的消息机制的源码就看的差不多了,下面进行一下总结:
1.首先Looper.prepare()在当前调用的线程中使用ThreadLocal保存一个Looper实例,然后该实例持有一个个MessageQueue对象,Looper.prepare()方法只能调用一次,所以MessageQueue在一个线程中只会有一个存在。
2.Looper.loop()方法会不端地从当前线程中的MessageQueue的实例中轮询消息,然后进行回调msg.target.dispatchMessage(msg)方法,message之所以能够将消息交给指定的Handler处理,是因为内部进行了message对象和Handler之间的绑定。
3.Handler的构造方法,也就是当我们new一个Handler的时候Handler会依次从当前的线程中取出已经保存在ThreadLocal中的Looper对象,并从Looper对象中取出MessageQueue。
4.Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。这里也进行了一次Message对象和当前发送消息的Handler之间的绑定。当使用new Message()获取Message的时候,上面提到的在Handler.obtian()方法中的绑定就没办法执行到了,因此需要在发送消息的时候再进行一次的绑定,以确保万无一失。
5.为什么我们可以不调用Looper.prepare()和Looper.loop()方法就能直接使用呢?原因是再程序启动的时候在ActiivtyThread的main方法中就已经帮我们调用过这两个方法了,因此可以直接使用,如果在子线程中使用的话,还是需要老老实实的调用上面的那两个方法了。
6.是Message指定的到底应该是那个Handler来处理消息,依赖于前面所提到的绑定
7.其实Handler不仅可以更新UI,你完全可以在一个子线程中去创建一个Handler,然后使用这个handler实例在任何其他线程中发送消息,最终处理消息的代码都会在你创建Handler实例的线程中运行。