0
点赞
收藏
分享

微信扫一扫

Handler的不完全解析

芭芭蘑菇 2021-09-21 阅读 50

Handler是什么

  • 个人理解:Handler是进程间进行通信的一种方式。

  • 官方:

    Handler允许你发送Message并处理Runnable对象,通过MessageQueue的关联。每个Handler实例和线程,和线程的MessageQueue相关联。当你创建Handler的时候他会绑定到Looper。他会将message和runnables交付到Looper的message列队中,并在Looper线程中执行他(runnables)。

    主要两个作用:1.在未来某个时刻执行任务;2.在不同的线程中入队并执行相关操作。

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() 这个方法执行

实现了线程的切换

Handler是怎么切换线程的

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

Handler内存泄露的原因?

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

  • 匿名内部类

  • 非静态内部类:

且给出了建议,加上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

  • 清空Handle
@Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
        // 外部类Activity生命周期结束时,同时清空消息队列 & 结束Handler生命周期
    }
  • 结束Looper
@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时就会采取不同的措施:

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

附上Activity创建流程图一张:

参考 :

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

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

简书:Activity启动流程

相关文章

Handler 10问,你顶的住吗?

举报

相关推荐

0 条评论