Handler源码分析

前言

Handler 是Android官方提供的一套用于UI更新的消息处理机制
主要功能为:发送消息处理消息

1 原理分析

我们从子线程发送消息,主线程更新UI的场景开始分析

// 子线程发消息
Message message = Message.obtain();
message.what = 0;
message.obj = ""; // something
handler.sendMessage(message);

// 主线程接收消息,更新UI
private final Handler myHandler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(@NonNull Message msg) {
        if (msg.what == 0) {
            // do something
        }
        return true;
    }
});
复制代码

handler.sendMessage(message) 出发,我们找到消息发出去前的几个处理。

  1. 消息放入消息队列前的预处理
  2. 消息进入消息队列

消息放入消息队列前的预处理

public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
            this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}
复制代码

预处理:拿到消息队列mQueue,然后把消息msg,放进去。
mQueue跟进去是Looper类创建的MessageQueue对象,里面的消息缓存架构可以了解一下。

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed); // 创建消息队列
    mThread = Thread.currentThread();
}
复制代码

消息进入消息队列

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
                               long uptimeMillis) {
    msg.target = this; // “抓住”当前发送消息的Handler对象
    msg.workSourceUid = ThreadLocalWorkSource.getUid();

    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis); // 进入消息队列
}
复制代码

注意:这里有行代码
msg.target = this;
this代表当前handler对象,也就是myHandler。
所以,发出去的消息会携带,当前handler对象的引用!

消息发出去后,Handler会进行处理messageQueue

// Looper.java
// 无限循环
for (;;) {
    Message msg = queue.next(); // might block
    if (msg == null) {
        // No message indicates that the message queue is quitting.
        return;
    }

    ...

    try {
        msg.target.dispatchMessage(msg);
        ...
    } catch (Exception exception) {
        ...
    } finally {
        ...
    }
	...
}
复制代码

可以看到looper启用了一个无限循环,不断去消息队列中找消息,然后,通过“target”调用dispatchMessage方法,传递消息。

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

// mCallback.handleMessage(msg)
// 这也是Java多态的巧妙应用
// 主线程接收消息,更新UI
private final Handler myHandler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(@NonNull Message msg) {
        // 主线程接收到msg
        if (msg.what == 0) {
			// do something
        }
        return true;
    }
});
复制代码

2 内存泄露问题

handler的内存泄露也是老生常谈的问题,比如下面这个handler就造成了内存泄露。

private final Handler myHandler = new Handler() {
    @Override
    public void handleMessage(@NonNull Message msg) {
        super.handleMessage(msg);
    }
};
复制代码

  原因就是这是个静态内部类,而发出去的msg,又携带着myHandler的引用,myHandler又持有对外面的Activity的匿名引用。
  所以,在用户退出当前Activity时,handler内部的一些耗时操作如果还在运行,会导致activity还被handler引用,最终导致activity还存留在堆栈中,没有被回收,内存泄露。
  解决内存泄露的方式也很简单,不使用这种静态内部类就行。
  可以使用Handler.Callback接口的方式,也可以使用弱引用的方式。

private static class MyHandler extends Handler {
    // 弱引用
    private final WeakReference<Activity> mActivity;

    public MyHandler(Activity mActivity) {
        this.mActivity = new WeakReference<Activity>(mActivity);
    }

    @Override
    public void handleMessage(@NonNull Message msg) {
        super.handleMessage(msg);
        Activity activity = mActivity.get();
        if (activity != null) {
            // do something
        }
    }
}
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享