Android Handler(线程间通信)

1.Handler机制
Handler是Android提供的用于发送、接收和处理消息或Runnable对象的类,它结合Message、MessageQueue和Looper类以及当前线程实现了一个消息循环机制,用于实现消息的异步加载和处理。
整个异步消息处理流程:
在这里插入图片描述
①Message:消息体,用于装载需要发送的对象。
一般不直接new Message得到实例对象,而是使用Message.obtain()或Handler.obtainMessage()获取。Message.obtain()会从消息池中获取一个Message对象,如果消息池中是空的才会new一个新Message,这样有利于消息资源的利用。
②Handler:直接继承自Object,一般是在子线程中发送Message或Runnable对象到消息队列中;并在UI线程中接收、处理从消息队列分发出来的Message对象。
③MessageQueue:消息队列,内部使用的是单链表结构(插入和删除效率比较高)。它由对应的Looper对象创建,并由Looper对象管理。
每个线程只会有一个消息队列。
④Looper:不断从消息队列中取出消息进行分发。调用Looper.loop()方法后,就会进入到一个无限循环中,每当发现消息队列中存在一条消息,就将它取出并调用Handler的handleMessage()方法。
每个线程只会有一个Looper对象。

2.Message
Message的构造方法为无参构造方法,且只有一个构造方法:public Message() { }
除了构造方法可以创建实例对象外,还可以通过内部的静态方法来创建:
static Message obtain()
static Message obtain(Message orig)
static Message obtain(Handler h)
static Message obtain(Handler h, Runnable callback)
static Message obtain(Handler h, int what)
static Message obtain(Handler h, int what, Object obj)
static Message obtain(Handler h, int what, int arg1, int arg2)
static Message obtain(Handler h, int what, int arg1, int arg2, Object obj)
静态方法里面都会首先调用第一个方法来创建一个Message对象。
public final class Message implements Parcelable {
public static final Object sPoolSync = new Object();
private static Message sPool; //对象池
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
public static Message obtain() {
//内部维护了一个Message对象池用于Message对象的复用,使用obtain()可直接从池内获取Message对象,避免重新分配
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flag = 0; //clear in-use flag
sPoolSize --;
return m;
}
}
//若池内无消息,则还是用new创建
return new Message();
}
}
如果消息池内有消息,则返回第一个消息实例。所以一般使用obtain()获得一个Message对象,可以避免每次都使用new重新分配内存,消耗更多的内存资源。

对于其他static obtain()的重载方法,通过源码可以发现,都是进行赋值操作。注意obtain( Handler h, Runnable callback)这个静态方法,因为涉及到Message的两个重要成员变量:
public final class Message implements Parcelable {
Handler target;
Runnable callback;
public static Message obtain(Handler h, Runnable callback) {
Message m = obtain();
m.target = h;
m.callback = callback;
return m;
}
}
也是赋值操作,其中target指向Message所关联的Handler。除了target还有一个callback,这个callback也是配合着Handler来发挥作用的。

总结Message:
①Message有8个静态方法可以创建Message实例;
②Message有两个重要的成员变量,分别为target 和callback,一个是Handler,一个是Runnable;
③Message有4个公开变量what、arg1、arg2、obj,可以存储消息进行传递;
④Message的对象池是一个链表,所以还有一个next成员变量,也是Message类型。

3.Handler
Handler在消息传递过程中扮演着重要的角色,是消息的主要处理者。Handler的主要作用就是发送、接收并处理消息。
①Handler的构造方法:
Handler()
Handler(Callback callback)
Handler(boolean async)
Handler(Callback callback, boolean async)
Handler(Looper looper)
Handler(Looper looper, Callback callback)
Handler(Looper looper, Callback callback, boolean async)
这些构造方法都是相继往下调用的,所以直接看最后一个方法:
public class Handler {
final Looper mLooper;
final MessageQueue mQueue;
final Callback mCallBack;
final boolean mAsynchronous;
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
}
Handler的这些构造方法里,如果looper不传值则会使用Looper.mylooper()获取当前线程的Looper实例;如果传值则使用传进来的looper对象。一般在子线程中使用Handler时才传此参数。

再看另外一个构造方法:
public class Handler {
public Handler(Callback callback,boolean async){
mLooper = Looper.myLooper(); //参数中没有传递Looper,则使用当前线程的Looper对象
if (mLooper == null) {
throw new RuntimeException(“Can’t create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()”);
}
mQueue = mLooper.mQueue; //mQueue为Looper对象对应的消息队列
mCallback = callback; //接口回调
mAsynchronous = async; //是否为异步消息
}

public interface Callback {
public boolean handleMessage(Message msg);
}
}
首先获取当前Handler实例所在线程的Looper对象。如果Looper不为空就获取Looper对应的消息队列,赋值给Handler的成员变量mQueue,然后设置Callback用于处理消息回调。

Handler会把Message发送到消息队列,然后排队等待,Looper从消息队列将消息一个一个的取出来,然后通知消息对应的Handler去分发处理消息。

总结Handler:
(1)Handler有且只能绑定一个线程的Looper;
(2)Handler的消息发送给Looper的消息队列MessageQueue中,需要等待处理。

在一个线程中可以创建多个handler对象,这些handler都往这个线程的Looper中塞消息,Looper按照顺序去轮询到消息,进行处理。
比如在线程A中,可以使用线程B中handler发送消息到线程B,线程B的handler意思是其Looper是在线程B中轮询的。这个handler就像是线程B放出去的口子,谁向线程B发消息,就通过我来塞。

如果在子线程中声明一个Handler是不能直接更新UI的,需要调用Handler相关的构造方法,传入主线程的Looper,这样创建的Handler实例,才能进行UI的更新操作。
注意,子线程默认是没有开启Looper的,所以在子线程中创建Handler之前,必须先开启子线程的Looper,否则就会报异常:
if (mLooper == null) {
throw new RuntimeException(“Can’t create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()”);
}
创建了Handler对象,接下来传递消息就需要用到Handler的sendMessage()方法了。

②Handler sendMessage()相关方法
在这里插入图片描述
当调用Handler发送消息时,最终都会调用到sendMessageAtTime()方法,然后调用enqueueMessage()把消息发送到消息队列。

public class Handler {
public boolean sendMessageAtTime( Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) { //若在创建Handler时没有指定Looper,就不会有对应的消息队列queue ,自然就会为null
RuntimeException e = new RuntimeException(this + “sendMessageAtTime() called with no mQueue”);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}

private boolean enqueueMessage( MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; //target就是当前的handler
if (mAsynchronous) { //是否为异步消息
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
}
msg.target = this;在发送消息到消息队列之前,明确的指定了消息的target为当前的Handler,以便于在后面Looper分发消息时用到。
queue.enqueueMessage(msg, uptimeMillis);调用了消息队列的enqueueMessage()方法来完成消息的发送。

③Handler dispatchMessage()方法
消息发送到消息队列后会交给Looper等待处理,Looper处理完后再通知对应的Handler处理,那么是怎样通知Handler处理消息的呢?秘密就在dispatchMessage()这个方法中。

public class Handler{
public void dispatchMessage(Message msg){
if (msg.callback != null) { //msg.callback不为空,表示使用了post(Runnable r)发送消息,则执行handleCallback( msg),即回调Runnable对象里重写的run()方法
handleCallback(msg);
} else { //msg.callback属性为空,表示使用了sendMessage(Message msg)发送消息,则执行handleMessage(msg),即回调重写的handleMessage(msg)方法
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}

//子类必须实现这个来接收消息
public void handleMessage(Message msg) {
}
}
当Looper处理完Message后会使用到Message的target属性,即发送消息的那个Handler。Looper会调用Handler的dispatchMessage()方法分发消息,所以前面在enqueueMessage()发送消息时需要指明Message的target就是这个道理。

回到dispatchMessage()这个方法:
①首先判断Message的callback是否为空,此处的callback就是创建Message时指定的callback属性(runnable对象)。若callback不为空,则将结果回调到callback中。
②若Handler的mCallback(Callback对象)不为空,也一样的道理。
③平时使用中都没有传入这个callback,而是直接实现handleMessage()这个方法,在这个方法中处理更新UI任务。

以上就是Handler发送和接收消息的基本过程:把消息发送到消息队列—>等待—>接收消息—>分发消息—>在回调中处理。

4.MessageQueue
MessageQueue不仅是逻辑的中心,并且是与native交互的类,一些比较重要的逻辑,例如阻塞与唤醒,都是由native实现的。

MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
//通过native方法初始化消息队列,其中mPtr是供native代码使用
mPtr = nativeInit();
}
在创建Looper()时会创建MessageQueue,这里直接调用了native方法,返回的mPtr参数是调用其他native方法时需要的参数。

整个handler工作原理的逻辑中心就是enqueueMessage()方法以及MessageQueue的next()方法。在MessageQueue中维护了一个Message链表,enqueueMessage()方法就是将Message按时间顺序排序插入到链表的合适位置,next()方法负责从链表中取出Message处理。

Handler发送消息时最终会调用MessageQueue的enqueueMessage()方法。
public final class MessageQueue{
boolean enqueueMessage(Message msg, long when) {
if(msg.target = = null){ //每个普通msg必须有一个target,除了屏障消息
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) { //正在退出时,回收msg,加入到消息池
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages; //队列中第一个Message
boolean needWake;
if (p = = null || when = = 0 || when < p.when) { //p为null代表链表为空,新进来的msg作为链表第一个元素;msg的触发时间是队列中最早的,则插入到p的前边,并作为mMessages
msg.next = p;
mMessages = msg;
needWake = mBlocked;//当阻塞时需要唤醒(如果当前是阻塞的,这时有消息插入进来了,要唤醒线程处理这个消息)
} else {
//将消息按时间顺序插入到MessageQueue ,一般不需要唤醒事件队列因为插入的并不是需要立马执行的消息,除非这个消息是同步屏障消息
needWake = mBlocked && p.target==null && msg.isAsynchronous();
Message prev;
for ( ; ; ) { //for循环是为了取出一个空的或者when比当前Message长的一个消息,然后进行插入(在队列中间插入)
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
//如果是同步屏障消息,则不唤醒,等着,等异步消息插进来
if(needWake && p.isAsynchronous()){
needWake = false;
}
}
//将msg插入到p的前边
msg.next = p;
prev.next = msg;
}
//消息没有退出,认为此时mPtr!=0,如果需要唤醒则唤醒
if(needWake) {
nativeWake(mPtr);
}
}
return true;
}
}
这个方法负责将Message插入到链表中合适的位置,并判断是否需要唤醒。

有了插入消息的方法,就有对应的取出消息的方法供Looper取出消息处理,它就是MessageQueue的next()方法。next()是一个无限循环的方法,如果消息队列中没有消息,next()会一直阻塞在这里。当有新消息到来时,next()会从中取出消息返回给Looper处理,并将其从消息列表中移除。
Looper取消息的过程:如果队列中有消息,就判断队头消息的执行时间是否大于当前时间,如果大于就调用nativePollOnce阻塞一段时间(队头消息的执行时间-当前时间),然后取出队头消息进行执行,否则就立即取出队头消息进行执行;如果队列中没有消息就一直阻塞,直到下一个消息来到才唤醒取消息的线程继续上述循环。
public final class MessageQueue{
Message next() {
final long ptr = mPtr;
if(mPtr 0) { //ptr是调用native方法进行阻塞或唤醒的关键参数,退出时会清空。当消息循环已经退出,则直接返回
return null;
}
int nextPollTimeoutMillis = 0;//阻塞时间
for (;😉 {
//native阻塞操作,等待指定的时间或者消息队列被唤醒,都会返回
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg=mMessages;//队头消息
//当前队头消息是同步屏障消息,则查询后面的异步消息
if (msg != null && msg.target
null) { //当查询到异步消息,则立刻退出循环,查询链表
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
//msg是正常消息或者经过上边的while循环查询到了后面的异步消息,则进行执行逻辑
if (msg != null) {
if (now < msg.when) {
// 当异步消息触发时间大于当前时间,说明还没到,则计算下一次需要阻塞的时间
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//将该消息从链表中删除
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;//从链表中删除
} else {
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();//设置消息的使用状态,即flags |= FLAG_IN_USE
return msg; //成功地获取消息队列中的下一条即将要执行的消息,交给Looper处理
}
} else {
//没有消息,有两种情况:mMessage本来就是null或者mMessage是同步屏障消息,但是遍历整个链表没有找到异步消息,则无限期阻塞,直到有地方唤醒
nextPollTimeoutMillis = -1;
}
}
//当调用一个空闲handler时,一个新message能够被分发,因此无需等待可以直接查询pending message.
nextPollTimeoutMillis = 0;
}
}
}
nativePollOnce(ptr, nextPollTimeoutMillis)是一个native方法,实际作用就是通过Native层的MessageQueue阻塞nextPollTimeoutMillis毫秒的时间。
如果nextPollTimeoutMillis = -1,则一直阻塞不会超时;如果nextPollTimeoutMillis = 0,则不会阻塞,立即返回;如果nextPollTimeoutMillis > 0,最长阻塞nextPollTimeoutMillis毫秒(超时),如果期间有程序唤醒会立即返回。

这里涉及到Linux的epoll机制,简单说就是在主线程的MessageQueue中没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。

MessageQueue还有一个退出方法quit():
public final class MessageQueue{
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException(“Main thread not allowed to quit.”);
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();//移除尚未处理的消息
} else {
removeAllMessagesLocked();//移除所有消息
}
nativeWake(mPtr);
}
}

private void removeAllMessagesLocked() {
Message p = mMessages; //队头消息
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}

private void removeAllFutureMessagesLocked( ){
final long now = SystemClock.uptimeMillis();
Message p = mMessages; //队头消息
if (p != null) {
if (p.when > now) { //队列中第一个消息的when比当前时间都大,则表明队列中所有消息都尚未处理,移除全部
removeAllMessagesLocked();
} else { // 正在处理的消息不做处理
Message n;
for ( ; ; ) {
n = p.next;
if (n == null) {
return;
}
if (n.when > now) {
break;
}
p = n;
}
p.next = null;
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}
}
当safe为true时,只移除尚未触发的所有消息,对于正在处理的消息不做处理;当safe为false时移除所有消息。

5.Looper
Looper负责不断的从消息队列中取出消息,然后分发处理消息。每个线程都可以且只能绑定一个Looper。
主线程之所以能处理消息是因为在APP启动时,ActivityThread中的main()方法中就已经启动了Looper循环。

Looper有两个重要的方法:prepare()和loop()。
①Looper.prepare()
Looper的构造方法:
public final class Looper {
final MessageQueue mQueue;
private Thread mThread; //记录当前线程
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue( quitAllowed);//创建消息队列,也就是说在创建Looper实例时会自动创建一个与之配对的消息队列
mThread = Thread.currentThread();
}
}
Looper在创建时会同时新建一个与之对应的消息队列MessageQueue。

Looper构造方法的调用是在prepare()里,接下来看prepare()方法,通过prepare()可以为Handler创建一个Lopper:
public final class Looper {
static final ThreadLocal< Looper> sThreadLocal = new ThreadLocal< Looper>();
private static Looper sMainLooper;

public static void prepare() {
prepare(true);
}

//为当前线程创建looper对象,同时也生成一个MessageQueue对象。若在子线程中需要手动调用该方法
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) { //首先判断sThreadLocal是否为空,不为空就抛出异常,即Looper.prepare()不能被调用两次。因此一个线程只能有一个looper实例
throw new RuntimeException(“Only one Looper may be created per thread”);
}
sThreadLocal.set(new Looper(quitAllowed)); //初次调用prepare()则创建Looper对象,并存放在ThreadLocal变量中(Looper对象是存放在Thread线程中的)
}

//为主线程创建Looper对象,同时生成一个消息队列。该方法在主线程创建时自动调用,即主线程的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对象使用ThreadLocal保存。
ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据。数据存储以后,只有在指定线程中可以获取到存储的数据,其他线程则无法获取,这样就保证了一个线程对应一个Looper。从源码中也可以看出一个线程只能有一个Looper,否则就会抛出异常。

prepareMainLooper()是系统在ActivityThread中调用的:
public static void main(String[] args) {

Looper.prepareMainLooper(); //为主线程创建一个Looper对象,同时生成一个消息队列对象
ActivityThread thread = new ActivityThread();//创建主线程
thread.attach(false);

if (sMainThreadHandler==null) {
sMainThreadHandler = thread.getHandler();
}

Looper.loop();//自动开启消息循环
}
系统在创建时会自动创建Looper来处理消息,所以在主线程中使用Handler时不需要手动调用Looper.prepare(),这样Handler就默认和主线程的Looper绑定。
当Handler绑定的Looper是主线程的Looper时,该Handler可以在其handleMessage中更新UI,否则更新UI则会抛出异常。

在开发中,可能在多个地方使用Handler,所以可以得出一个结论:一个Looper可以和多个Handler绑定,那么Looper是怎么区分Message由哪个Handler处理呢?这里就需要看loop()方法了。

②Looper.loop()
public final class Looper {
public static void loop() {
final Looper me = myLooper(); //获得当前线程的Looper对象。myLooper()用于返回sThreadLocal里存储的Looper实例,若me为null则抛出异常。所以loop()执行前必须执行prepare()创建一个Looper实例
if (me==null) {
throw new RuntimeException(“No Looper; Looper.prepare() wasn’t called on this thread.”);
}
final MessageQueue queue = me.mQueue;//当前Looper对应的消息队列
for ( ; ; ) {
Message msg = queue.next(); //从消息队列中取出消息(可能会阻塞)
if (msg == null) { //消息队列中没有消息则退出循环
return;
}
try {
msg.target.dispatchMessage( msg);//分发消息到对应的handler。把消息派发给它的target属性,target就是msg对应的handler
}
msg.recycleUnchecked(); //将Message放入消息池中进行回收
}
}
}
public static Looper myLooper(){
return sThreadLocal.get();
}
首先获取当前Looper,如果没有则抛异常,有则获取它的消息队列MessageQueue。所以在子线程中使用Handler时必须手动调用Looper的prepare()和loop()方法。

loop方法中是个死循环,不断从MessageQueue()中读取next()消息,这个过程可能会阻塞,阻塞的原理在MessqgeQueue.next()中。总体来看loop()的逻辑很简单:next()取消息,取不到就阻塞;取到了就将分发逻辑转到message.target(handler)上;取出来是空,就结束轮询。

在子线程中使用Handler:
class LooperThread extends Thread {
public Handler mHandler;

public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// 处理消息
}
};
Looper.loop();
}
}

获取到MessageQueue后开启消息循环,不断从MessageQueue中读取消息,队列中有消息则调msg.target.dispatchMessage(msg)分发处理消息;没有消息则阻塞,等待消息。
所以Looper不需要考虑怎么区分Message由哪个Handler处理,只负责开启消息循环接收并处理消息。处理完消息后调用msg.recycleUnchecked()回收消息。
开启消息循环后,是可以停止的。Looper提供了quit()和quitSafely()来退出。
public void quit() {
mQueue.quit(false);
}

public void quitSafely() {
mQueue.quit(true);
}
实际上调用的是MessageQueue中的退出方法。调用quit()会直接退出Looper,而quitSafely()只是设定一个退出标记,然后把消息队列中的已有消息处理完毕后才安全地退出。在Looper退出后,通过Handler发送消息会失败。如果在子线程中手动创建了Looper,则应在处理完消息后退出Looper终止消息循环。
注意:主线程的消息循环不允许退出,即无限循环。子线程的消息循环允许退出,调用消息队列的MessageQueue的quit()即可。

总结下Looper:
①被创建时与线程绑定,保证一个线程只会有一个Looper实例 ,并且一个Looper实例只有一个MessageQueue。
②创建后,调用loop()开启消息循环,不断从MessageQueue中取出Message,然后交给Message所属的Handler去处理,也就是msg.target属性。
③处理完消息后,调用msg.recycleUnchecked回收消息。

6.四大概念的联系
Handler机制正是通过这四大概念相互关联形成的,简单概括为:
Handler将Message发送到Looper的消息队列MessageQueue中,等待Looper循环读取并处理Message,然后调用Message的target,即附属的Handler的dispatchMessage()方法,将该消息回调到handleMessage()方法中,然后完成更新UI操作。

首先要明白的是,Handler和Looper对象是属于线程内部的数据,不过也提供了与外部线程的访问接口,Handler就是公开给外部线程的接口,用于线程间的通信。Looper是由系统支持的用于创建和管理MessageQueue的依附于一个线程的循环处理对象,而Handler是用于操作线程内部消息队列的,所以Handler也必须依附一个线程,而且只能是一个线程。

对异步消息处理的整个流程梳理一遍:
当应用程序开启时,系统会自动为UI线程创建一个Looper和MessageQueue循环处理Message。首先需要在主线程中创建一个Handler对象,并重写handlerMessage()方法。当子线程中需要进行UI操作时,就创建一个Message对象并通过Handler将这条Message发送出去。之后这条消息就会被添加到MessageQueue消息队列中等待被处理,而Looper则会一直尝试从MessageQueue中取出待处理消息,并找到与消息对象对应的Handler,然后调用Handler的handleMessage()方法。由于Handler是在主线程中创建的,所以此时handleMessage()方法中的代码也会在主线程中运行,于是在这里就可以安心地进行UI操作了。

7.Handler类的两个主要用途
①执行定时任务(post)
指定任务时间,在某个具体时间或某个时间段后执行特定的任务操作,例如使用Handler提供的postDelayed(Runnable r,long delayMillis)方法指定在多久后执行某项操作,比如当当、淘宝、京东和微信等手机客户端的开启界面功能,都是通过Handler定时任务来完成的。
②线程间的通信(sendMessage)
在执行较为耗时的操作时,Handler负责将子线程中执行操作的结果传递到UI线程,然后UI线程再根据传递过来的结果进行相关UI元素的更新。

8.执行定时任务(post)
Handler的post()方法会传递一个Runnable对象到消息队列中,在这个Runnable对象中重写run()方法,一般在这个run()方法中写入需要在UI线程上进行的操作。
post()方法允许把一个Runnable对象入队到消息队列中。它的方法有:
①boolean post(Runnable r):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后立即执行。
②boolean postAtTime(Runnable r,long uptimeMillis):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,在特定的时间执行。
③boolean postDelayed(Runnable r,long delayMillis):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,延迟delayMills秒后执行。
④void removeCallbacks(Runnable r):从消息队列中移除一个Runnable对象。

举例说明如何通过Handler的post方式在新启动的线程中更新UI:
public class MainActivity extends Activity {
private static Handler handler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnMes1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {//子线程
@Override
public void run() {
// 使用post方式更新UI
handler.post(new Runnable() {
@Override
public void run() {
tvMessage.setText(“使用Handler.post在工作线程中发送一段执行到消息队列中,在主线程中执行。”);
}
});
}
}).start();
}
});
}
注意:post()方法中Runnable对象的run()方法是运行在主线程上的(虽然看上去是写在子线程当中的)。这个Runnable对象被放到了消息队列中,然后主线程中的Looper(因为Handler是在主线程中生成的,所以Looper也在主线程中)将这个Runnable对象从消息队列中取出来,取出来之后做了些什么呢?要了解这个过程,只能看源码了。
public class Handler {
public final boolean post(Runnable r) {
return sendMessageDelayed( getPostMessage®, 0);
}

private static Message getPostMessage ( Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
}
post()方法中直接调用了sendMessageDelayed()方法,里面有两个参数,重点是第一个参数。
getPostMessage()方法完成了两个操作:一是生成一个Message对象,二是将runnable赋值给Message的callback属性。最后返回这个Message对象。

现在理解了如何把一个Runnable对象放到消息队列中:实际上是生成了一个Message对象,并将runnable赋值给Message对象的callback属性,然后再将Message对象放置到消息队列当中。

接下来就是Looper.java的dispatchMessage的方法了:msg.target.dispatchMessage(msg);
回顾一下Handler.java的dispatchMessage()方法:
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()方法。打开handleCallback()方法的源码:
private static void handleCallback(Message message) {
message.callback.run();
}
看到这个方法就明白了:message的callback属性直接调用了run()方法。
所以,Looper取出携带有r对象的Message对象以后,调用了dispatchMessage()方法,然后判断Message的callback属性是否为空,此时的callback属性有值,所以执行handleCallback( Message message),在该方法中执行了message.callback.run()。根据Java的线程知识可以知道,如果直接调用Thread对象或者Runnable对象的run()方法,是不会开辟新线程的,而是在原有的线程中执行。 因为Looper是在主线程中,所以dispatchMessage()方法和handleMessage()方法也都是在主线程中运行。所以post()里面的run方法也自然是在主线程中运行的。
使用post()方法的好处在于:避免了在主线程和子线程中将数据传来传去的麻烦。

9.Handler实现线程间通信
①在子线程发送消息,在主线程中接收消息
public class MainActivity extends Activity {
private Handler handler ;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler = new MyHandler() ;
button.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View arg0) {
Thread t = new NetworkThread();
t.start();//开启子线程
}

//在主线程中接收数据,更新UI
class MyHandler extends Handler {
@Override
public void handleMessage(Message msg){
String s = (String)msg.obj ;
textView.setText(s) ;
}
}

//在子线程中发送数据
class NetworkThread extends Thread {
@Override
public void run(){
try {
Thread.sleep(2*1000) ;//模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
String s = “从网络中获取的数据” ;
Message msg = handler.obtainMessage() ;
msg.obj = s ;
handler.sendMessage(msg); //在子线程中发送消息
}
}
}
②在主线程中发送消息,在子线程中接收消息
public class MainActivity extends Activity {
private Handler handler ;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//点击按钮时,在主线程发送Message
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Message msg = handler.obtainMessage() ;
handler.sendMessage(msg) ;
}
}) ;
//开启子线程
WorkerThread wt = new WorkerThread() ;
wt.start() ;
}

//在子线程中生成handler
class WorkerThread extends Thread {
@Override
public void run() {
Looper.prepare() ;//生成Looper对象
//在子线程中生成Handler对象
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.i(“后台输出”, “收到了消息对象”);
}
};
Looper.loop() ; //通过Looper对象将消息取出来
}
}
}
这是主线程发送消息,子线程接收消息的固定写法,分为三个步骤:
(1)准备Looper对象 : Looper.prepare();
(2)在子线程中生成Handler对象
(3)调用Looper.loop()方法之后,Looper对象将不断地从消息队列当中取出消息,然后调用handler的handleMessage()方法处理该消息;如果消息队列中没有消息,则该线程阻塞。
注意,此时handleMessage()方法是在子线程中运行的。

总结一下,首先执行Looper的prepare()方法,这个方法有两个作用:一是生成Looper对象,二是把Looper对象和当前线程对象形成键值对(线程为键)存放在ThreadLocal中,然后生成handler对象,调用Looper的myLooper()方法得到与Handler所在线程对应的Looper对象。这样handler、looper 、消息队列就形成了一一对应的关系,然后执行第三个步骤,即Looper在消息队列当中循环的取数据。

10.总结
①应用启动时会启动主线程ActivityThread,在ActivityThread的main方法中会调用Looper.prepareMainLooper()和Looper.loop()启动Looper。
②Looper启动时会创建一个MessageQueue实例,并且只有一个实例,然后不断从MessageQueue中获取消息,无消息则阻塞等待消息,有则调用。
③msg.target.dispatchMessage(msg) 处理消息。
④在使用Handler时,需要先创建Handler实例,Handler在创建时会获取当前线程关联的Looper实例和Looper中的消息队列MessageQueue。然后在发送消息时会自动设置Message的target属性为Handler本身,并把消息放入MessageQueue中由Looper处理。Looper取出消息后交给Handler重写的handleMessage方法去处理。
⑤如果要在子线程中使用Handler就需要新建Looper实例,传给Handler即可。

我对Handler机制的理解:
Handler声明可以在任意线程(主线程或子线程均可)。Handler实例化的时候,在它的构造方法中会去获取当前handler实例所在线程的Looper对象,赋值给Handler的成员变量mHandler,并把该Looper对象的MessageQueue属性赋值给Handler的成员变量mQueue,这样Handler就获取到了该线程的消息队列。Handler在调用sendMessage方法发送消息时,会调用消息队列的enqueueMessage方法把消息放入消息队列mQueue中,把消息放入消息队列之前,会给该消息的target属性赋值为当前handler。现在消息已经在消息队列里了,然后Looper对象通过loop()方法不断从消息队列里取出消息msg,然后调用msg.target.dispatchMessage()分发消息去处理,而msg.target正是发送这个消息的handler,所以实例调用的是对应handler对象的dispatchMessage()方法,在这个dispatchMessage()方法中回调msg的Runnable接口方法或者handler的handleMessage方法。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值