概述 Handler机制又称为Android中的消息机制,这是Android中的重点,是最常用的线程间通信的方式。本blog首先介绍android中为什么要提供消息机制,然后以分析原码的形式讲解消息机制中重要的类及类中重要的方法,再讲解各个类之间的调用关系,最后对Handler的执行机制进行总结。
Android中的消息机制
一,Android中为什么要提供消息机制 我们都知道在android中有两个规定: 1. 网络请求操作要放到子线程。 2. 不能在子线程更新UI。所以Android中必须有线程间的通讯机制。那么为什么要有这两个规定呢?
耗时操作为什么要放在子线程中?
答:如果在主线程一直执行代码,那么界面就会卡顿,为了提高用户体验,android增加了ANR机制,即界面长时间没有响应就会出现ANR。我们在开发中既为了避免界面卡顿,又避免出现ANR所以把耗时的操作放在子线程中。
为什么不能在子线程中更新UI?
控件是线程不安全的,如果不同线程同时修改UI控件将产生异常。
为什么不给控件加锁呢?
加锁确实是一个解决的办法,但是加锁会让逻辑变得复杂,降低UI绘制的效率。总体来说不如添加Handler机制处理效率高。
二,Android消息机制概述 Android的消息机制主要是指Handler的运行机制以及Handler所附带的MessageQueue和Looper的工作过程,这三者是一个整体。
MessageQueue的中文翻译是消息队列,作用是存储消息,里面是以单链表的结构进行存储。
Looper的中文翻译是循环,这里叫消息循环,MessageQueue的作用是存储消息,looper的作用就是从messageQueue里面取出新消息,然后交由handler进行处理。
此外还有两个重要的类,Message和ThreadLocal。Message用来携带数据。ThreadLocal保证线程内数据安全。
下面开始看各个类的原码,这里仅贴出重要方法的重要方法体,想更详细了解原码的小伙伴可以自行查看原码。
ThreadLocal类 这是一个存储数据的类,提供了set和get方法,一个ThreadLocal对象只能存储一个对象。这个类的最大特点是可以指定线程存取数据,即:在一个线程中使用set方法存储一个数据,只有在该线程中使用get方法才能取出来,在其他线程中使用get方法取不出的,这就保证了线程间数据的安全。
一,set方法 set方法的核心原码如下:
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
分析:在调用ThreadLocal的set方法存储数据时,首先根据当前线程得到一个Values对象,如果得不到就去创建一个Values对象,Values中储存的是键值对,键时当前的ThreadLocal对象,值就是我们存储在ThreadLocal对象中的值。
二,get方法 get方法的核心原码如下:
public T get() {
// Optimized for the fast path.
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {
return (T) table[index + 1];
}
} else {
values = initializeValues(currentThread);
}return (T) values.getAfterMiss(this);
}
分析:在调用ThreadLocal的get方法取出对象时,首先也是得到Values对象,如果是同一个线程,则此时一定可以通过values(currentThread)获取到values对象,此时就可以根据某些算法取出通过set方法存进去的对象。
三,ThreadLocal类在Handler机制中的作用 这里先简单说明一下,如果不懂可以跳过去,等看完整篇之后回头再看。
在Handler机制中,需要保证UI线程中只有一个MessageQueue对象和一个Looper对象,系统在app启动时给我们创建了一个Looper对象,并保存到ThreadLocal中,在创建Handler对象时需要得到Looper对象(这个可以看Handler的构造方法),此时我们要保证得到的是UI线程中的Looper对象,就需要使用ThreadLocal。
Message类 Message作用是封装数据,在Handler的使用中已经用到,下面详细介绍一下它几个重要的字段和方法。
一,Message类中重要的字段 Message类中重要的字段如下:
Arg1和arg2可以直接传递int数据。
callBack:是一个回调方法,这个方法在UI线程被调用。
data:是bundle类型,里面可以封装很多数据。
next:是为了消息池而定义的,执行消息池中的下一个Message对象。
obj:传递数据时可以给Message绑定一个对象,比如String,Bitmap,Student,等等。
sPool:维护的消息池。
target:这个是很重要的字段,一个app只有一个主线程,一个线程中只有一个MessageQueue对象和一个Looper对象,不同Handler发送过来的消息怎么对应给该Handler进行处理,靠的就是这个字段。Handler在发送消息时给Message的target赋值。即可以根据Message找到对应的Handler.
What:同一个handler可以发送很多Message,通过what来区分是哪一条消息。
when:这个是系统使用的字段。我们在得到Message对象后不能发送两次,因为系统处理后Message的when设为0,再次遇到0的Message将不处理。
二,Message类中的对象回收机制 在获取Message对象时使用的方法是:Message.obtain()。下面看下这个方法的原码:
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();
}
由原码可知,在Message类中维护了一个消息池,当消息池中有Message对象时就拿过来使用,当没有Message对象时就创建对象。其实Message有一个回收机制,当Message消息被Handler执行过后就会被回收,放到消息池中,等待下一次使用,避免了对象不停的创建和销毁。Message的回收机制主要方法是:recycle()方法和recycleUnchecked()方法,这两个方法只是Message中消息回收机制中的方法,对Handler运行机制没有作用,这儿不做介绍,感兴趣的小伙伴可以自己去看原码。
Handler类 在handler的使用方法中已经接触到了handler类的几个方法,下面对重要方法做具体分析。
一,Handler的构造方法 在创建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();
//得到Looper对象
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
//得到MessageQueue对象
mCallback = callback;
mAsynchronous = async;
}
这段代码中有两个重要之处,就是得到了Looper对象和MessageQueue对象,这两个对象就是主线程中唯一的Looper对象和MessageQueue对象。
二,enqueueMessage方法 在发送消息时使用的是sendMessage方法,但这个方法没有具体的逻辑代码,它和其他发送消息的方法一样,最终调用的是:enqueueMessage方法,下面看enqueueMessage方法的原码。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
这个方法其实也特别简单,就两个操作,一个是将this赋值给Message的target字段。另外一个就是调用MessageQueue的enqueueMessage方法,将Message放入到消息队列中。
三,dispatchMessage方法 这个方法的作用是分发消息,原码如下:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
注:这个方法在Looper的loop方法中被调用,因此执行了我们重写了handleMessage方法。
MessageQueue类 MessageQueue的中文翻译是消息队列,作用是存储消息对象,里面是以单链表的结构进行存储。
MessageQueue的作用有两个,插入消息,取出并删除消息。
插入消息对应的方法是:enqueueMessage
取出并删除消息对应的方法是:next
一,enqueueMessage方法 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) {
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;
msg.next = p;
// invariant: p == prev.next
prev.next = msg;
}
}
return true;
}
分析:这个方法的作用是存储Message对象,方法中首先做了一个判断:这个Message对象必须有target字段,即必须带有handler对象。剩下的操作就是根据时间对Message进行排序,设置message的next指向。
注:这个方法是在handler中的sendMessage方法中被调用的。
二,next方法 next方法的核心代码是:
Message next() {
for (;
;
) {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) {
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 (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}}
}
分析:这个方法的原码比较多,作用就是返回一个Message对象,并把该Message对象从消息队列中清除。
这个方法中维护了一个死循环,只有当有msg对象不等于null时才有返回值,否则会一直循环,UI线程的代码就停留在了这儿。
Looper类 Looper的中文翻译是循环,这里叫消息循环。MessageQueue的作用是存储消息,looper的作用就是从messageQueue里面取出新消息,然后交由handler进行处理。
一,prepareMainLooper()方法 这个方法的作用是创建UI线程中的Looper对象。原码如下:
public static void prepareMainLooper() {
prepare(false);
//创建Looper对象。
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
//得到Looper对象。
}
}
这段代码中调用了两个非常重要的方法,一个是prepare,这个方法中创建了Looper对象,并把Looper对象保存到ThreadLocal中。另外一个是myLooper方法,这个方法的作用是取出保存在ThreadLocal中的Looper对象。
二,Looper.loop()方法 这个方法是重重之重,它好比司令部,对整个消息机制的运行起支配作用。
方法的原码:
public static void loop() {
final Looper me = myLooper();
//1,获取Looper对象final MessageQueue queue = me.mQueue;
for (;
;
) {//开启一个死循环
Message msg = queue.next();
// 从消息队列中取出消息
if (msg == null) {return;
}Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}msg.target.dispatchMessage(msg);
//调用handler的dispatchMessage方法if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
msg.recycleUnchecked();
//回收Message对象
}
}
总结来说,这个方法中做了四件大事:
- 得到UI线程内的looper对象和MessageQueue对象。
- 开启一个死循环,在死循环中不断的从MessageQueue中取消息。
- 取出消息后调用handler的dispatchMessage方法分发消息。
- 回收Message对象。
Handler机制中各方法的调用关系 Handler机制中的方法按执行的时机不同可以分为三块,一是app启动时的准备工作,二是当我们调用handler的sendMessage时发送消息,三是处理消息。
一,app启动时的准备工作 一个app启动时会调用ActivityThread类中的main方法。这个是非常重要的方法,在这里面做了很多事情,下面只看与Handler机制有关的代码:
public static void main(String[] args) {
SamplingProfilerIntegration.start();
//。。。
Looper.prepareMainLooper();
// 创建ActivityThread实例
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}AsyncTask.init();
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
这个方法中调用了两个重要的 方法:
Looper. prepareMainLooper()和Looper.loop();
Looper. prepareMainLooper()的作用是:创建Looper对象和MessageQueue对象。
Looper.loop()的方法是开启死循环。代码会停留在儿,等待消息队列中的消息。
二,发送消息 发送消息使用的是Handler的sendMessage方法,最终调用的是MessageQueue的enqueueMessage方法,把消息放入消息队列。
三,处理消息 当消息队列有有消息时,Looper的loop方法就会执行,调用queue.next()得到Message对象,然后再调用handler的HandleMessage方法处理消息。
总结 【Handler的运行机制】看到这儿应该对Handler的执行机制都明白了,这儿再做一个总结:
在app启动时首先执行的是ActivityThread类中的main方法,这个方法中主要做两件事情,一是调用looper的prepare方法创建looper对象和Messagequeue对象。二是调用looper的loop方法,开启一个死循环不断的从消息队列中获取消息。我们使用Handler首先在主线程创建一个handler对象,并重写handlemessage方法。在子线程中使用sendMessage将消息发送到消息队列中,此时looper的loop方法就可以从消息队列中拿出消息,然后交由handler对象进行处理,然后就执行了我们重写的handlemessage方法。