Handler是什么

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

  • 官方:

    A Handler allows you to send and process Message and Runnable objects associated with a thread’s MessageQueue. Each Handler instance is associated with a single thread and that thread’s message queue. When you create a new Handler it is bound to a Looper. It will deliver messages and runnables to that Looper’s message queue and execute them on that Looper’s thread.

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

    There are two main uses for a Handler: (1) to schedule messages and runnables to be executed at some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.

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

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所在线程中来,最后执行message.callback.run();【或者handle.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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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启动流程