为了更好的理解 Looper 的工作原理,我们需要对 ThreadLocal 进行了解,如果对 ThreadLocal 没有了解的童鞋,可以参看 ThreadLocal 原理
概述
Handler 作为日??⒌谋乇福豢杀苊饩鸵婕罢夥矫娴闹?。从开发者角度来说,Handler 是 Android 消息机制的上层接口,使得开发的时只需与 Handler 交互即可。Handler 使用也很简单,能够轻松将一个任务切换到 Handler 所在的线程中执行。
很多人认为Handler的作用就是更新UI,的确没错,但是更新UI仅仅是Handler的一个特殊的使用场景。具体来说,就是有时候需要在子线程做一些耗时操作,比如说访问网络或者耗时的I/O操作,当这些耗时操作完成时,程序的UI进行相应的改变。由于安卓开发规范的限制,我们不能在子线程中访问UI控件,因为UI的控件是线程非安全的,这个时候通过Handler就可以将更新UI的操作切换到主线程中执行。
Android 的消息机制主要是指 Handler 的运行机制。事实上,Handler,Looper,MessageQueue 是一套运行体制而出现的,MessageQueue 是一个消息队列,以队列形式提供插入和删除,主要用于消息的存储,内部实现是采用单链表的形式来组织 Message。而 Looper 用于处理消息,Looper 内部会以无限循环去查是否有新的 Message ,有则处理,没有就等待。需要特殊说明的是,Looper 内部是使用 ThreadLocal 实现的,由于ThreadLocal 可以在每一个线程中互不干扰的存取数据,所以通过ThreadLocal 就可以轻松获取每个线程的 Looper。
Message :android.os.Message是定义一个Messge包含必要的描述和属性数据,并且此对象可以被发送给android.os.Handler处理。属性字段:arg1、arg2、what、obj、replyTo等;其中arg1和arg2是用来存放整型数据的;what是用来保存消息标示的;obj是Object类型的任意对象;replyTo是消息管理器,会关联到一个handler,handler就是处理其中的消息。通常对Message对象不是直接new出来的,只要调用handler中的obtainMessage方法来直接获得Message对象。
需要特殊说明的是,线程是默认没有 Looper 的,如果需要使用 Handler 就必须为线程创建Looper,但是APP 的主线程中,即 ActivityThread ,在主线程被创建的时候就会初始化 Looper。另外,在没有Looper 的线程创建 Handler 也会失败。
使用案例
话不多说,上例子:
public class MainActivity extends AppCompatActivity {
private TextView textView;
private String TAG = "MainActivity";
private int i = 0;
Handler mHandler = new Handler(){
/**
* handleMessage接收消息后进行相应的处理
* @param msg
*/
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.what==1){
textView.setText(msg.arg1+"");
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.textView);
}
public void onClick(View v){
++i;
//创建新的线程
new Thread(){
@Override
public void run() {
super.run();
doSendMsg();
}
}.start();
}
/**
* 在子线程中做耗时操作,完成之后,通知Handler更新UI
*/
private void doSendMsg(){
try {
Thread.sleep(1000);//模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
Message message = Message.obtain();
message.arg1 = i;
message.what = 1;
mHandler.sendMessage(message);
}
}
原理分析
代码版本 : Android API25
Message
生成一个 Message
前面讲过 Message 通常不是 new 出来的,而是通过调用 Handler 的obtainMessage() 得到一个新的 Message:
public final Message obtainMessage()
{
return Message.obtain(this);
}
而 Handler 就调用 Message 的 obtain(Handler h) 方法:
public final class Message implements Parcelable {
public int what; // 让接收者知道这是什么
public int arg1; // 存储 int 用于传递过去
public int arg2;
public Object obj; // 传递一个对象过去
public Messenger replyTo; // 可以发送对此消息的回复。具体如何使用取决于发送者和接收者
public int sendingUid = -1;
/*package*/ static final int FLAG_IN_USE = 1 << 0; // 标记消息被使用
/*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1; // 标记消息是异步的
/*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;
/*package*/ int flags;
/*package*/ long when; // Message 的执行时间 重要?。。? /*package*/ Bundle data;
/*package*/ Handler target; // target 负责处理该消息
/*package*/ Runnable callback; // Runnable 类型的 callback
/*package*/ Message next; // 下一条消息,因为消息队列是链式存储的
private static final Object sPoolSync = new Object(); // 控制并发访问
private static Message sPool; // 回收池的头结点
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
private static boolean gCheckRecycle = true;
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool; // 将头结点赋值给Message
sPool = m.next; // 将头结点更新为下一个节点
m.next = null; // 断掉以前头节点与当前节点联系
m.flags = 0; // clear in-use flag
sPoolSize--; // 将回收池的数量减一
return m;
}
}
return new Message();
}
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h; // 注意,这里将 Handler 保存在了 target 中,后面会调用target来处理这个Message
return m;
}
// ...
}
到了最后就是Message 的 obtain() 方法,从 global pool 返回一个新的Message实例。 允许我们在许多情况下避免分配新对象。而这个 global pool 呢,其实就是一个单链表,从头结点取一个 Message ,如果没有就 new 一个 Message。而这个单链表呢,就是讲无用需要回收的 Message 组织起来的。the global pool 其实就是使用静态常量组织了一些无用了的 Message,组织的数据结构就是单链表。
看源码就知道,重载了多个obtain 方法,其实就是把上述可选参数配置一下,然后调用 obtain() 得到一个Message。
回收 Message
可能看了生成有点懵,那我们提前看一下回收,就好一点了, Message 的 源码:
public void recycle() {
if (isInUse()) { // isInUse() return ((flags & FLAG_IN_USE) == FLAG_IN_USE);
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return; // 在用就不回收
}
recycleUnchecked(); // 直接回收
}
/**
* Recycles a Message that may be in-use.
* Used internally by the MessageQueue and Looper when disposing of queued Messages.
*/
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE; // 设置为没有使用
what = 0; // 抹去数据
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool; // 将当前 Message 的下一个设置为以前的头结点
sPool = this; // 更新头结点为当前节点
sPoolSize++;
} // 如果到达MAX_POOL_SIZE数量,这个Message就会因为没有与引用链相连而被GC回收
}
}
回收还是很简单的,就是先检验是否在使用,如果不是,抹去上面的数据,将 Message 加到我称为 回收池
的链表里。需要注意的是,这里如果回收池数量到了上限,这个Message就会因为没有与引用链相连而被GC回收。
MessageQueue
MassageQueue 叫做消息队列,通过一个单链表的数据结构来维护消息列表,而且单链表在插入和删除上比较有优势。MessageQueue 主要包含两个操作:插入 和 读取。插入读取对应的方法分别是enqueueMessage(Message msg, long when) 和 next()。
工作流程
这里的工作流程先是讲一下例子里面的工作流程,其次补充一些必要的流程,如回收等。
UI线程 Looper 的创建
我们知道 UI 线程是在 ActivityThread 中创建的,这个函数就是整个 APP 的入口。接下来就是 ActivityThread 中的 main:
public static void main(String[] args) {
...
Looper.prepareMainLooper(); // 1. 创建UI线程的 Looper
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
// UI 线程的Handle
// getHandler() 得到的是 ActivityThread.H (extends Handler)
sMainThreadHandler = thread.getHandler();
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop(); //2. 执行消息循环
throw new RuntimeException("Main thread loop unexpectedly exited");
}
看到没有,通过1,2步的配置,这时候 UI 线程中 Looper 其实已经跑起来了,在程序中就已经可以使用 Handler 了。而子线程却默认没有配置。
需要注意的 UI 线程使用的 prepareMainLooper()
来准备 Looper,但是这个方法虽然是 public 的,但是这是专门为 UI 线程量身定做的,我们绝对不可以使用,我们准备Looper可以使用 Looper 的 prepare()
就好。
prepareMainLooper()
接下来我们来一步一步分析 (Looper 源码:)
public static void prepareMainLooper() {
prepare(false); // new 一个 Looper 放到该线程的ThreadLocalMap中
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
// 将 UI 线程的 Looper 放到静态常量 sMainLooper 中,那么随时随地都可以new 出主线程的 Handler
sMainLooper = myLooper(); // myLooper() return sThreadLocal.get();
}
}
public static void prepare() {
prepare(true); // 子线程中默认是可以销毁消息队列的
}
private static void prepare(boolean quitAllowed) { // True if the message queue can be quit.
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// sThreadLocal 是在类定义的时候就初始化了的 static final ThreadLocal<Looper>
sThreadLocal.set(new Looper(quitAllowed));
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get(); // 得到当前线程的 Looper
}
很简单,结合注释,应该都懂了,就是 new 了一个 Looper ,放到了主线程的 ThreadLocalMap 中。那 new 的时候干了什么呢?
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
原来是这样,在 new Looper 的时候就创建了 UI 线程的消息队列,并且指定不可以删除。
// True if the message queue can be quit.
private final boolean mQuitAllowed;
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
队列是否可以删除,一直向下传递,最后原来是保存在这里。但是这里还有一个 Native 方法,是干嘛的呢?笔者认为是得到了这个对象所在线程的引用。
到这里,ActivityThread 中的第一步就算是完成了。由于 sMainThreadHandler 的后期使用涉及复杂,就留到后面讲解,这里用 例子中的 Handler 代替讲解,最后执行的 Handler 是一样的,但是 ActivityThread.H(sMainThreadHandler )封装了其他东西。
创建 Handler
看着例子中的 Handler 是直接 new 出来的,那我们看一下 Handler 的无参构造方法:
public class Handler {
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
...
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;
}
}
我们看到 Handler 在构造方法中,通过 Looper.myLooper() 得到当前线程(UI 线程)的Looper,并且保存到本地 final 变量 mLooper 中。
要知道,消息队列被封装在 Looper 中,而每一个 Looper 又会关联一个线程(Looper 通过 ThreadLocal 封装),最终等于每一个消息队列都会关联一个线程。同时,由上代码可知,每个 Handler 也都会关联一个消息队列。在这里需要注意,Looper 和 MessageQueue 并没有与 Handler关联,而是Handler 与 Looper 和 MessageQueue 建立联系。
Looper.loop()
创建了Looper之后,就要执行消息循环了,我们知道,通过 Handler 来 Post 消息给消息队列,那么怎么处理呢?那就是最开始第二步的 Looper.loop() 中的。
public static void loop() {
final Looper me = myLooper(); // 获取当前线程的 Looper
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue; // 1. 获取消息队列
// 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 (;;) { // 2. 消息循环
Message msg = queue.next(); // might block可能阻塞 3. 获取消息
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg); // 4. 处理消息
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
msg.recycleUnchecked(); // 回收
}
}
我们可以看到 loop 方法中实际上就是建立一个死循环,然后通过从消息队列中逐个取出消息,最后进行处理,至到取到 null 值才退出。这里并没有任何的阻塞,那我消息取完了就退出了么?不,原理请看 MassageQueue 的 next() :
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
// 注意,以下的代码都在循环体里面
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis); // 重要?。?!
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis(); // 从开机到现在的毫秒数(手机睡眠的时间不包括在内)
Message prevMsg = null; // 前驱结点
Message msg = mMessages; // 头结点
// 如果没有 Handler,就在列表中找下一个同步的 Message 来执行。
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) { // 如果头结点不为空
if (now < msg.when) { // 如果还没有到执行时间
// Next message is not ready. Set a timeout to wake up when it is ready.
// 设置唤醒时间,距离多久执行 与 int 最大值 2^31 - 1 作比较
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 注意这时候已经到执行时间了
// Got a message.
mBlocked = false;
// 从消息队列中取出这个 Message
if (prevMsg != null) { // 如果前驱结点不为 null,
prevMsg.next = msg.next; // 请参看 next() 图 1
} else {
mMessages = msg.next; // 将头结点后移
}
msg.next = null; // 断掉要处理的 Message 与 消息队列的联系
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse(); // 标记为使用
return msg;
}
} else { // 头结点为空
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
} //同步代码块结束
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
// Disposes of the underlying message queue.
// Must only be called on the looper thread or the finalizer.
private void dispose() {
if (mPtr != 0) {
nativeDestroy(mPtr);
mPtr = 0;
}
}
代码虽然很长,但是我们还是得看啊,其实就是取出单链表(我们前面已说过,MessageQueue其实是一个单链表结构)中的头结点,然后修改对应指针,再返回取到的头结点而已。因为这里采用的是无限循环,所以可能会有个疑问:该循环会不会特别消耗CPU资源?其实并不会,如果messageQueue有消息,自然是继续取消息;如果已经没有消息了,此时该线程便会阻塞在该next()方法的 nativePollOnce() 方法中,主线程便会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生(设置的nextPollTimeoutMillis到了)时,才通过往pipe管道写端写入数据来唤醒主线程工作。这里涉及到的是Linux的pipe/epoll机制,epoll机制是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。
总结:
通过 Looper.loop() 来创建 Looper 对象( 消息队列封装在 Looper 对象中 ),并且保存在 sThreadLocal 中,然后通过 Looper.loop() 来执行消息循环。
说了取,当然接着就是分发了。我们调用msg.target.dispatchMessage(msg) 来执行 Message 。在创建 Message 的过程中,传过来的 Handler 的引用就被保存在了Message中(最上面有代码和讲解)。接下来看一下 Handler 的处理:
final Callback mCallback;
public interface Callback {
public boolean handleMessage(Message msg);
}
public void handleMessage(Message msg) {
}
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run(); // 此时是在 Handler 所在线程中执行
}
这里面也特别简单,大家都知道当我们在 Handler post()消息的时候是使用 Runnable,而 sendMessage() 是自己构建的 Message。这里首先判断 Message 类型,如果是 Runnable ,就调用run运行,如果不是,先判断创建 Handler 的时候是否设置回调,设置了就调用回调中的处理方法 handleMessage(msg),如果没有就使用默认的 handleMessage(msg),这时候的这个方法大多数时候都会被重写,就像例子一样。
这里我们可以看到,在分发消息时三个方法的优先级分别如下:
- Message的回调方法优先级最高,即message.callback.run();
- Handler的回调方法优先级次之,即Handler.mCallback.handleMessage(msg);
-
Handler的默认方法优先级最低,即Handler.handleMessage(msg)。
使用 Handler 来 sendMessage(Message msg)
既然提到 post() 和 sendMessage(),那么下面就讲解一下它是如何将一个 Message 加到队列中的。
现讲解 sendMessage() 吧,例子就是最上面的例子,那我们接着看 Handler 的源码吧:
/**
* Pushes a message onto the end of the message queue after all pending messages
* before the current time. It will be received in {@link #handleMessage},
* in the thread attached to this handler.
*
* @return Returns true if the message was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0); // 默认延时为 0
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) { // 校验延时,因为延时只能 >= 0
delayMillis = 0;
}
// SystemClock.uptimeMillis() 从开机到现在的毫秒数(手机睡眠的时间不包括在内)
// SystemClock.uptimeMillis() + delayMillis算出来就是更新时间
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
// 这个值是通过从Looper 赋值过来的,就意味着是持有和Looper 中相同的引用
// Looper 中的修改的话mQueue,这个值也会被修改
// 当调用 Looper.quit() 的时候,这个值就被置空了的。
MessageQueue queue = mQueue;
if (queue == null) { // 因为需要发送到 MassageQueue中,所以不能为空
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; // 保存 Message 的 target
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
一直往下调用了三个方法才到底,还好都简单,来看一下。将设置的时延转化成应该被执行的时间,拿到关联的消息队列,随后保存 Message 的 target,然后调用消息队列的 enqueueMessage(msg, uptimeMillis)
,将这个Message 加入队列。那怎么加的么?接下来就是 MessageQueue 的 enqueueMessage(Message msg, long when)
:
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) { // 一个新的 Message 是不会 InUse 的,在回收的时候设置为没在使用的
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) { // 获得自身的同步锁
if (mQuitting) { // MessageQueue 正在退出
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle(); // 将Message实例回收
return false;
}
msg.markInUse(); // flags |= 1 标记正在使用
msg.when = when; // 设置要插入 Message 的执行时间
Message p = mMessages; // 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(); //记得看上面的英文注释 默认为 false
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); // 唤醒线程的 Native 方法
}
}
return true;
}
看 enqueueMessage 的实现,它的主要操作就是单链表的插入操作,并且这链表是以执行时间 when 作为顺序的。需要重提的是,这时候的 when
已经转换成了距离开机到执行的毫秒数。
- 校验。是检测target是否存在,因为Message. targe 是用来处理这个 Message 的,所以一定要有target,其次判断当前 Message 是否正在被使用,然后验证当前 MessageQueue 是不是已经被 quit 了(mQuitting),验证通过过后就是正式的插入操作。
- 配置。设置 Message 该有的属性,msg.markInUse(); msg.when = when;
- 这个方法比较巧妙,将几种具体情况用一份代码解决了,但是都可以归结为:在头结点之前插入结点。看到这里应该注意到,如果插入的是需要非延时 Message,并且线程阻塞了,就会调用
nativeWake(mPtr)
唤醒线程。
- 当还没有链表的时候(p == 0)
- 消息的执行时间 比 里面的消息还要早(when == 0 || when < p.when)
-
在链表中间或最后插入。循环遍历链表,拿到链表合适的 Message,然后再将新 Message 插入。由于还没有到消息的处理时间,就不会唤醒线程。此插入过程如下:
使用 Handler 来 post()
先上一个使用的例子:
private Handler mHandler;//全局变量
@Override
protected void onCreate(Bundle savedInstanceState) {
mHandler = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);//在子线程有一段耗时操作,比如请求网络
mHandler.post(new Runnable() {
@Override
public void run() {
mTestTV.setText("This is post");//更新UI
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
例子很简单,就不讲解了,接下来就来看一下 Handler 怎么 post 的吧:
/**
* Causes the Runnable r to be added to the message queue.
* The runnable will be run on the thread to which this handler is
* attached.
*
* @param r The Runnable that will be executed.
*
* @return Returns true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*/
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain(); // 得到一个新的 Message
m.callback = r; // 是使用 post 的 Runnable 才会保存有这个参数
return m;
}
眼尖的童鞋估计看到就要easy了。在 post 一开始就调用了 sendMessageDelayed(Message msg, long delayMillis)
,是不是很眼熟,对的,前面sendMessage(Message msg)
一开始也是调用这个方法。这就是非延时消息的插入。后面具体如何插入请参看上一条。
同样的,如果是使用 postDelayed(Runnable r, long delayMillis)
呢?使用例子请参看 面试题:Handler机制,Handler除了线程通信还有什么作用 中的作用二的第二种实现方式。转回来看 Handler 的 postDelayed :
public final boolean postDelayed(Runnable r, long delayMillis)
{
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
原来啊,大家都用的一套,还是调用的 sendMessageDelayed
方法,只不过延迟时延不再为默认的 0. 这时候就是延迟消息的插入。
退出消息循环
退出消息循环有两种方式,分别是Looper 的 quit()
和 quitSafely()
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
这还是很简单,直接调用了 MassageQueue 的 qiut(boolean safe)
:
void quit(boolean safe) {
if (!mQuitAllowed) { // 主线程设置不允许退出,其他子线程默认设置的true
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) { // 默认为 false,表示是否正在退出消息队列
return;
}
mQuitting = true; // 全局唯一一次赋值,表示正在退出消息队列
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
首先判断是否是主线程,因为主线程的消息队列不允许退出,然后判断当前线程是否正在退出。值得注意的是,mQuitting 是 MassageQueue 的成员变量,是拥有默认值的,默认值是 false。如果不是正在退出消息队列,则将其标志为 正在退出,注意,全局就只有这里修改了 mQuitting 的值。然后判断根据要求是否安全移除 MessageQueue。
private void removeAllMessagesLocked() {
Message p = mMessages; // 头结点
while (p != null) {
Message n = p.next; // 下一个结点
p.recycleUnchecked(); // 前面讲过,就是将数据抹去,放入回收的那个链表
p = n;
}
mMessages = null; // 将MessageQueue 中保存的头结点设置为 null
}
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis(); // 从开机到现在的毫秒数(手机睡眠的时间不包括在内);
Message p = mMessages; // 头结点
if (p != null) {
// 如果头结点设置的延迟时间 > 大于当前时间
// 注意 这里比较的时候都是用的从开机到现在的毫秒数
// 这里意味着还没有到设置的执行时间
if (p.when > now) {
removeAllMessagesLocked(); // 直接移除全部还未到执行时间的 Message
} else {
Message n;
for (;;) {
n = p.next; // 拿到下一个节点
if (n == null) { // 结束标志:没有下一个
return;
}
if (n.when > now) { // 还没有到设置的执行时间,退出循环
break;
}
p = n;
}
// 这时候得到的节点 p 是没有到设置的执行时间的前一个节点
// n 是没有到设置的执行时间的节点
p.next = null;
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}
removeAllMessagesLocked()
直接将MessageQueue 中的 Message 全部回收掉。无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送的需要延迟执行的消息)还是非延迟消息(delayMillis == 0)。
removeAllFutureMessagesLocked()
方法呢,就是只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理,在这个方法中,表现为直接return ,然后因为队列有Message,所以相应的 dispatchMessage(msg) 会调用。
quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消息。
面试题
handler发消息给子线程,looper怎么启动?
发消息就是把消息塞进去消息队列,looper在应用起来的时候已经就启动了,一直在轮询取消息队列的消息。
为什么在子线程中创建Handler会抛异常?
首先看如下代码:
new Thread(){
Handler handler = null;
@Override
public void run() {
handler = new Handler();
}
}.start();
前面说过,Looper 对象是 ThreadLocal 的,即每个线程都有自己的 Looper,这个 Looper 可以为空。但是,当你在子线程中创建 Handler 对象时,如果 Looper 为空,那就会抛出异常。源码解释一下:
public class Handler {
...
public Handler(Callback callback, boolean async) {
...
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;
}
...
}
从上述程序中,我们可以看到,当 mLooper 对象为空的时候,抛出了异常。这是因为该线程中的 Looper 对象还没有创建,因此 sThreadLocal.get() 会返回 null。Handler 的原理就是要与 MassageQueue 建立关联,并且将消息投递给MassageQueue,如果连 MassageQueue 都没有,那么 Handler 就没有存在的必要,而 MassageQueue 又被封装在 Looper 中,因此,创建 Handler 时 Looper 一定不能为空。解决方法:
new Thread(){
Handler handler = null;
@Override
public void run() {
Looper.prepare(); // 1. 为当前线程创建 Looper,并会绑定到 ThreadLocal 中
handler = new Handler();
Looper.loop(); // 2. 启动消息循环
}
}.start();
在UI线程为什么可以直接使用呢,就是因为在 ActivityThread中默认帮你执行了 1,2步了的。
Handler为什么loop是死循环。
在android中如果主线程(UI线程)中进行耗时操作会引发ANR(Application Not Responding)异常,产生ANR的原因一般有两种:
当前的事件没有机会得到处理(即主线程正在处理前一个事件,没有及时的完成或者looper被某种原因阻塞住了)
当前的事件正在处理,但没有及时完成
比如onCreate()中进行了耗时操作,导致点击、触摸等不响应,就会产生ANR。为了避免ANR异常,android使用了Handler消息处理机制,让耗时操作在子线程运行,需要UI线程进行处理的操作给UI线程发送消息。
我们知道Handler发消息给UI线程就可以处理消息,UI线程维护着一个Looper和一个消息队列,Looper不停的拿消息队列的消息去分发处理。到这里问题来了:如果这么做的话,UI线程岂不是要一直死循环轮询消息队列拿消息?死循环不是造成ANR吗?
是的,确实是死循环,但是 ANR 还得另说。
ActivityThread 的 main()
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
...
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
Looper.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
...
msg.target.dispatchMessage(msg);
...
msg.recycleUnchecked();
}
}
Looper.loop()是在死循环处理消息,如果main方法中没有looper进行循环,那么主线程一运行完毕就会退出,那才不正常了呢!?
所以,ActivityThread的main方法主要就是做消息循环,一旦退出消息循环,那么你的应用也就退出了。
那么问题重新问一遍:那为什么这个死循环不会造成ANR异常呢?
其实原因是显然的,我们知道Android是由事件驱动的,Looper.loop() 不断地接收事件、处理事件,每一个点击触摸或者说Activity的生命周期都是运行在 Looper.loop() 的控制之下,如果它停止了,应用也就停止了。只能是某一个消息或者说对消息的处理阻塞了 Looper.loop(),而不是 Looper.loop() 阻塞消息?;谎灾?,消息队列为空的时候会阻塞主线程,而处理消息的时候不可以阻塞,这时候的阻塞 5 s就会 ANR。
也就说我们的代码其实就是在这个循环里面去执行的,当然不会阻塞了。
而且主线程Looper从消息队列读取消息,当读完所有消息时,主线程阻塞。子线程往消息队列发送消息,并且往管道文件写数据,主线程即被唤醒,从管道文件读取数据,主线程被唤醒只是为了读取消息,当消息读取完毕,再次睡眠。因此loop的循环并不会对CPU性能有过多的消耗。
总结:Looer.loop()方法可能会引起主线程的阻塞,但只要它的消息循环没有被阻塞,能一直处理事件就不会产生ANR异常。
Handler 机制 很多细节需要关注:如线程如何建立和退出消息循环等等)
这里就是让你回答 Handler 的工作原理。
关于Handler,在任何地方new Handler 都是什么线程下?
这个需要分类讨论:
- 像最开始那样呢,直接new 出来,不带Looper 参数,那么就在创建 Looper 的线程下。
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
public Handler(boolean async) {
this(null, async);
}
public Handler(Callback callback, boolean async) {
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;
}
- 在任何地方 new 出指定线程的 Handler。例如主线程,看案例:
new Thread(new Runnable() {
@Override
public void run() {
// 在子线程中实例化Handler同样是可以的,只要在构造函数的参数中传入主线程的Looper即可
Handler handler = new Handler(Looper.getMainLooper());
}
}).start();
前面讲过,UI线程调用的 prepare 函数不一样,多保存了UI线程的 Looper 到 Looper.sMainLooper 中的。其目的是在任何地方都可以实例化 UI 线程的 Handler。
而这时候调用的构造方法是
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
其实就是与传入的 Looper绑定,换言之,如果我传入的是其他线程的Looper,我同样也可以实例化其他线程的 Handler。
请解释下在单线程模型中Message、Handler、Message Queue、Looper之间的关系
讲到Handler,肯定离不开Looper、MessageQueue、Message这三者和Handler之间的关系,下面简略地带过,详细自己可以参看上面源码
Handler
将要执行的Message或者Runnable到消息队列。Looper
每一个线程只有一个Looper,每个线程在初始化Looper之后,然后Looper会维护好该线程的消息队列,用来存放Handler发送的Message,并处理消息队列出队的Message。它的特点是它跟它的线程是绑定的,处理消息也是在Looper所在的线程去处理,所以当我们在主线程创建Handler时,它就会跟主线程唯一的Looper绑定,从而我们使用Handler在子线程发消息时,最终也是在主线程处理,达到了异步的效果。
那么就会有人问,为什么我们使用Handler的时候从来都不需要创建Looper呢?这是因为在主线程中,ActivityThread默认会把Looper初始化好,prepare以后,当前线程就会变成一个Looper线程。
MessageQueue
MessageQueue是一个消息队列,用来存放Handler发送的消息。每个线程最多只有一个MessageQueue。MessageQueue通常都是由Looper来管理,而主线程创建时,会创建一个默认的Looper对象,而Looper对象的创建,将自动创建一个MessageQueue。其他非主线程,不会自动创建Looper。Message
消息对象,就是MessageQueue里面存放的对象,一个MessageQueu可以包括多个Message。当我们需要发送一个Message时,我们一般不建议使用new Message()的形式来创建,更推荐使用Message.obtain()来获取Message实例,因为在Message类里面定义了一个消息池,当消息池里存在未使用的消息时,便返回,如果没有未使用的消息,则通过new的方式创建返回,所以使用Message.obtain()的方式来获取实例可以大大减少当有大量Message对象而产生的垃圾回收问题。
Handler机制,Handler除了线程通信还有什么作用
Handler的主要用途 :
- 推送未来某个时间点将要执行的Message或者Runnable到消息队列。
- 在子线程把需要在另一个线程执行的操作加入到消息队列中去。
1. 推送未来某个时间点将要执行的Message或者Runnable到消息队列
实例:通过Handler配合Message或者Runnable实现倒计时
- 方法一,通过Handler + Message的方式实现倒计时。代码如下:
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding mBinding;
private Handler mHandler ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
//设置监听事件
mBinding.clickBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//通过Handler + Message的方式实现倒计时
for (int i = 1; i <= 10; i++) {
Message message = Message.obtain(mHandler);
message.what = 10 - i;
mHandler.sendMessageDelayed(message, 1000 * i); //通过延迟发送消息,每隔一秒发送一条消息
}
}
});
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
mBinding.time.setText(msg.what + ""); //在handleMessage中处理消息队列中的消息
}
};
}
}
这里用到了DataBiding,可能没用过的同学看起来有点奇怪,但其实反而简略了代码,有一定基础的同学看起来都不会有太大压力。通过这个小程序,笔者希望大家可以了解到Handler的一个作用就是,在主线程中,可以通过Handler来处理一些有顺序的操作,让它们在固定的时间点被执行。
- 方法二,通过Handler + Runnable的方式实现倒计时。代码如下:
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding mBinding;
private Handler mHandler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
//设置监听事件
mBinding.clickBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
for (int i = 1; i <= 10; i++) {
final int fadedSecond = i;
//每延迟一秒,发送一个Runnable对象
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mBinding.time.setText((10 - fadedSecond) + "");
}
}, 1000 * i);
}
}
});
}
}
方法二也是通过代码让大家加深Handler处理有序事件的用途,之所以分开Runnable和Message两种方法来实现,是因为很多人都搞不清楚为什么Handler可以推送Runnable和Message两种对象。
其实,无论Handler将Runnable还是Message加入MessageQueue,最终都只是将Message加入到MessageQueue。Handler的post Runnable对象这个方法只是对post Message进行了一层封装,即将Runnable 放到的Message 中的 mCallback 存储起来,值得注意的是,如果直接将Message 加入MessageQueue的话,那么mCallback将为null,所以最终我们都是通过Handler推送了一个Message罢了,至于为什么会分开两种方法,只是为了更方便开发者根据不同需要进行调用。下面再来看看Handler的第二个主要用途。
2. 在子线程把需要在另一个线程执行的操作加入到消息队列中去
实例:通过Handler + Message来实现子线程加载图片,在UI线程显示图片
效果图如下
代码如下(布局代码也不放出来了)
public class ThreadActivity extends AppCompatActivity implements View.OnClickListener {
private ActivityThreadBinding mBinding = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_thread);
// 设置点击事件
mBinding.clickBtn.setOnClickListener(this);
mBinding.resetBtn.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
// 响应load按钮
case R.id.clickBtn:
// 开启一个线程
new Thread(new Runnable() {
@Override
public void run() {
// 在Runnable中进行网络读取操作,返回bitmap
final Bitmap bitmap = loadPicFromInternet();
// 在子线程中实例化Handler同样是可以的,只要在构造函数的参数中传入主线程的Looper即可
Handler handler = new Handler(Looper.getMainLooper());
// 通过Handler的post Runnable到UI线程的MessageQueue中去即可
handler.post(new Runnable() {
@Override
public void run() {
// 在MessageQueue出队该Runnable时进行的操作
mBinding.photo.setImageBitmap(bitmap);
}
});
}
}).start();
break;
case R.id.resetBtn:
mBinding.photo.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.default_pic));
break;
}
}
/***
* HttpUrlConnection加载图片,很简单
* @return
*/
public Bitmap loadPicFromInternet() {
Bitmap bitmap = null;
int respondCode = 0;
InputStream is = null;
try {
URL url = new URL("https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=1421494343,3838991329&fm=23&gp=0.jpg");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(10 * 1000);
connection.setReadTimeout(5 * 1000);
connection.connect();
respondCode = connection.getResponseCode();
if (respondCode == 200) {
is = connection.getInputStream();
bitmap = BitmapFactory.decodeStream(is);
}
} catch (MalformedURLException e) {
e.printStackTrace();
Toast.makeText(getApplicationContext(), "访问失败", Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return bitmap;
}
}
很简单,其实最主要的就是当我们需要在主线程执行一些操作的时候,就可以直接使用这种方式,这种方式有着直接发送 Message 不可实现的先天优势。
handler机制组成,handler机制每一部分的源码包括looper中的loop方法、threadlocal概念、dispatchmessage方法源码,runnable封装message等
上面讲解的还记得住么?
请解释下在单线程模型中Message、Handler、Message Queue、Looper之间的关系
- Android的单线程模型
当一个程序第一次启动时,Android会同时启动一个对应的主线程(Main Thread),主线程主要负责处理与UI相关的事件,如:用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理。所以主线程通常又被叫做UI线程。
在开发Android 应用时必须遵守单线程模型的原则:Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。
如果在非UI线程中直接操作UI线程,会抛出异常,这与普通的java程序不同。因为 ViewRootImpl 对 UI 操作做了验证,这个验证工作是由 ViewRootImpl 的 checkThread 方法来完成的:
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
由于UI线程负责事件的监听和绘图,因此,必须保证UI线程能够随时响应用户的需求,UI线程里的操作应该向中断事件那样短小,费时的操作(如网络连接)需要另开线程,否则,如果UI线程超过5s没有响应用户请求,会弹出对话框提醒用户终止应用程序。顺便说一下 ANR 默认情况下,在android中Activity的最长执行时间是5秒,BroadcastReceiver的最长执行时间则是10秒。
如果在新开的线程那为什么系统不对 UI 控件的访问加上锁机制呢?缺点有两个:首先加上锁机制会让 UI 访问的逻辑变得复杂;其次锁机制会降低 UI 访问的效率,因为锁机制会阻塞某些线程的执行。鉴于这两个缺点,最简单且高效的方法就是采用单线程模型来处理UI操作,对于开发者来说也不是很麻烦,只需要通过 Handler 切换一下 UI 访问的执行线程就好。
中需要对UI进行设定,就可能违反单线程模型,因此android采用一种复杂的Message Queue机制保证线程间通信。
- 后面就是分析的那一套了。Message Queue 、 Handler 、 Looper
Handler、Thread和HandlerThread的差别
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
/**
* Constructs a HandlerThread.
* @param name
* @param priority The priority to run the thread at. The value supplied must be from
* {@link android.os.Process} and not from java.lang.Thread.
*/
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
/**
* Call back method that can be explicitly overridden if needed to execute some
* setup before Looper loops.
*/
protected void onLooperPrepared() {
}
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
/**
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason is isAlive() returns false, this method will return null. If this thread
* has been started, this method will block until the looper has been initialized.
* @return The looper.
*/
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
/**
* Quits the handler thread's looper.
* <p>
* Causes the handler thread's looper to terminate without processing any
* more messages in the message queue.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p class="note">
* Using this method may be unsafe because some messages may not be delivered
* before the looper terminates. Consider using {@link #quitSafely} instead to ensure
* that all pending work is completed in an orderly manner.
* </p>
*
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*
* @see #quitSafely
*/
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
/**
* Quits the handler thread's looper safely.
* <p>
* Causes the handler thread's looper to terminate as soon as all remaining messages
* in the message queue that are already due to be delivered have been handled.
* Pending delayed messages with due times in the future will not be delivered.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p>
* If the thread has not been started or has finished (that is if
* {@link #getLooper} returns null), then false is returned.
* Otherwise the looper is asked to quit and true is returned.
* </p>
*
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*/
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
public int getThreadId() {
return mTid;
}
}
由于 HandlerThread 实在是代码少得变态,我就直接将全部源码贴上来了。
直接看一下run() 方法可能就明白了,就是将 Looper 启动起来,就等于主线程一样,可以直接使用 Handler 了,没必要 Looper.prepare() 再 Looper.loop() 了。需要知道这根本不是 handler ,而是封装了 Looper 的 Thread,方便了子线程与子线程通信。
还有一点
Handler: 它的消息处理方式是阻塞式的,必须一条一条的处理。耗时操作 不应该用handler处理。
HandlerThread:继承自Thread,它有个Looper,在这里可以执行耗时操作
什么是 IdleHandler?有什么用?怎么用
https://mp.weixin.qq.com/s/KpeBqIEYeOzt_frANoGuSg
这里还没写,努力更新中...
Handler消息机制,postDelayed会造成线程阻塞吗?对内存有什么影响?
- Handler消息机制
- postDelayed会造成线程阻塞吗:
还有印象么,上面讲解的 postDelayed。postDelayed只是在 post 的时候加了延时,最后这个延时讲被转换成执行时间存在每一个 Message 中。而在 loop() 中调用 next() 的死循环是阻塞式的,只有在下个消息到达或者有事务发生(设置的nextPollTimeoutMillis到了)时,才通过往pipe管道写端写入数据来唤醒线程工作。
也就是说如果当前消息队列中消息全部为 延时Message(全部没到执行时间),而这个 Message 的执行时间又比MessageQueue 中所有消息执行时间早,那么在loop循环取next 的时候就会因为最早这一个Message(刚刚postDelayed的Message)还没到执行时间而阻塞。
-
对内存有什么影响
Looper 是通过 ThreadLocal 存储在线程中的,而MessageQueue 是封装在 Looper 中的。
参考文章:
Android 官方文档 :Handler
MessageQueue
Looper
《 Android 开发艺术探索 》
《 Android 开发进阶 从小工到专家 》
深入理解Android中的Handler机制
一步一步分析Android的Handler机制
【从源码看Android】03Android MessageQueue消息循环处理机制(epoll实现)