Handler机制
Handler介绍:
我们都知道Android的主线程不能做耗时任务,否者会导致ANR.但是界面的更新又必须在主线程中进行,这样我们就必须在子线程中处理耗时的任务,然后在主线程中更新UI.
但是,我们怎么知道子线程何时完成任务,又应该什么时候更新UI,更新什么内容,为了解决这个任务,Android为我们提供了一个消息机制即Handler机制.
源码分析:
创建Handler时无参构造函数内调用了两个参数的构造函数,而在两个参数的构造函数中就是将一些变量进行赋值.
通过Looper中的myLooper方法来获得Looper实例的,如果Looper为null的话就会抛无法在未调用Looper.prepare()的线程内创建handler异常
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.myLooper()之前必须先调用Looper.prepare()方法
public static void prepare() {
prepare(true);//prepare()方法调用了prepare(boolean quitAllowed)方法
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//如果Looper为空,实例化了一个Looper,然后将Looper设置进sThreadLocal中
sThreadLocal.set(new Looper(quitAllowed));
}
从上面代码中可以看到,prepare()方法调用了prepare(boolean quitAllowed)方法,实例化了一个Looper,然后将Looper设置进sThreadLocal中,由此可见Looper是唯一的,多次调用Looper.prepare()会报异常
什么是ThreadLocal:
Threadlocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其他线程来说无法获取到数据.
虽然在不同线程中访问的是同一个ThreadLocal对象,但是它们通过ThreadLocal获取到的值却是不一样的.
一般来说,当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候,就可以考虑采用ThreadLocal
知道了ThreadLocal.set()方法的作用,则Looper.prepare()就是将Looper与当前线程进行绑定(当前线程就是调用Looper.prepare方法的线程)
Looper.prepare()方法在主线程中Android系统已经帮我们在ActivityThread类的main方法中调用Looper.prepareMainLooper();从当前线程中的ThreadLocal中取出Looper实例
public static void prepareMainLooper() {
prepare(false);//这里调用了prepare方法
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();//从当前线程中的ThreadLocal中取出Looper实例
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
通过上面的代码,我们可以知道mQueue就是MessageQueue,在我们调用Looper.prepare方法时就将mQueue实例化了,拿到Looper中的mQueue这个成员变量,然后再赋值给Handler中的mQueue
mQueue = mLooper.mQueue;
Handler明明是在子线程发送的消息怎么会跑到主线程中?
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);
}
其实Handler.sendMessage()、sendEmptyMessage等方法最终都是调用sendMessageAtTime(),返回enqueueMessage()方法
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; 就是将当前的Handler对象赋值给了Message中的target变量,从而将调用sendMessage()方法的Handler与message进行绑定.
queue.enqueueMessage(msg,uptimeMillis)就是调用了MessageQueue中的enqueueMessage(),将消息放入到消息队列中,如果消息成功放入消息队列,返回true,失败返回false,而失败的原因通常是因为处理消息队列正在退出.
Handler在sendMessage时会将自己设置给Message的target变量即将自己与发送的消息绑定。Handler的sendMessage是将Message放入MessageQueue中。
怎样从MessageQueue中获取Message
在ActivityThread类main()中,结尾处有Looper.loop();
public static void loop() {
final Looper me = myLooper();//通过myLooper方法拿到与主线程绑定的Looper
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;//从Looper中得到MessageQueue
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;
}
try {
//这句代码是重点
msg.target.dispatchMessage(msg);
Looper.loop();就是拿到用与主线程绑定的Looper对象,从Looper对象中得到MessageQueue,死循环从queue.next()不断取出消息,若队列中无消息,则阻塞等待.
msg.target.dispatchMessage(msg);在msg.target就是发送消息的那个Handler,所以这句代码本质就是调用了Hadler的dispatchMessage()方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
//if中的代码其实是和if (msg.callback != null) {handleCallback(msg);}
//原理差不多的,只不过mCallback是Handler中的成员变量。
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//当上面的条件都不成立时,就会调用这句代码
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
msg.callback就是Runnable对象,当msg.callback不为null时会调用 handleCallback(msg)方法
那什么情况下if (msg.callback != null)这个条件成立呢!就是调用Handler的post方法呀,最后在Runnable的run方法中来处理
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
post方法最终也是将Runnable封装成消息,然后将消息放进MessageQueue中
public void handleMessage(Message msg) {
//它就是一个空方法,具体的代码是我们实例化Handler重写handleMessage()这个方法进行处理的
}
总结
Hanler机制主要是四个类
- Handler :负责发送和处理消息
- Message:用来携带数据
- MessageQueue:消息队列
- Looper:消息泵,负责不断的从MessageQueue中取Message
Handler
- 在实例化Handler的时候要先调用Looper.prepare()
Looper.prepare()方法就是实例化了一个Looper,然后将Looper设置进ThreadLocal中,将Looper与当前线程绑定
但是,在主线程中Android系统已经帮我们调用了Looper.prepare方法可以看下ActivityThread类中的main方法
Looper.prepareMainLooper();这句话的实质就是调用了Looper的prepare方法
- 在实例化Handler的时候,通过Looper.myLooper()获取Looper,然后在获取Looper中的MessageQueue.
Looper.myLooper()就是从ThreadLocal中获取Looper对象,Looper构造中创建MessageQueue
- 在子线程中调用Handler的sendMessage方法时会将自己设置给Message的target变量即将自己与发送的消息绑定,将Message放入MessageQueue中,然后调用Looper.loop()方法来从MessageQueue中取出Message,执行msg.target.dispatchMessage(msg)
Handler的无论时sendMessage、sendEmptyMessage等方法最后调用了sendMessageAtTime()方法,sendMessageAtTime()方法最后返回的是enqueueMessage()
enqueueMessage()方法最后是MessageQueue中的enqueueMessage方法,将消息放进消息队列中,如果消息已成功放入消息队列,则返回true。失败时返回false,而失败的原因通常是因为处理消息队列正在退出。
- 通过Looper.loop()从MessageQueue中取出消息
ActivityThread类中的main方法结尾处,Looper.loop()方法,通过myLooper()方法拿到与主线程绑定的Looper,从Looper中得到MessageQueue,通过next()方法获取Message,没有消息时阻塞
msg.target.dispatchMessage(msg);说明已经从消息队列中拿到了消息,msg.target也就是发送消息的那个Handler,所以这句代码的本质就是调用了Handler中的dispatchMessage(msg)方法
dispatchMessage(msg)中判断是post()还是send(),如果是post()就是调用Runnable中的run()方法,如果是send()及调用handlerMessage(Message msg)方法,handlerMessage()方法是空方法,自己重写进行处理
在子线程中使用Handler
在子线程中使用Handler的方式如下
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}