欢迎转载,请标明出处:http://blog.csdn.net/a_long_/article/details/51635378 ,本文出自【阿隆 a_long_】
本文目录:
1.Handler的两个作用
2.为什么创建Handler之前必须要调用Looper的prepare()方法?
3.为什么所以每个线程对应一个looper对象,同样对应一个messageQueue?
4.为什么子线程不更新UI,也会需要用到Looper?
5.如果子线程需要用Looper,怎么使用?
6.为什么主线程不需要prepare()?
7.Handler的构造函数里还有一个async是干什么的?
8.Handler是在哪个线程执行的post和handleMessage?
9.Handler里面的callback又是什么鬼?
10.HandlerThread的run方法已经被写死了,那还怎么在子线程中干活:
11. MessageQueue如何根据规定的时间对信息执行调度,如何在无信息的时候阻塞,有信息的时候唤醒:
1.Handler的两个作用
1:调度消息:在未来执行某操作(放到Looper里有顺序的执行)。
2:在别的线程(而不是自己的线程)执行某操作。(子线程耗时操作后利用Hadnler在主线程更新UI)
源码中的解释:
There are two main uses for a Handler: (1) to schedule messages and
runnables to be executed as some point in the future; and (2) to enqueue
an action to be performed on a different thread than your own.
2.为什么创建Handler之前必须要调用Looper的prepare()方法?
由于:
原因1:Handler的构造函数有很多种,但最终都会直接去拿当前线程的sThreadLocal里面的Looper实例(sThreadLocal.get();),如果没有实例就报异常。
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
所以导致:创建Handler之前必须要调用Looper的prepare()方法。
3.为什么所以每个线程对应一个looper对象,同样对应一个messageQueue?
Looper在创建实例的时候,会和当前线程绑定且创造出一个对应的MessageQueue,构造函数如下:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
由于Looper对象只能创建一次,原因如下:
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对象,同样只对应一个messageQueue。4.为什么子线程不更新UI,也会需要用到Looper?
应用实例:大量图片的加载工具:比如GridView实现手机的相册功能,一般会用到LruCache,线程池,任务队列等;那么异步消息处理可以用哪呢?
1、用于UI线程当Bitmap加载完成后更新ImageView
2、在图片加载类初始化时,我们会在一个子线程中维护一个Loop实例,当然子线程中也就有了MessageQueue,Looper会一直在那loop停着等待消息的到达,当有消息到达时,从任务队列按照队列调度的方式(FIFO,LIFO等),取出一个任务放入线程池中进行处理。
简易的一个流程:当需要加载一张图片,首先把加载图片加入任务队列,然后使用loop线程(子线程)中的hander发送一个消息,提示有任务到达,loop()(子线程)中会接着取出一个任务,去加载图片,当图片加载完成,会使用UI线程的handler发送一个消息去更新UI界面。
(此例摘自鸿洋老师)
总结:子线程的Looper是维护消息队列、调度用的,使得线程池有序运作。如果要更新UI,还是需要主线程的Handler。
5.如果子线程需要用Looper,怎么使用?
方法一:Looper类里面的描述,非常清楚:
* Class used to run a message loop for a thread. Threads by default do
* not have a message loop associated with them; to create one, call
* {@link #prepare} in the thread that is to run the loop, and then
* {@link #loop} to have it process messages until the loop is stopped.
*
* Most interaction with a message loop is through the
* {@link Handler} class.
*
*
This is a typical example of the implementation of a Looper thread,
* using the separation of {@link #prepare} and {@link #loop} to create an
* initial Handler to communicate with the Looper.
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();
}
}
方法二:使用HandlerThread,在创建Handler的时候给一个参数Handler handler = new Handler(handlerThread.getLooper())。
原理和方法一是一样的,只是HandlerThread帮忙封装了prepare(), Looper.loop();方法,所以不用自己来调用。
源码如下:
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
6.为什么主线程不需要prepare()?
因为在创建ActivityThread的时候调用了prepareMainLooper(),这时已经创建了,无需自己再调用。
源码如下:
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
7.Handler的构造函数里还有一个async是干什么的?
这个参数默认是false,也就是默认Handler是非异步(同步)的,也就是Looper一个一个往外拿Message,这才能保证UI线程不报异常。
但是如果一个Looper的MessageQueue里都可以接受同步,那么把这个参数设成true可以提高效率。
不过实际上这样的构造函数都被@hide了,所以实际上所有的Handler都是同步的。
Handler类对async参数的解释:
* Handlers are synchronous by default unless this constructor is used to make
* one that is strictly asynchronous.
*
* Asynchronous messages represent interrupts or events that do not require global ordering
* with respect to synchronous messages. Asynchronous messages are not subject to
* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
*
* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
* each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
8.Handler是在哪个线程执行的post和handleMessage?
都是在Handler所在的线程。比如Handler一般在主线程创建,那么post的runnable对象以及handleMessage都是在主线程工作的,容易造成ANR。应该建立子线程,在run函数中通过主线程handler的引用来sendMessage,这样就不会耗时。或者在子线程的Handler中的handleMessage中循环调用给自己发消息并且利用主线程的Handler更新UI,也可以实现子线程做耗时任务最终UI线程更新UI的目的。
handler的发送message和task只是往某一个looper的MessageQueue注入一项。
Looper是和thread结合的,就是一个事件循环。当该线程回到其事件循环时,之前注入的项就会得到执行。
至于在哪个线程里执行,要看handler发送到哪个线程的looper了,创建handler时都会绑定一个looper(ui线程是自动绑定的),handler发送的message等都会在该looper的线程得到处理。
如果是想在HandlerThread执行,那么在创建Handler的时候new Hadnler(handlerThread.getLooper())即可。
9.Handler里面的callback接口又是什么鬼?
根据源码里面的说明:Callback interface you can use when instantiating a Handler to avoid having to implement your own subclass of Handler.
可以用callback实例化Handler而不必自己实现Handler的子类。
所以和自己实现Handler的子类的效果是相同的,只是方便一些。
10.HandlerThread的run方法已经被写死了,那还怎么在子线程中干活:
用子线程的Handler的方法,有sandMessage、sendMessageAtTime等,比如可以在handleMessage里面加入sendMessageAtTime(message,1000),那么子线程就会每秒钟去干活。
11.MessageQueue如何根据规定的时间对信息执行调度,如何在无信息的时候阻塞,有信息的时候唤醒:
这不是队列,而是一个链表,放入链表中合适的位置用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) {
//p为null(代表MessageQueue没有消息) 或者msg的触发时间是队列中最早的, 则进入该该分支
msg.next = p;
mMessages = msg;
needWake = mBlocked; //当阻塞时需要唤醒
} else {
//将消息按时间顺序插入到MessageQueue。一般地,不需要唤醒事件队列,除非
//消息队头存在barrier,并且同时Message是队列中最早的异步消息。
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;
prev.next = msg;
}
MessageQueue是按照Message触发时间的先后顺序排列的,队头的消息是将要最早触发的消息。当有消息需要加入消息队列时,会从队列头开始遍历,直到找到消息应该插入的合适位置,以保证所有消息的时间顺序。
读取下一条message用MessageQueue的next函数:
@Override
Message next() {
final long ptr = mPtr;
int pendingIdleHandlerCount = -1; // 循环迭代的首次为-1
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//阻塞操作,当等待nextPollTimeoutMillis时长,或者消息队列被唤醒,都会返回
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
//当消息Handler为空时,查询MessageQueue中的下一条异步消息msg,则退出循环。
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
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;
//设置消息的使用状态,即flags |= FLAG_IN_USE
msg.markInUse();
return msg; //成功地获取MessageQueue中的下一条即将要执行的消息
}
} else {
//没有消息
nextPollTimeoutMillis = -1;
}
//当消息队列为空,或者是消息队列的第一个消息时
if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
//没有idle handlers 需要运行,则循环并等待。
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
}
//当调用一个空闲handler时,一个新message能够被分发,因此无需等待可以直接查询pending message.
nextPollTimeoutMillis = 0;
}
}
参考:
鸿洋大神的分析
guolin大神的Handler分析
广泛的网上对Handler、Looper的源码分析
Mars老师的教程
Handler详细使用方法实例