Handler的不完全解析

2020-10-27  本文已影响0人  慕尼黑凌晨四点

Handler是什么

Handler机制和原理

图解Handler运行机制

大致流程如图,首先初始化一个Handle(callback),后调用sendMsg(msg)方法【post方法最后也是调用的sendMsg()】,传入Message对象,将这个handle赋值给msg.target。然后调用queue.enqueueMessage(msg)将message入队。

另一方面,另外一个线程(通常为主线程)会调用Looper.loop()方法循环从Message Queue中读取数据【此方法在ActivityThread类中已被调用,所以主线程不用再次调用;若为其他线程则需要自己手动调用一次了】。获取到数据后调用msg.target.dispatchMessage(msg);方法。target就是handler,这里就跳回到了Handle所在线程中来。

    public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

Handler 在处理消息时会先查看 Message 是否有 callback,有则优先交由 Message 的 callback 处理消息,没有的话再去看看Handler 有没有 callback,如果也没有才会交由 handleMessage() 这个方法执行

实现了线程的切换

这里Handle和Lopper一定在一个线程,因为初始化handle之前必须要执行Looper.prepare()方法,【因为handle中要持有Looper对象,因为messageQueue是从Looper对象中获取到的】。

之所以我们平常没写Looper.prepare()也能运行,是因为ActivityThread中已经调用了该方法。

Handler是怎么切换线程的

上面:point_up:已经写了。看了上文还不懂可能我表达出了问题。

Handler内存泄露的原因?

直接在MainActivity中匿名内部类 new Handle(){} 或者创建非静态内部类的时候会有warning警告。

image-20201027122230172.png image-20201027122340874.png

且给出了建议,加上static,改为静态内部类。

原因是:1. 非静态内部类(或匿名内部类)【Handler】会一直持有外部类(这里的MainActivity)的引用。

  1. MessageQueue存储了Message,而Messagetarget属性为handler对象,handler又持有的Activity的对象。
  2. 所以,当Activiy要销毁的时候,MessageQueue中还存在未处理完的Message,leaks...

怎么处理handler的内存泄露;

1.静态内部类 + 弱引用

也就是上图中Android studio 建议的方案。

同时,加上WeakReference弱引用持有Activity实例,GC回收时发现了这个弱引用对象便会将其回收,从而避免内存泄露。

详细代码参考Android 内存泄露:详解 Handler 内存泄露的原因

2 .当外部类结束生命周期时,清理Handle或Looper

@Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
        // 外部类Activity生命周期结束时,同时清空消息队列 & 结束Handler生命周期
    }
@Override
protected void onDestroy() {
    super.onDestroy();
    getMainLooper().quitSafely();
}

Looper死循环为什么不会导致应用卡死?

Android应用程序的主线程在进入消息循环过程前,会在创建一个新binder线程。

public static void main(String[] args) {
    .....
    //创建Looper和MessageQueue对象,用于处理主线程的消息...
    Looper.prepareMainLooper();
    
    //创建ActivityThread对象
    ActivityThread thread = new ActivityThread();
    
    //建立Binder通道 (创建新线程)
    thread.attach(false, startSeq);
    
     //消息循环运行
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

app一直开着,Looper.loop就会一直循环【就是为了保障app的运行啊】,由于有Binder线程的存在,所以Looper中会收到Message,当收到不同的Message时就会采取不同的措施:

ActivityThread中的handleMessage方法

包括所谓BIND_APPLICATION、LAUNCH_ACTIVITY等一系列方法,都是写在这里,通过接收到的消息不同,进行相应的操作。

附上Activity创建流程图一张:

Activity创建流程图

参考 :

知乎:https://www.zhihu.com/question/34652589

简书:Android 内存泄露:详解 Handler 内存泄露的原因

简书:Activity启动流程

相关文章

Handler 10问,你顶的住吗?

上一篇 下一篇

猜你喜欢

热点阅读