Android 消息机制-Handler浅析
阅读原文时间:2021年04月20日阅读:1

前言:Android的消息机制其实主要指的就是Handler的运行机制。

目录

一、Handler、Looper与MessageQueue

二、ThreadLocal

三、MessageQueue(消息队列)工作原理

四、Looper的工作原理

五、Handler的工作原理

六、主线程消息处理

七、Handler内存泄漏问题


一、Handler、Looper与MessageQueue        

         Handler的运行需要底层MessageQueue和Looper支撑。其中MessageQueue(消息队列)使用单链表的数据结构来存储消息列表,只是存储消息列表,不会去处理消息,消息的处理交由Looper(循环)来处理。Looper使用无限循环的方式来查看是否有新消息进入MessageQueue,有的话就处理消息,没有的话就等待。线程默认没有Looper,需要使用Handler就需要为线程创建Looper。这时候大家可能就有疑问了,我也使用过Handler,也没有碰到需要创建Looper的情况啊?这是因为,ActivityThread(主线程/UI线程)在被创建的时候会初始化Looper。所以在主线程中可以直接使用Handler。Handler在哪个线程中被创建,之后使用Handler切换的代码逻辑就会被切换到哪个线程中。这也就是我们常在主线程中创建Handler,然后在子线程中使用Handler将更新UI界面的逻辑切换到创建此Handler的主线程中来更新UI。

        可能有人要问了,我们都知道UI的更新要放在主线程中来执行,耗时的操作要放在子线程中来完成,但是,Why?为什么不能再子线程中直接更新UI呢,这样不是更方便吗?这是因为子线程涉及到多线程的情况,假如在多线程中并发访问UI,UI控件本身又不是线程安全的,那UI控件就该哭诉了:到底想让我展示什么? 所以面对此问题,Handler可以完美解决,想更新UI,切回主线程即可。

二、ThreadLocal

    ThreadLocal是线程内部的存储类,可以在每个线程中存储数据。其他线程是无法获取这个线程中的数据的。比如我们先实例化一个ThreadLocal对象:

ThreadLocal<Boolean> mThreadLocal = new ThreadLocal<>();

然后可以通过调用mThreadLocal.set(Boolean)和mThreadLocal.get()分别为所在的线程设置值和取值。

三、MessageQueue(消息队列)工作原理

        前面说过消息队列只存储消息,不处理消息。它主要有两个操作:插入和读取。其中enqueueMessage方法往消息队列中插入一条消息,next方法从消息队列中取出一条消息并将其从消息队列中移除。

        调用handler.post(runnable)和handler.sendMessage(message)方法都会走如下方法流程:sendMessageDelayed()->sendMessageAtTime()->enqueueMessage(queue, msg, uptimeMillis);

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

看返回的方法,这个时候会执行MessageQueue的enqueueMessage方法进行单链表的消息插入操作。Looper会无限循环来查看消息队列中是否有新消息,Looper就是通过调用MessageQueue的next方法来取出新消息。

        handler.post(runnable)虽然传入的参数是一个线程Runnable,但参数会经过getPostMessage方法返回一个message,其实流程和handler.sendMessage(message)相同。

private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

四、Looper的工作原理

        Looper的作用是无限循环的从MessageQueue中查看是否有新消息,毕竟消息队列中存储的消息总要有人来处理。如果查看到有新消息就立刻处理,没有的话就阻塞等待。一个线程中只有一个Looper。

Looper的构造方法如下:

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

Handler要正常工作肯定是需要Looper的,那Looper怎么创建呢?如下:

Looper.prepare();//创建Looper
Handler handler = new Handler();
Looper.loop();//开启消息循环

在子线程中想创建Handler,就需要创建Looper,因为创建子线程的时候,是没有自动初始化Looper的操作的。

        Looper可以采用quit和quitSafely来退出Looper,其中quit会直接退出Looper,不管你消息队列中是否还有消息没处理。quitSafely会把消息队列中的消息处理完后退出。如果Looper退出了,再使用Handler发消息,因为没人处理了,所以会失败。

        在子线程中手动创建Looper,在所有事情处理完后应该手动调用quit退出Looper,不然这个子线程又不需要做什么了,但Looper还会一直处于等待状态。退出Looper后,这个线程也会立即终止。

handler.getLooper().quit();//子线程的事情处理完调用quit方法退出Looper,避免Looper一直处于等待状态

 Looper的quit方法其实就是调用消息队列的quit方法。

public void quit() {
&nbsp; &nbsp; mQueue.quit(false);
}

Looper最重要的方法就是loop方法了。loop方法是一个死循环,只能通过调用MessageQueue的next方法返回null时才会结束。

for (;;) {  //死循环
    Message msg = queue.next(); // might block
    if (msg == null) {
        // No message indicates that the message queue is quitting.
        return;   //当MessageQueue的next方法返回null时,跳出死循环
    }
...
}

looper方法通过调用MessageQueue的next方法来获取新消息,当没有消息时,next方法会一直阻塞,那loop也会一直阻塞。当有消息时,MessageQueue的next方法返回这个消息,Looper拿到这个消息后开始处理

try {
    msg.target.dispatchMessage(msg);
}

在Message中target是一个Handler: Handler target;指的是发送这条消息的Handler对象。这个时候代码逻辑成功的从一个线程切到指定的线程中了。

五、Handler的工作原理

        Handler的工作主要是发送消息和接收消息,一个线程中可以创建多个Handler,Handler向消息队列中插入一条消息,Looper通过MessageQueue的next方法取出消息,将这条消息发送给Handler处理,这时Handler会调用dispatchMessage方法

public void dispatchMessage(Message msg) {
    if (msg.callback != null) { //msg.callback是一个Runnable对象,也就是post传递的Runnable参数
        handleCallback(msg); //不为null时调用handleCallback处理
    } else { 
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);//我们熟悉的处理消息的方法
    }
}

从dispatchMessage方法中可以看出可以分别处理Handler调用的post传递的Runnable和sendMessage传递的message。

对象为Runnable时调用handleCallback(msg)处理,查看源码可知会调用Runnable的run方法。

private static void handleCallback(Message message) {
    message.callback.run();
}

对象为message时调用handleMessage(msg)处理,Handler类中handleMessage方法是空的

public void handleMessage(Message msg) {
}

我们在创建Handler对象时重写处理这个消息的逻辑

mhandler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        //处理消息的逻辑
    }
};

六、主线程消息处理

    前面说过,主线程可以直接使用Handler,这是因为主线程ActivityThread在main方法被调用的时候会调用Looper.prepareMainLooper()来创建Looper,然后调用Looper.loop()开启消息循环。

public static void main(String[] args) {
    ...
    Looper.prepareMainLooper();
    ...
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

ActivityThread类中定义可一个内部类H,继承Handler。

private class H extends Handler {...}

AMS回调ApplicationThread的Binder方法,ApplicationThread向H发送消息,H收到的消息会在ActivityThread处理

private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
    Message msg = Message.obtain();
    msg.what = what;
    msg.obj = obj;
    msg.arg1 = arg1;
    msg.arg2 = arg2;
    if (async) {
        msg.setAsynchronous(true);
    }
    mH.sendMessage(msg);//发送消息
}

七、Handler内存泄漏问题

        内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。(摘自百度百科)

        在Android中,内存泄漏一般是由长生命周期拥有短生命周期的对象,短生命周期的对象生命周期已经结束,但由于长生命周期还拥有此对象的强引用,导致系统无法回收短生命周期的对象,内存无法释放,造成内存泄漏。

       在Handler中内存泄漏的常见错误用法是,如果Handler声明为内部类,内部类天然的拥有外部类的实例引用,Handler支持延迟发送消息,假如说是5s,但是在5s内消息还未来得及处理,Activity就finish了,但此时Handler是拥有此Activity的对象的引用的,由于一般的引用是强引用,所以即使内存不足抛出内存泄漏异常,系统也不会回收这个对象。

private Handler mhandler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        if (msg.what == 1){

        }
    }
};

        解决方法是,将Handler设为静态,用弱引用包裹外部类对象。静态内部类不会持有外部类的引用,如果需要使用外部类,使用java.lang.ref.WeakReference包裹外部类对象访问,垃圾回收器扫描到弱引用对象,不管内存是否充足,都会将他回收,所以不会引起内存泄漏。弱引用即使在引用对象的同时,依然允许垃圾回收器来回收该对象。

private Mhandler mhandler = new Mhandler(this);
private static class Mhandler extends Handler{
    private WeakReference<MainActivity> weakReference;
    private Mhandler(MainActivity activity){
        weakReference = new WeakReference<MainActivity>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        MainActivity activity = weakReference.get();
        if (activity != null){

        }
    }
}