一、为什么要学习Handler消息机制:
感觉Android App都是通过Handler消息机制驱动起来的, 比如activity的生命周期、View的绘制/刷新等
二、总结
Handler: 发送和处理消息 ; Looper: 轮询消息队列, 通过loop()方法调用MessageQueue.next()方法,取出消息交给Handler处理 ; MessageQueue: 优先级队列,存储消息,next()函数真正取消息得地方; Message: 消息体 , 通讯/切换的根本, 因为Message是可以在线程间共享的
整个工作流程感觉和传送带工作流程比较像:
Handler(工人)把Message(包裹)从别的地方(可以是当前线程也可以是子线程)放到MessageQueue(传送带)上, Looper(电机)通过loop()轮询,从MessageQueue中取出消息,交给Handler的dispatchMessage处理
Handler消息机制流程.png
三、Handler:
1.作用以及使用方法: 线程间通讯、延时任务/循环任务、消息处理
1、线程间通讯: 主线程中创建Handler实例: MyHandler mHandler = new MyHandler () , 子线程中执行完成任务后通过mHandler.sendMessage等方法发送消息, 然后Handler在自己的handleMessage方法中处理消息,这样就完成了把消息从子线程发送到了主线程
2、延时任务/循环任务: sendMessageDelayed(Message msg, long delayMillis)/postDelayed(Runnable r, long delayMillis)等方法
3、消息处理: dispatchMessage ->runnable.run() 、callBack回调、重写handleMessage()
2.相关函数:
1、发送消息/任务: sendMessage(Message msg)/post(Runnable run) -> sendMessageAtTime(Message msg, long uptimeMillis) ->enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)
2、处理Looper取出来的消息: dispatchMessage(Message msg), 感觉像责任链模式:
如果消息的callBack(一个Runnable)不为空,那就直接调用callBack的run(0方法, 如果msg.callBack为空, 再看new Handler 的时候是否设置CallBack, 如果设置了就调用CallBack的handleMessage方法, 如果前2者都为空, 那么再调用Handler的方法: handleMessage
public void dispatchMessage(Message msg) {
if (msg.callback != null) {//如果消息设置了callBack(就是一个Runnable)
handleCallback(msg);
} else {
if (mCallback != null) {//如果new Handler的时候设置了CallBack回调
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg); //如果前2者都没
}
}
3、移除消息: removeMessages(int what): 移除msg.what = what的消息,可能是多个;
removeCallbacksAndMessages(Object token): 移除msg.obj = token的消息, 如果token = null,那么移除所有消息和callBack;
4、runWithScissors(final Runnable r, long timeout): 同步执行代码,意思是等待Runnable中的代码执行完成后, 后面的代码才会执行;
应用场景: 子线程向主线发送一个任务,等待主线程任务执行完成后,再继续执行
3.注意事项
1、内存泄漏:
(1).在使用Handler的时候, 最好用静态内部类+弱引用的方式(静态内部类不持有外部类引用), 并且在activity/fragment等要销毁的候:removeCallbacksAndMessages(nulll), 避免内存泄漏
private MyHandler mHandler =new MyHandler(TestHandlerActivity.this)
private static class MyHandler extend Handler{
private final WeakReference<TestHandlerActivity> mWeakReference;
MyHandler(TestHandlerActivity activity){
mWeakReference = new WeakReference<>(activity)
}
@OverRide
public void handleMessage(Message msg){
TestHandlerActivity activity = mWeakReference.get();
if(activity!=null){
//doing something
}
}
}
(2).为何会内存泄漏: Message.target = handler > handler又持有activity的引用(内部类默认持有外部类引用), 如果activity关闭的时候, Message还在MessageQueue里面(比如发送的延时任务还未执行), 那么target就一直在, target在activity就得不到释放
public boolean sendMessageAtTime(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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;//把this handler赋值给了Message
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
四、Looper
1.作用和使用方法:
1.作用: 通过loop()方法死循环轮询消息列表, 取出可以执行的消息交给Handler处理
2.使用方法: 先调用Looper.prepare() 然后 new Handler , 然后调用Looper.loop()开始轮询
private lateinit var mHandler: Handler
private fun startLoopAtAsyncThread() {
Thread {
Looper.prepare()
mHandler = object : Handler(Looper.myLooper()){
override fun handleMessage(msg: Message?) {
super.handleMessage(msg)
//子线程处理消息
}
}
Looper.loop()
}.start()
mHandler.sendEmptyMessage(11)
}
2.主要函数
1.构造函数: 私有的别的地方不能直接new, 同时创建了一个消息队列MessageQueue, 然后和当前线程进行了绑定
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
2.prepare()函数: 只能调用一次,保证一个线程只有一个Looper; 需要先于loop()调用 ; 通过ThreadLocal保存, 保证Looper是和当前线程有关的, 和其他线程无关(线程隔离)
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {//判断下如果有Looper了, 那么就抛出错误
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
3.loop()函数:
public static void loop() {
final Looper me = myLooper();
if (me == null) {//1.如果还没有调用prepare,就抛错
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
for (;;) {//2.死循环轮询
Message msg = queue.next(); // might block 3.取消息得时候大部分是阻塞的
if (msg == null) {//4.调用quit()之后, 结束轮询,线程结束
// No message indicates that the message queue is quitting.
return;
}
//5. 1、通过给Looper设置Printer 可以监听每个消息的处理时间, 这就是BlockCanary的原理
//Looper.getMainLooper().setMessageLogging()
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
try {
msg.target.dispatchMessage(msg);//6. 把消息交给Handler处理的地方
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logging != null) {//5.2、日志打印监听结束的地方
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
//7. 享元模式, 复用内存的地方
msg.recycleUnchecked();
}
}
4.quit()函数: 中断loop轮询, 线程销毁; 流程是: 调用MessageQueue的quit(), 设置mQuitting标志位为true, 如果为true, 那么MessageQueue的next()返回一个null的消息, Looper loop的时候,如果判断取到的消息为null, 那么直接return, 退出死循环
public void quit() {
mQueue.quit(false);
}
Message next() {
if (mQuitting) {
dispose();
return null;
}
}
5.prepareMainLooper() 、getMainLooper(): 在ActivityThread的main()方法中调用了,所以我们平时使用主线程的Handler的时候,不用先调用Looper.prepare();
作用是: App启动的时候就准备好主线程, 通过这个主线程的Handler可以驱动整个app运行
public static void main(String[] args) {
Looper.prepareMainLooper();//1. 准备好主线的Looper
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);// 2. 感觉是开始的地方
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();//3. 开始轮询
throw new RuntimeException("Main thread loop unexpectedly exited");
}
五、MessageQueue
1.作用: 存/取消息的一个优先级队列
2.主要函数:
1.存消息: enqueueMessage(Message msg, long when)
2.取消息: Message next()
3.如何保证线程安全: 存/取消息的时候都加: synchronized (this)
这个锁说明: 所有调用对一个MessageQueue对象的线程都是互斥的, 也就是说存/取消息的时候,每次都只能执行一个线程的请求, 也就说是: 存/取动作都只能一个个来;
next()函数中也要加锁, 是为了保证在取的时候, 不会被别的地方存消息进来, 这样就能保证取的消息不混乱
4.IdleHandler: 当MessageQueue.next()取消息的时候,发现现在没有可以执行的消息,也就是Handler空闲的时候, 会回调这个类
应用场景:
1.Activity启动优化, onCreate/onResume/onStart中非必要且耗时较短的操作,可以放在IdleHandler中执行
Looper.myLooper()?.queue?.addIdleHandler {
LogUtil.i("------IdleHandler回调------------")
//返回false 表示 只执行一次, true表示 每次空闲的时候都会回调
false
}
5.消息机制同步屏障
1.什么是同步屏障: 阻碍同步消息, 让异步消息优先通过,得到处理, 我理解的是: 消息队列设置一个障碍, 如果遇到这个障碍的时候, 优先取障碍后的下一个异步消息
2.如何开启/关闭同步屏障: MessageQueue.postSyncBarrier() / removeSyncBarrier(int token)
3.源码理解:
postSyncBarrier()的时候会直接在队列里面加一个target==null 的消息, 然后调用next()的时候,会判断如果当前消息的target == null , 就优先取队列里面下一个异步消息(isAsynchronous)
Message next() {
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());
}
}
4.应用场景:
UI更新的相关消息都是异步消息,需要优先处理
View的draw、invalidate、requestLayout等方法都会调到:
ViewRootImpl的scheduleTraversals:
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();//1.开启同步屏障
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
//编舞者Choreographer: mChoreographer.postCallback会走到这里
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
if (DEBUG_FRAMES) {
Log.d(TAG, "PostCallback: type=" + callbackType
+ ", action=" + action + ", token=" + token
+ ", delayMillis=" + delayMillis);
}
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
if (dueTime <= now) {
scheduleFrameLocked(now);
} else {//2.发送异步消息 msg.setAsynchronous(true);
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
void unscheduleTraversals() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);//3.移除掉同步屏障
mChoreographer.removeCallbacks(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
六、Handler消息机制常见问题
1:一个线程有几个 Handler?
2:一个线程有几个 Looper?如何保证?
3:Handler 内存泄漏原因? 为什么其他的内部类没有说过有这个问题?
4:为何主线程可以new Handler?如果想要在子线程中new Handler 要做些什么准备?
5:子线程中维护的Looper,消息队列无消息的时候的处理方案是什么?有什么用?
6:既然可以存在多个 Handler 往 MessageQueue 中添加数据(发消息时各个 Handler 可能处于不同线程),那它内部是如何确保线程安全的?
7:我们使用 Message 时应该如何创建它?
8:Looper死循环为什么不会导致应用卡死
七、HandlerThread
1.什么是HandlerThread: 创建好了Looper的一个子线程
2.作用:
(1)方便使用: 创建好了Looper,
(2)保证了获取looper的时候的安全
synchronized 锁住mLooper的赋值, getLooper的时候也用synchronized锁住,while循环一直等待,
只有当mLooper不为null的时候才返回, wait() 会释放锁, notifyAll{}不释放锁
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {//1.赋值的时候锁住, 别人getLooper会等待这里执行完成后才返回
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
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) {//如果looper还是null, 那么就等创建好了再返回
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
3.使用
val handlerThreadOne = HandlerThread("handlerThreadOne")
handlerThreadOne.start()
//获取子线程one的handler, handlerThread 只有在start之后,looper才会准备好, 因为Looper是
//在run方法里面准备的
val threadOneHandler = ThreadOneHandler(handlerThreadOne.looper)
private class ThreadOneHandler(looper: Looper) : Handler(looper) {
override fun handleMessage(msg: Message?) {
super.handleMessage(msg)
msg?.run {
LogUtil.i("线程: ${Thread.currentThread().name} 接收到了消息: msg.what = ${msg.what}")
}
}
}
八、IntentService
1.什么是IntentService: 处理耗时任务的service, 处理完成后自动销毁;
处理串行任务: 多次startService任务会依次串行执行,然后才会自动销毁(stop()方法的参数startId)
2.使用:
val intentOne = Intent(applicationContext, MyIntentService::class.java)
intentOne.action = ACTION_ONE
startService(intentOne)
class MyIntentService(name: String) : IntentService(name) {
//如果没有0参数的构造,会报错
constructor() : this("default")
override fun onHandleIntent(intent: Intent?) {
intent?.apply {
val action = intent.action
if (!TextUtils.isEmpty(intent.action)) {
when (action) {
ACTION_ONE -> {//第一个intent
Thread.sleep(5000)
LogUtil.i("第一个intent任务完成, 线程: " + Thread.currentThread().name)
}
ACTION_TWO -> {//第二个任务
Thread.sleep(4000)
LogUtil.i("第二个intent任务完成, 线程: " + Thread.currentThread().name)
}
ACTION_THREE -> {//第二个任务
Thread.sleep(3000)
LogUtil.i("第三个intent任务完成, 线程: " + Thread.currentThread().name)
}
}
}
}
}
override fun onDestroy() {
super.onDestroy()
LogUtil.i("onDestroy")
}
}
网友评论