在 android 开发中,经常会在子线程中进行一些操作,当操作完后会通过 handler 发送一些数据给主线程,通知主线程做响应的操作。原理:子线程 handler 主线程,其实构成了线程模型中的经典问题,生产者-消费者模型(子线程往消息队列中放消息,主线程从消息队列中取消息)。生产者-消费者模型:生产者和消费者在同一时间段内共用一个存储空间,生产者往存储空间中添加数据,消费者从存储空间中取走数据。
1 Handler 源码分析
1.1 handler 机制的相关类
Handler: 发送和接收消息;
Looper: 用于轮询消息队列,一个线程只能有一个Looper;
Message: 消息实体;
MessageQueue: 消息队列,用于存储消息和管理消息。
1.2 创建 Looper
创建 Looper 的方法是调用 Looper.prepare() 方法。在 ActivtyThread 中的 main 方法为我们 prepare 了
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
//其他代码省略...
Looper.prepareMainLooper(); //初始化Looper以及MessageQueue
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop(); //开始轮循操作
throw new RuntimeException("Main thread loop unexpectedly exited");
}
Looper.prepareMainLopper()
public static void prepareMainLooper() {
prepare(false);//调用prepare(), 消息队列不可以quit
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been
prepared.");
}
sMainLooper = myLooper();
}
}
prepare 有两个重载的方法,主要看 prepare(boolean quitAllowed),quitAllowed 的作用是创建 MessageQueue 时标识消息队列是否可以销毁,主线程不可以销毁。
public static void prepare() {
prepare(true);//消息队列可以quit
}
// 私有的构造函数
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {//不为空表示当前线程已经创建了Looper
throw new RuntimeException("Only one Looper may be created per thread");
//每个线程只能创建一个Looper
}
// ThreadLocal 多线程中重要的知识点,线程上下文的存储变量
sThreadLocal.set(new Looper(quitAllowed));//创建Looper并设置给sThreadLocal,这样get的
时候就不会为null了
}
注意:一个线程只要一个Looper。如何保证只有一个?
只有一个ThreadLocal(源码里为final),就保证只有一个 Looper。ThreadLocalMap里的<key, value> ----> <唯一的ThreadLocal, Looper>。为了保证 ThreadLocalMap.set(value) 时,value 不会被覆盖(即Looper不会改变),会先进行上面代码的 if 操作,if (sThreadLocal.get() != null) , 不为空表示当前线程已经创建了Looper,然后直接抛异常结束prepare。如果 if 不成立,则 new Looper()。
1.3 创建MessageQueue以及Looper与当前线程的绑定
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);//创建了MessageQueue
mThread = Thread.currentThread(); //当前线程的绑定
}
MessageQueue(boolean quitAllowed) {
//mQuitAllowed决定队列是否可以销毁 主线程的队列不可以被销毁需要传入false, 在MessageQueue的 quit()方法
//就不贴源码了
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
1.4 Looper.loop()
同时是在 main 方法中 Looper.prepareMainLooper() 后 Looper.loop() 开始轮询
public static void loop() {
final Looper me = myLooper();//里面调用了sThreadLocal.get()获得刚才创建的Looper对象
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this
thread.");
}//如果Looper为空则会抛出异常
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
if (msg == null) {
//由于刚创建MessageQueue就开始轮询,队列里是没有消息的,等到Handler sendMessage
enqueueMessage后
//队列里才有消息
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);//msg.target就是绑定的Handler,详见后面Message的部
分,Handler开始
//后面代码省略.....
msg.recycleUnchecked();
}
}
1.5 创建 Handler
最常见的创建 handler 的方式
Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
在内部调用 this(null, false);
public Handler(Callback callback, boolean async) {
//前面省略
mLooper = Looper.myLooper();//获取Looper,**注意不是创建Looper**!
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;//创建消息队列MessageQueue
mCallback = callback; //初始化了回调接口
mAsynchronous = async;
}
//这是Handler中定义的ThreadLocal ThreadLocal主要解多线程并发的问题
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
1.6 创建 Message
public static Message obtain(Handler h) {
Message m = obtain();//调用重载的obtain方法
m.target = h;//并绑定的创建Message对象的handler
return m;
}
public static Message obtain() {
synchronized (sPoolSync) {//sPoolSync是一个Object对象,用来同步保证线程安全
if (sPool != null) {
//sPool是就是handler dispatchMessage 后 通过recycleUnchecked回收用以复用的Message
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
1.7 Message 和 Handler 的绑定
创建 Message 的时候可以通过 Message.obtain(Handler h) 这个构造方法绑定。当然也可以在 Handler 中的 enqueueMessage() 绑定,所以发送 Message 的方法都会调用此方法队列,所以在创建 Message 的时候是可以不绑定的。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; //Message 与 Handler 绑定
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
1.8 Handler 发送消息
Handler 发送消息的重载方法很多,但是主要的只有2种。sendMessage(Message), sendMessage方法通过一些列重载方法调用,sendMessage --(调用)--> sendMessageDelayed --(调用)--> sendMessageAtTime --(调用)--> enqueueMessage(MessageQueue里),将消息保存在消息队列中,最终由 Looper 取出,交给 Handler 的 dispatchMessage 进行处理。
我们可以看到在 dispatchMessage 方法中,message 中 callback 是一个 Runable 对象,如果 msg.callback != null,则直接调用 callback 的 run 方法,否则判断 mCallBack 是否为空,mCallback 在 Handler 构造方法初始化,在主线程直接通过无参的构造方法 new 出来的为 null,所以会直接执行后面的 handleMessage() 方法。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {//callback在message的构造方法中初始化或者使用
handler.post(Runnable)时候才不为空
handleCallback(msg);
} else {
if (mCallback != null) {//mCallback是一个Callback对象,通过无参的构造方法创建出来的handler,
该属性为null,此段不执行
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);//最终执行handleMessage方法
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
1.9 Handler 处理消息
在 handleMessage(Message) 方法中,我们可以拿到 message 对象,根据不同的需求进行处理,整个 Handler 机制的流程就结束了。
handler不仅仅是用于线程间通信,它只是 handler 的一个附属功能。而 handler 正真的功能是"所以代码都是在 handler 中运行,它维持着我们 Android app 运行的整个框架"。
Handler 线程间通信的整个调度流程
handler.sendMessage(开始) --> MessageQueue.enqueueMessage(消息队列,把Message 插入消息队列) --> looper.loop() --> messageQueue.next() --> handler.dispatchMessage--> handler.handleMessage(结束)
子线程 到 主线程的切换:handler.sendEmptyMessage(子线程) -到-> handler.handleMessage(主线程)
MessageQueue: 由单链表实现的优先级队列(按时间排序)
MessageQueue 里有一个 message 对象,一个 next(Message)也是 Message 对象。
MessageQueue(fianl) 是跟随 Looper 创建的
Handler 相关面试题:
1 一个线程有几个 Handler ?
Hanler 机制只有一个,Handler 想要几个就几个(new Handler)
2 一个线程有几个 Looper ?如何保证?
略....
3 Handler 内存泄漏原因?为什么其它的内部类没有说过这个问题?
java基础:内部类会持有外部类对象(Handler 是一个内部类)。例如 RecyclerView 的 Adapter 里的 ViewHolder 也是一个内部类,它就没有内存泄漏的问题
答:与生命周期有关。
4 子线程里 new Handler 要做什么准备工作?
在子线程里准备 Looper.prepare() 和 Looper.loop()两个工作,而主线程是在程序启动时,ActivtyThread 里的 main 方法就为我们准备了。
5 子线程中维护的Looper,消息队列无消息的时候的处理方案是什么?有什么用?
调用 quit()。调用 quit 首先会唤醒线程,然后 MessageQueue 给了一个 null,最后退出 loop (结束了)
6 既然可以存在多个 Handler 往 MessageQueue 中添加数据(发消息时各个 Handler 可能处于不同线程),那它内部是如何确保线程安全的?取消息呢?
锁。
synchronized: 内置锁。可以修饰代码块、函数。上锁、开锁都是由 JVM 完成(内置到系统里面的锁 --> 内置锁)。
7 Looper 死循环为什么不会导致应用卡死?
ANR(Application Not Response)应用不响应。Android系统对于一些事件需要在一定的时间范围内完成,如果超过预定时间能未能得到有效响应或者响应时间过长,都会造成ANR
ThreadLocal 全局唯一(整个App 就一个, 由 static final 修饰),Looper 是线程唯一(一个线程一个)
线程1 --》ThreadLocalMap 1 --》<唯一的ThreadLocal, Looper1> --》MessageQueue1
线程2 --》ThreadLocalMap 2 --》<唯一的ThreadLocal, Looper2> --》MessageQueue2
享元设计模式
用于减少创建对象的数量,以减少内存占用和提高性能。可以防止内存抖动,内存复用。
Handler中message的内容置空、Activity的管理都是享元设计模式
消息机制之同步屏障 -- 架构思维
同步屏障:处理紧急的消息。
消息是根据执行时间进行先后排序,然后消息是保存在队列中,因而消息只能从队列的队头取出来。当有紧急消息时,就会将 msg.target 置为 null,当我们在轮询消息时,发现当前 msg != null && msg.target == null(如下代码), 就会遍历整个 msg 队列,找到那个紧急消息,然后处理掉。
HandlerThread 存在的意义
HandlerThread 是 Thread 的子类,就是一个线程,只是它在自己的线程里帮我们创建了 Looper
方便使用,方便初始化,方便获取线程 looper
保证了线程安全
HandlerThread 的用法:在子线程中创建一个自己的 Looper
上面代码为 HandelrThread 里的 run 方法,为我们创建一个自己的 Looper,因为它里面有写 Looper.prepare() 和 mLooper = Looper.myLooper(); 然后通过 getLooper() 方法就能获取到 HandlerThread 为我们创建的 Looper。
当 run() 方法执行的 Looper.prepare() 时,此时右调用了 getLooper() 方法,那么 mLooper 就为空,此时执行 wait() 操作,并释放锁。wait 释放锁后 run() 方法继续运行,给 mLooper 赋值为 Looper.myLooper()。然后执行 notifyAll() 唤醒其它等待的锁(是唤醒,并不代表这里的 getLooper 右继续运行),notifyAll() 并不能释放锁。当 run() 方法全部执行完后,getLooper() 方法里才又重新拿到锁继续运行。注意:run() 方法和 getLooper() 方法里是同一个锁。
IntentService 存在的意义
Service 一般用于处理后台耗时任务。
应用需求:一项任务分成几个子任务,子任务按顺序先后执行,子任务全部执行完后,这项任务才算成功。
这个需求可能用多个线程来处理,一个线程处理完一个子任务 --> 下一个线程处理完一个子任务 --> 下一个线程继续 ....
IntentService 就可以帮我们完成这个工作。而且,能够很好的管理线程,保证一个时间段里只有一个子线程处理一个子任务,而且是一个一个的完成任务,有序进行。
IntentService 的onCreate() 函数里会创建一个 HandlerThread(异步线程),然后onHandleIntent() 函数处理任务(是一个抽象函数,继承),耗时任务就在里面处理。
Service 的生命周期是后台管理的,IntentService extends Servicej, 所以IntentService 的生命周期也是后台管理的。所以 IntentService 的 onCreate() 函数是由后台调度的,以及 onStart() 函数。
走 onStart() 方法在子线程里发送一个消息。 再由 ServiceHandler 来处理 IntentService 里子线程发送的消息。
ServiceHandler 处理接收的消息,在 handleMessage里调用 onHandleIntent(我们重写)来处理消息。
所以综上,我们在使用 IntentService 时,就可以 new 一个 IntentServcie的子类,实现 onHandleIntent 来处理耗时的后台任务。
消息处理完后调用 stopSelf(),service 自动停止 --> 内存自动释放
Glide 生命周期
glide.with(context).from(url).into(imageView)
这里的 context --> fragment/activity 。当我们的 fragment/activity 生命周期结束时,Glide 也结束。