参考文档:
https://blog.csdn.net/cshichao/article/details/8787357
http://www.voidcn.com/article/p-ckonfcng-bpx.html
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2016/1014/6675.html
https://www.cnblogs.com/panhouye/p/6494753.html
https://blog.csdn.net/qq_30379689/article/details/53394061
https://www.cnblogs.com/xiaowangabc/p/3973927.html
Handler作为线程之间传递消息的机制,当它被创建时,就会自动关联当前所在位置的线程和消息队列。Handler通过把Message或Runnable塞进消息队列,等待适当时机处理对应操作。
(1)Handler的创建:
基本的创建如下:
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
}
};
但是这样创建,系统会提示你:This Handler class should be static or leaks might occur,大意是指Handler必须是静态类,否则容易引起内存泄露。因为消息队列中等待被被处理的消息持有Handler对应的引用,上述代码创建的Handler是一个匿名内部类,它持有所在外部类的引用(不只是匿名内部类,内部类也会引起这个问题),如果消息队列里的消息长时间未被处理,Handler对象和Handler持有的外部类引用就会一直被持有,导致系统无法及时回收,此时就容易造成内存泄露。
根据参考文档里的内容,解决这个系统提示的方法有如下两种:
1,采用new Callback的方式,本方式系统不会有内存泄露的提示:
private final Handler myHandler = new Handler(new Handler.Callback(){
@Override
public boolean handleMessage(@NonNull Message msg) {
return false;
}
});
但存疑的地方是,有人说这样的写法也有可能发生内存泄露,参考:https://ask.csdn.net/questions/661236
2,采用弱引用的方式,定义一个static的内部类Handler,然后让它持有Activity的弱引用
static class XXHandler extends Handler {
//XXActivity为Handler所在的Activity
WeakReference<XXActivity> mActivity;
MyHandler(XXActivity activity) {
mActivity = new WeakReference<XXActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
XXActivity theActivity = mActivity.get();
switch (msg.what) {
//对应的具体操作
}
}
};
//具体操作的代码如下
//实例Handler,持有对应Activity的弱引用
XXHandler xHandler = new XXHandler(this)
//在需要操作的方法内执行对应操作
private void doSomething() {
xHandler.sendEmptyMessage(0x111);
}
使用静态内部类为什么会避免内存泄露呢?static修饰的东西不属于类本身,自然也不会持有类的引用,但系统却要为这些静态的东西开辟额外的内存空间,如果一个应用里含有大量的静态修饰的东西,容易造成卡顿,所以适时释放内存是必要的。
****在子线程中创建Handler****
class MyThread extends Thread {
@Override
public void run() {
//创建Looper对象并关联到子线程
Looper.prepare();
//创建Handler对象
Handler handler = new Handler(Looper.myLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
//调用loop方法循环遍历消息队列里的消息并发给Handler处理
Looper.loop();
}
}
(2)Handler参数的说明:
1,Message:不同线程之间用于传递消息的封装,通过what区分消息,arg1、arg2 字段用来传递int类型的数据,obj可以传递任意类型的字段。
***new Message和obtainMessage的区别***
Message msg = new Message(),这是直接在内存中申请空间,相对效率更低,如果多次使用new的方式创建Message,也容易造成内存紧张。
obtainMessage方法的源码:
@NonNull
public final Message obtainMessage(int what) {
return Message.obtain(this, what);
}
//
public static Message obtain(Handler h, int what) {
Message m = obtain();
m.target = h;
m.what = what;
return m;
}
//
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();
}
通过源码发现,obtain方法里,如果Handler已经有了对应的what标记的Message就不再次创建,直接取出来使用,反之则新建一个Message。
做个测试,代码如下:
private final Handler myHandler = new Handler(new Handler.Callback(){
@Override
public boolean handleMessage(@NonNull Message msg) {
switch (msg.what) {
case UPDATE_TXT:
String times = (String) msg.obj;
txtHint.setText("Handler更新TextView,time=" + times);
break;
}
return false;
}
});
//即时操作
private void sendNow() {
Log.e(TAG, "sendNow");
long times = System.currentTimeMillis();
Message message = myHandler.obtainMessage(UPDATE_TXT);
message.obj = "sendNow" + times;
message.sendToTarget();
}
//延时操作
private void sendDelay() {
Log.e(TAG, "sendDelay");
long times = System.currentTimeMillis();
Message message = myHandler.obtainMessage(UPDATE_TXT);
message.obj = "sendDelay" + times;
myHandler.sendMessageDelayed(message, 30 * 1000);
}
如果只调用sendNow方法,txtHint直接更新内容;如果只调用sendDelay,则在30秒后更新txtHint;但如果调用sendDelay后,在30秒的延迟时间里调用sendNow呢?此时Handler会将指定的what参数的Message取出,然后取消掉对应的延时操作,直接将此Message转给当前的操作,所以会立即更新txtHint,而延时30秒的操作就地取消。
**********************************************************************************
****Handler处理异步Message****
参考:https://www.jianshu.com/p/d28922aa5da9 https://blog.csdn.net/milllulei/article/details/80927539
通常创建的Handler调用send方法发送的消息都是同步消息,插入到队列中根据触发时间排序。Android系统16ms会刷新一次屏幕,如果主线程的消息过多,在16ms之内没有执行完,必然会造成卡顿或者掉帧。如果要让消息没有延时直接处理呢?此时可以用异步消息,使用异步需要用到一个叫做同步屏幕的东西,目的是让异步消息不用排队等候处理。同步屏障的作用就是一堵墙,先堵住同步消息,让异步消息先执行完后这堵墙消失,接着再执行同步消息。
查看MessageQueue的postSyncBarrier(低版本是enqueueMessage)方法的源码:
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
//将一个新的同步屏障插入到消息队列
//屏障的目的就是阻拦其他同步消息的处理,所以不需要唤醒消息队列
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
从源码看出方法里生成了一个没有target的Message也就是这个消息没有指定Handler对象(同步消息都有指定Handler),然后将这个消息插入到队列前排,然后再结合MessageQueue的next方法:
Message next() {
...
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//根据nextPollTimeoutMillis的值调用本地C++代码阻塞线程
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//如果msg是postSyncBarrier方法生成的同步屏障,则while循环直到取出异步消息
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
...
}
}
从postSyncBarrier方法生成的消息没有target,符合if (msg != null && msg.target == null)的判断条件,形成一个同步屏障阻塞同步消息,while循环取出异步消息进行处理。
如何发送异步消息:
<1>Message.setAsynchronous(true)
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
Message msg = myHandler.obtainMessage(ASYNC_TXT);
msg.setAsynchronous(true);
msg.sendToTarget();
<2>Handler的构造方法,传入 async 参数
public Handler(@Nullable Callback callback, boolean async) {
...
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
以此方式构造的Handler发送的消息都是异步消息。
使用和移除消息同步屏障
设置一个同步屏障:
//通过映射的方式调用postSyncBarrier设置同步屏障
try {
Log.d("TAG","插入同步屏障");
MessageQueue queue=handler.getLooper().getQueue();
Method method=MessageQueue.class.getDeclaredMethod("postSyncBarrier");
method.setAccessible(true);
token= (int) method.invoke(queue);
} catch (Exception e) {
e.printStackTrace();
}
移除同步障碍:
//通过映射的方式调用removeSyncBarrier移除屏障
try {
Log.d("TAG","移除屏障");
MessageQueue queue=handler.getLooper().getQueue();
Method method=MessageQueue.class.getDeclaredMethod("removeSyncBarrier",int.class);
method.setAccessible(true);
method.invoke(queue,token);
} catch (Exception e) {
e.printStackTrace();
}
---------------------------------------------------------------------------------------------------------------------------------------------
2,Handler:作为线程之间传递消息的即时接口,既可以在消息队列中创建、插入或移除消息,又可以在handleMessage方法中处理消息。
参考:https://blog.csdn.net/bzlj2912009596/article/details/80189760
https://www.cnblogs.com/baiyi168/p/6394628.html
public Handler(@Nullable 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();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
查看源码发现,每个Handler默认配置一个Looper,没有Looper,Handler根本无法存放和处理消息队列里的消息。
Handler里比较重要的方法:
(1)obtainMessage();obtainMessage(int what);obtainMessage(int what, Object obj);obtainMessage(int what, int arg1, int arg2);obtainMessage(int what, int arg1, int arg2, Object obj);
从Handler中判断是否有对应的Message引用,有的话直接取出使用,没有再new创建。方法可以接sendToTarget()方法进行链式调用,sendToTarget方法其实还是调用了sendMessage方法,源码如下:
public void sendToTarget() {
target.sendMessage(this);
}
链式调用的代码举例如下:
myHandler.obtainMessage(UPDATE_TXT).sendToTarget();
(2)getLooper(),获取Handler的looper对象。
(3)post(Runnable);postAtTime(Runnable,long);postDelayed(Runnable long);postAtFrontOfQueue(Runnable r);
通过查看源码发现这些方法内部其实还是调用了sendMessage等方法,postAtFrontOfQueue方法里实际调用的是sendMessageAtFrontOfQueue(getPostMessage(r)),表示将runnable插入到消息队列头部。
(4)sendEmptyMessage(int what);sendEmptyMessageAtTime(int what, long uptimeMillis);sendEmptyMessageDelayed(int what, long delayMillis);sendMessage(@NonNull Message msg);
sendMessageAtTime(@NonNull Message msg, long uptimeMillis);sendMessageDelayed(@NonNull Message msg, long delayMillis);
Handler用于发送Message的方法,调用sendEmptyMessage系统会自动构建一个message并且设置what参数然后发送。
//查看Handler的post方法的源码,调用的还是sendMessageDelayed方法
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
//post方法里调用此方法将Runnable封装为Message供Handler发送
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
//sendMessageDelayed最终还是调用sendMessageAtTime
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
//先判断消息队列是否存在,不存在则抛出异常,存在则enqueueMessage方法将消息存入消息队列里
public boolean sendMessageAtTime(@NonNull 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);
}
(5)hasMessages(int what);hasMessages(int what, @Nullable Object object);
检查消息队列是否存在对应的Message,实际应用场景用于判断前一个what的Message是否执行完毕,再进行后续的操作。
(6)removeCallbacks(Runnable r);removeCallbacks(Runnable r, Object token);removeMessages(int what); removeMessages(int what, Object object) ;
删除消息队列中的指定消息,已经在执行的消息不能被删除。
(7)removeCallbacksAndMessages(Object token);删除所有的消息(包括已发送的)和回调,当传的token为null时,直接删除Handler里的所有消息和回调。
原文:Remove any pending posts of callbacks and sent messages whose obj is token. If token is null, all callbacks and messages will be removed.
注意的是,Handler的生命周期与Activity并不一致,如果Activity要被系统销毁,但Activity中的Handler还有消息要处理,此时会造成阻塞Handler无法释放,同时Activity也无法正常释放,造成内存溢出。
此时可以调用Handler.removeCallbacksAndMessages(null),将Handler中所有的消息和回调都删除,避免Handler阻塞造成的内存溢出。
---------------------------------------------------------------------------------------------------------------------------------------------
3,MessageQueue
参考:
https://www.jianshu.com/p/8c829dc15950
https://blog.csdn.net/nmyangmo/article/details/82260616
https://haokan.baidu.com/v?vid=11527397145831332654&pd=bjh&fr=bjhauthor&type=video
一个Handler配置一个Looper,一个Looper维护了一个MessageQueue,MessageQueue(消息队列)如同一个集合存储Handler传入的Message消息,每插入一个Message都会根据message的触发时间进行重新排序,Handler的handleMessage方法处理触发时间最早的message。
一个线程只能有一个Looper和一个MessageQueue,但可以有多个Handler,下面的代码以在子线程中创建Handler为例做说明:
class MyThread extends Thread {
private Handler handler;//与该子线程关联的Handler
private Handler handler2;//与该子线程关联的Handler
public void run() {
Looper.prepare();//创建该子线程的Looper
handler = new Handler(){//默认与当前线程关联
public void handleMessage(android.os.Message msg) {
Log.d("当前子线程是----->", Thread.currentThread()+"");
};
};
//可以创建多个Handler
handler2 = new Handler(){//默认与当前线程关联
public void handleMessage(android.os.Message msg) {
Log.d("当前子线程是----->", Thread.currentThread()+"");
};
};
Looper.loop();//调用了该方法才能不断循环取出消息
}
}
从代码中可以看到MyThread里可以生成多个Handler,但Looper和MessageQueue在一个线程中却都只有一个。在MyThread中要正常使用Handler的功能,首先要Looper.perpare()创建适用于MyThread的Looper,同时Handler调用无参的构造方法,(无参构造器默认与当前线程相关联),最后不能忘记Looper.loop(),这样Lopper才能循环轮询消息队列里的消息。
(1)MessageQueue.enqueueMessage,添加Message进消息队列
当Handler调用sendMessage(sendMessageDelayed等)方法时,大致会由sendMessage-->sendMessageDelayed-->sendMessageAtTime-->enqueueMessage,并最终指向MessageQueue.enqueueMessage方法,此方法为将Handler传递过来的消息插入到指定的消息队列里等待处理,源码如下:
boolean enqueueMessage(Message msg, long when) {
//msg.target就是发送此消息的Handler,如果为空,抛出异常
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
//判断是否有相同的Message正被使用,如果是也不再执行下一步
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
//mQuitting表示此消息已被消息队列放弃,消息被回收
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();//标记自己正被使用
msg.when = when;//指定消息的触发时间,但实际处理消息的时间可能更晚
//mMessagess是消息队列里的第一个元素(头元素)
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
//如果此队列中头部元素是null(空的队列,一般是第一次),或者此消息不是延时的消息,则此消息需要被立即处理,
//此时会将这个消息作为新的头部元素,并将此消息的next指向旧的头部元素,然后判断如果Looper获取消息的线程如果是阻塞状态则唤醒它,让它立刻去拿消息处理
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//如果此消息是延时的消息,则将其添加到队列中,原理就是链表的添加新元素,按照when,也就是延迟的时间来插入的,延迟的时间越长,越靠后,
//这样就得到一条有序的延时消息链表,取出消息的时候,延迟时间越小的,就被先获取了。插入延时消息不需要唤醒Looper线程。
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; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {//唤醒线程
nativeWake(mPtr);
}
}
return true;
}
整个方法的流程:先判断Message关联的Handler是否为空,而且判断这个Message是否以备使用,如果都正常再进入synchronized中的代码,先判断是否被消息队列放弃了,没有放弃再标记自己正处于被使用的状态,Message是一个链表类型,Message.next表示在它后面的Message对象,mMessagess是消息队列里的第一个元素。
if (p == null || when == 0 || when < p.when) {这句话的意思是,如果mMessagess为空或者传进来的msg不是延时消息或者msg的延时期间比mMessagess的还要短,将msg作为消息队列里第一个元素,原先的mMessagess放在msg的后面(msg.next = p),needWake = mBlocked判断Looper的获取消息的线程是否阻塞,如果阻塞则立即唤醒去取消息。
在else的分支里,for循环里,循环取出前一个和后一个Message元素(prev和p),如果后一个元素p为空或者msg的when比p的延时时间when靠前,则退出循环,将msg插入到前一个消息prev的后面(next),将后一个消息p插入到msg的后面(next)。
所以MessageQueue的enqueueMessage方法作用:插入Message消息到消息队列里;唤醒Looper中的等待线程处理消息。
(2)MessageQueue.next,取出消息,源码:
Message next() {
//如果Looper已退出并被释放,则return null,
//如果系统尝试重启一个退出后不支持重启的Looper,就可能发生这样的情况
final long ptr = mPtr;
//mPtr是MessageQueue的一个long型成员变量,关联的是一个在C++层的MessageQueue,
//阻塞操作就是通过底层的这个MessageQueue来操作的;当队列被放弃的时候其变为0。
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//阻塞方法,主要是通过native层的epoll监听文件描述符的写入事件来实现的。
//如果nextPollTimeoutMillis=-1,一直阻塞不会超时。
//如果nextPollTimeoutMillis=0,不会阻塞,立即返回。
//如果nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒(超时),如果期间有程序唤醒会立即返回。
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
//尝试检索下一条消息,如果找到就返回之。
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
//msg.target == null表示此消息为消息屏障(通过postSyncBarrier方法发送来的)
//如果msg被一个屏障阻止运行,在消息队列里找到下一个异步类型消息续接上运行
//同时所有同步消息都将忽略(平常发送的一般都是同步消息)
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.
//下一条消息未准备好,设置超时时间当它准备就绪时可以被唤醒
//当前时间还未到msg的触发时间,则设置阻塞时间nextPollTimeoutMillis
//进入下次循环时调用nativePollOnce方法进行阻塞直到触发时间
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//正常取出消息,mBlocked=false表示不进行阻塞
mBlocked = false;
//msg被取出等待处理
if (prevMsg != null) {
//如果有前一个消息,把msg的next链接为prevMsg的next
prevMsg.next = msg.next;
} else {
//如果没有前一个消息,说明这个msg是队列头部消息,它被处理后,它的next就是队列的头部消息
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();//标记msg正被使用中
return msg;
}
} else {
//没有msg,则一直阻塞线程,直到被主动唤醒,此时只会处理不紧急的任务(IdleHandler)
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
//所有被挂起的message都被处理完后,如果msg已从队列中移除则处理掉
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.
//没有Message且没有非紧急任务时,设置阻塞,跳出本次循环
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; //释放对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;//重设非紧急任务数量为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.
//当调起一个非紧急任务时,一个新的message可能已经被传递进来了,此时返回查找等待被处理的message而不再继续等待
nextPollTimeoutMillis = 0;
}
}
从源码看出next的方法大体的流程如下:
1,首次进入方法或消息队列里的消息都被执行完时,nextPollTimeoutMillis赋值为-1,然后处理非紧急任务IdleHandler,再次进入循环时,线程被阻塞直到被外部主动唤醒(插入消息后根据消息类型决定是否需要唤醒)。
2,如果消息队列里的头部消息是消息屏障,while循环里找出异步消息然后执行,同时放弃后面的同步消息。
3,如果取出的msg消息还未到触发时间,则赋值nextPollTimeoutMillis = 延时的时间,阻塞线程直到msg的触发时间唤醒线程处理msg消息。
4,如果消息需要即时处理或触发时间已到,取出此消息给Looper处理。
*********************************************************************************************************************************
分析enqueueMessage和next方法的源码得出结论:
消息的入列和出列是生产--消费模式,Looper调用loop方法在对应的线程中不断地用next取出消息,而Handler关联的线程通过enqueueMessage插入消息到消息队列里,同时为了防止多个线程对消息队列进行操作,enqueueMessage和next方法都使用了synchronized (this)同步锁机制,this就是MessageQueue对象,确保操作的唯一性。
问题1:Handler.sendMessageDelayed是如何实现延迟的?
从next方法里得出,如果取出的消息还未到触发时间,计算并赋值nextPollTimeoutMillis,阻塞线程直到消息的触发时间,这段时间内调用nativePollOnce(ptr, nextPollTimeoutMillis)这个本地C++方法,调用C++底层并最终会通过Linux的epoll监听文件描述符的写入事件来实现延迟处理消息。
问题2:Looper.loop是一个死循环,拿不到需要处理的Message就会阻塞,那在UI线程中为什么不会导致ANR?
此问题解答来自:https://www.jianshu.com/p/8c829dc15950
APP的入口ActivityThread的main方法源码:
public static void main(String[] args) {
...
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
主线程里Looper通过loop开启死循环不断地接收和处理消息,如果没有这个死循环,主线程处理完手头的事情就退出,app也跟着崩溃。
主线程的Looper负责从消息队列中读取消息,如果队列里没有消息则阻塞进入休眠状态,如果子线程传入消息则唤醒主线程进行读取操作,读取完毕再次进入阻塞休眠状态。主线程不进行耗时操作,耗时操作都交于子线程处理,主线程的Looper循环会尽量减小对cpu性能的消耗。
从https://blog.csdn.net/weixin_36289667/article/details/107960145里抄录这么一段话做理解
ActivityThread的 main 方法的主要作用就是做消息循环,一旦退出消息循环,主线程运行完毕,那么应用也就退出了。Android是事件驱动的,在Looper.loop()中循环分发事件,而Activity的生命周期都依靠于主线程的 Loop.loop() 来调度,所以可想而知它的存活周期和 Activity 也是一致的。当没有事件需要处理时,主线程就会阻塞;当子线程往消息队列发送消息,并且往管道文件写数据时,主线程就被唤醒。真正会卡死主线程的操作是在执行回调方法 onCreate/onStart/onResume 等操作的时间过长,导致掉帧,甚至发生ANR,looper.loop() 本身不会导致应用卡死。
主线程Looper从消息队列读取消息,当读完所有消息时,主线程阻塞。子线程往消息队列发送消息,并且往管道文件写数据,主线程即被唤醒,从管道文件读取数据,主线程被唤醒只是为了读取消息,当消息读取完毕,再次睡眠。因此loop的循环并不会对CPU性能有过多的消耗。
---------------------------------------------------------------------------------------------------------------------------------------------
4,Looper
参考:https://blog.csdn.net/linghaidong/article/details/54709303
作为安卓线程间消息通信的机制,Looper管理消息队列,实现多线程间消息的互通,用户无需考虑互斥加锁的问题。Looper循环遍历消息队列中的消息,取出消息交于Handler处理。
ActivityThread类
public static void main(String[] args) {
...
//在ActivityThread的main方法里创建主线程的Looper
Looper.prepareMainLooper();
...
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
//主线程的Looper调用loop方法循环遍历消息队列,当有消息时分发给对应Handler处理,没有消息时阻塞处于休眠状态,等待被唤醒
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
Activity类
public void recreate() {
if (mParent != null) {
throw new IllegalStateException("Can only be called on top-level activity");
}
//判断Activity的Looper是不是主线程的Looper,不是抛出异常
if (Looper.myLooper() != mMainThread.getLooper()) {
throw new IllegalStateException("Must be called from main thread");
}
mMainThread.scheduleRelaunchActivity(mToken);
}
主线程ActivityThread在main方法里调用Looper.prepareMainLooper()初始化一个专供主线程使用的Looper,不停循环遍历消息队列,有消息时交给对应Handler处理,没有消息时阻塞处于休眠,等待被唤醒。查看Activity的源代码recreate方法里会判断当前Activity的looper是不是主线程的looper,如果不是会抛出异常,说明Activity用的是主线程的looper。整个app运行中的组件,Activity,service等等运行时的各种操作都是在主线程中调用looper循环遍历消息队列操作更新的,所以耗时操作不要在主线程里操作就是防止ANR。
Looper的prepare方法源码:
//本地化存储机制,一个线程只能对应一个
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static void prepare() {
prepare(true);
}
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));
}
//Looper绑定当前所在线程,生成属于自己的消息队列
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
参考:https://blog.csdn.net/sun927/article/details/51031268 https://blog.csdn.net/ly502541243/article/details/87475229
为什么一个线程只能有一个Looper?
ThreadLocal实现了线程本地存储。所有线程共享同一个ThreadLocal对象,但不同线程仅能访问与其线程相关联的值,一个线程修改ThreadLocal对象对其他线程没有影响。ThreadLocal保证每个线程都有自己的Looper,同时为了确保唯一性,在prepare方法里会先判断ThreadLocal对象是否已创建,如果已创建则会抛出异常提示"一个线程只能生成一个Looper",sThreadLocal.set(new Looper(quitAllowed))绑定当前线程,生成属于自己的消息队列。
所以在子线程中创建Handler时为什么要调用Looper.prepare()?
子线程里没有Looper,子线程中创建Handler不调用Looper.prepare,报错如下:
Uncaught handler: thread Thread-8 exiting due to uncaught exception
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
调用Looper.prepare,会创建一个新的消息队列,并绑定这个子线程。调用Looper.loop方法开始循环遍历消息队列里的消息。
public static void loop() {
...
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);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
msg.recycleUnchecked();
}
}
查看loop方法的源码,looper开启一个for循环,不停遍历当前的消息队列,只要找到了消息,就调用Handler的dispatchMessage方法处理消息,然后消息msg进行自我回收。
Handler的dispatchMessage方法源码:
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
这个方法大意如此:1,如果msg是通过post传入的Runnable类型,因为post最终调用的还是send方法,runnable当作Message的Callback使用,所以msg.callback!= null,直接运行runnable的run方法;2,if (mCallback != null)表示如果构造Handler时有传入Callback参数,则调用Handler的callback处理消息;3,如果前面两种情况都不触发,即通过Handler mHandler = new Handler() { public boolean handleMessage(@NonNull Message msg) {} }创建Handler,此时将消息msg传回Handler的handleMessage方法中供开发者自行处理。