Handler源码和机制分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sliverbullets/article/details/83107984

在这个包 android.os.Handler下的Handler

注:所有”英语解释“均来自源码英文的有道翻译结果。

打开这个类,发现这个类其实很简单只有850行。
其中属性:
在这里插入图片描述
在这里插入图片描述

IMessenger :传送门

其中属性变量MAIN_THREAD_HANDLER是从ActivtyThread.java来的,看图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
为什么要注意static,是因为如果你不做好的处理的话它会导致内存泄漏,它引用着MainThread,app销毁时,Handler对象里一直持有它的强引用,导致它不会被GC回收,导致内存泄漏。

顺便补充一点:
volatile关键字:volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。
参考链接:Java并发编程:volatile关键字解析

Handler的构造方法(一共是7个构造方法)

由于代码复制看不见提示所以截图给出
在这里插入图片描述callback是你要执行任务线程的回调
async是传递是否同步,ture:使所有message都同步; flase: 不同步。
调用两个参数的构造

callback, async
在这里插入图片描述
调用两个参数的构造

looper, callback, async
在这里插入图片描述
looper: 用于为线程运行消息循环 并且会绑定当前线程,handler创建前必须先有looper,这个后面会解释。
调用三个参数的构造

looper ,callback ,async
在这里插入图片描述
调用三个参数的构造

async
在这里插入图片描述
调用两个参数的构造

callback ,asyn
在这里插入图片描述
两个参数构造的实现,然后发现内部创建了looper,如果没有looper就会报异常,这也就是上面的那个问题的答案,looper提供了循环队列。还有这个FIND_POTENTIAL_LEAKS标志,true,表示需要执行检查内存泄露操作,就会执行里面的一堆判断,大概意思是,如果是Handler匿名内部类或子类或本地类,并且成功获取到Handler类修饰是静态的,就会出警告。

looper ,callback , async
在这里插入图片描述
三个参数构造的具体内容,因为需要的参数都有了,所以直接赋值。

接下来我们会看到这样一个接口,里面有一个handleMessage方法,这个方法也是我们经常重写的。事实上,我们经常会这样使用handler:①直接 Handler handler = new Handler(){ handleMessage( msg)};②自己写一个class类继承Handler。这样做的目的都是为了重写handleMessage方法来让Handler绑定的线程去做一些事情。这个接口也是为了我们方便使用handler来处理不同线程的事务。不过这两种方法都用的子类继承的方式。

//英文解释:在实例化处理程序时可以使用回调接口,以避免必须实现自己的处理程序子类
 public interface Callback {
    /**
     * @param msg A {@link android.os.Message Message} object
     * @return True if no further handling is desired
     */
    public boolean handleMessage(Message msg);
}

下面是我们用第二种方法时会遇到的:

//英语解释:子类必须实现这个才能接收消息
public void handleMessage(Message msg) {
}

再接着看这个Handler类,会遇到它的一个方法dispatchMessage(Message msg):
其逻辑:如果传入的msg的callback不为空,也就是msg其实是一个callback(线程里的接口,用来执行线程内容),它就会调用handleCallback(msg),也就是执行这个callback,否则,它会判断mCallback(也就是接口,上面有提到)是否为null,如果不为空,就执行用户重写的handleMessage;为空,就执行Handler类里的handleMessage方法,总之,它就是要把msg(消息)按一个方法逻辑处理掉。

//英文解释:在这里处理系统消息。
 public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

在接下来是handler关于message的一堆获取方法(一共5个obtainMessage方法):
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

我们随便看一个Message.obtain方法看看它怎么获取的?
在这里插入图片描述

在这里插入图片描述
我们可以得到下面结论:
m.target 的类型是Handler用,来说明是哪个handler持有的这个消息
m.what 的类型是int,标识作用
m.arg1和m.arg2类型是int,携带两个int值
m.obj类型是Object,传递Object类型值
总的来说就是根据你给的参数,创建了一个msg对象,然后返回给你。

然后在Handler类里就又能看到下面这样的一堆方法:
先总的来说吧:
我认为主要分两类和一特殊:
①postXXX 一般是指内容为Runnable类型的
按时间分:
- AtTime 定时
- Delayed 延时,并最终会通过计算调用AtTime

②sendXXX 一般是指内容为Message类型的
- AtTime 定时
- Delayed 延时,并最终会通过计算调用AtTime
特殊:postAtFrontOfQueue(Runnable r),然后里面调用sendMessageAtFrontOfQueue(Message msg) 这两个方法都有贴。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述这个方法主要作用就是把msg直接放到mQueue的队头,它通过把uptimeMills这个参数设置为0来做到的。

总的来看,最终都会调用这个方法enqueueMessage(queue,msg,uptimeMillis)
在这里插入图片描述
这个方法的作用是把msg入队。这个方法里先判断是否同步,然后设置是否同步。是的,会发现msg并不是你一发出去就会执行的,它会放到一个队列里,这个队列是按时间(需要执行时的时间先后)排的。这也就是为什么说上一个方法是特殊的了,因为它直接将这个参数设置为了0。

既然有队列又有入队的方法,那么就一定有出队移除的方法:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

还有这些判断msg或者callback是否已经在队列:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

最后还有这样的几个方法,主要是用Printer对象来打印出打印出Handler以及Looper和Queue中的一些信息。
在这里插入图片描述

在这里插入图片描述
如果还想继续了解Printer:传送门

到此为止,Hanlder类就解析完毕了。
这里再给出一个Handler使用时的内存泄漏链接:链接1

Handler消息机制

先看一张你们可能比较熟悉的图:
在这里插入图片描述

首先,Handler在哪个线程创建,就会哪个线程执行handleMessage方法。它会持有该线程的Looper和循环队列。
所以我们从Handler创建开始看这张图,我们需要创建Handler,但这是不够的,不能构成循环,根据源码知道先有Looper才有Handler,消息队列是Looper的一个成员。这样循环的元素就有了,下来通过他们各自的联系构成循环,handler通过sendMessage将一个Message对象放入循环队列,循环队列按照执行时间排序,Looper则负责一直读取消息,取出来的消息是在handler的handleMessage中完成的。这就是消息循环过程。

Handler的三种交互场景:
①UI线程子发消息到子线程
②子线程发消息到UI线程
③子线程与子线程
详细:传送门

补充:
主线程中的Looper.loop()一直无限循环为什么不会造成ANR?传送门
深入理解MessageQuene传送门

总结:
1.Handler可以进行线程间通信;
2.Handler创建前必须现有Looper对象,子线程调用Looper.parpare(),Main线程自带。
3.Handler的handleMessage执行线程是Handler对象创建线程。
4.Handler在使用过程中的隐患:内存泄漏。
解决:Ⅰ. 它里面有个标志位FIND_POTENTIAL_LEAKS用来标志是否有内存泄漏情况,如果有它会使用它会使用
final Class<? extends Handler> klass = getClass();
创建一个静态内部类,让这个类里Activity引用为弱引用。
Ⅱ.若你的Handler是被delay的Message持有了引用,那么使用相应的Handler的removeCallbacks()方法,把消息对象从消息队列移除。

猜你喜欢

转载自blog.csdn.net/sliverbullets/article/details/83107984