Android的消息机制主要指的是Handler的消息机制。其主要由Message、MessageQueue、Looper、Handler组成。
- ThreadLocal
理解ThreadLocal对于理解Handler的消息机制有很大的帮助。它是线程内部的一个数据存储类。
使用场景:
1.某些数据是以线程为作用域且不同线程具有不同数据副本的时候(Looper)
2.使用场景是复杂逻辑下对象传递
ThreadLoca的使用:
final ThreadLocal<Boolean> mThreadLocal = new ThreadLocal<>();
mThreadLocal.set(true);//UI Thread
Log.i(TAG, "[mainThread]mThreadLocal="+mThreadLocal.get());
new Thread("Thread#1") {
@Override
public void run() {
mThreadLocal.set(true);
Log.i(TAG, "[Thread#1]mThreadLocal="+mThreadLocal.get());
}
}.start();
new Thread("Thread#2") {
@Override
public void run() {
Log.i(TAG, "[Thread#2]mThreadLocal="+mThreadLocal.get());
}
}.start();
2.消息队列的工作原理
MessageQueue只要包括俩个操作,插入和读取消息,enqueue()、next()。内部是通过单链表实现的。调用enqueue()方法实现消息的插入,next()方法返回一条消息,并从消息队列移除该消息。next 方法是一个无限循环的方法,如果消息队列中没有消息的话,next方法会一直阻塞在这里。当有消息时,将其从单列表总移除。
3.Looper的工作原理
Looper在Android的消息机制中扮演着消息循环的角色,在它的构造方法中会创建一个MessageQueue,然后将当前线程保存起来。
通过Looper.prepare()创建一个Looper,Looper.loop开启消息循环。当任务处理完成后,应调用Looper.quit/quitSafely退出循环。二者的却别是quit会直接退出Looper,后者会把消息队列中的已有消息处理完毕后安全的退出。如果不退出,则该线程就会一直处于等待状态,当Looper退出后,该线程就会立刻停止。
loop方法是一个死循环,只有当next方法返回null时,才会退出循环。当调用Looper.quit方法时,会调用MessageQueue的quit/quitSafely方法来通知消息队列退出,则此时next方法会返回null。loop会调用MessageQueue的next获取新消息,next方法是一个阻塞操作,当没有消息时,next方法会一直阻塞在哪里,这也导致loop方法一直阻塞在那里。最终会调用msg.target.dispatchMesage方法处理消息。Handler的dispatchMessage方法是在创建Handler时所使用的Looper中执行的,这样就把代码逻辑切换到指定的线程中去执行了。
4.Handler的工作原理
Handler通过post或send方法发送消息,其调用过程是sendMessage->sendMessageDelayed->sendMessageAtTime,在sendMessageAtTime方法中会调用MessageQueue的enqueue方法将消息加入到消息队列中。MessageQueue的next方法就会被调用,Looper收到消息后就会处理,最终消息交由Handler去处理,即Handler的dispatchMessage方法会被调用。
这里涉及到一个使用Handler的方式:
1.派生Handler的一个子类并重写其handleMessage方法
2.通过实现CallBack接口,达到直接使用Handler,不需要派生子类
3.通过Looper构造Handler一个Handler,实现一些特殊的功能。
5.主线程的消息循环
ActivityThread的入口方法为main方法,通过Looper.prepareMainLooper()来创建。ActivityThread通过ApplicationThread和AMS进行进程间通信,AMS以进程间的通信方式完成ActivityThread的请求后会回调ApplicationThread中的Binder方法,然后ApplicationThread会向H发送消息,H收到消息后会将ApplicationThread中的逻辑切换到ActivityThread中去执行,即切换到主线程中执行。
6.为什么Android中子线程不能访问UI控件?
因为Android的主线程不是线程安全的,多线程并发访问可能会导致UI控件出现不可预期的结果,那为什么不对UI控件加入同步锁机制呢?
大致原因有二:
- 加入锁机制会使逻辑复杂
- 锁机制会降低UI访问效率,同步锁会阻塞某些线程的执行
所以Android主线程使用单线程模型来执行UI操作。
7.子线程有哪儿些更新UI的方法?
- 通过在主线程创建Handler,子线程发送消息,Handler接受并处理子线程发来的消息
- 用Activity.runOnUiThread方法
- 创建Handler,传入getMainLooper
- view.post(Runnable r)
8.解决Handler内存泄露的方法?
使用Handler为什么会造成内存泄露?
- 有延时消息时,改消息会一直保存在主线程的消息队列里,并且影响系统对Activity的回收,当Activity销毁时要移除延时消息
- 匿名内部类导致的内存泄露,使用静态内部类,并且对上下文或Activity使用弱引用。
网友评论