Handler是一个优秀的内存共享方案。其内存管理和设计思路相当完整。 通过Handler来通知UI组件更新或者是处理对应消息。那么Handler消息机制是什么?

Handler1 消息传递机制

开篇问题

1.一个线程有几个handler

2.一个线程有几个looper,如何保证

3.Handler会出现内存泄漏问题吗,怎么解决内存泄漏

4.子线程总如何使用Handler

App Launcher和System_Server交互 App Launcher和System_Server交互

android 和linux 交互会使用socket

可以说Handler、Binder、Socket是整个Android系统的灵魂。从本文开始从源码角度分析Handler

handler使用

1.handler初始化

Handler构造,当前api30之后,废弃了2个,隐藏3个,实际我们可用的只有两个构造,必须指定当前是哪个Looper

 1@Deprecated
 2public Handler() {
 3    this(null, false);
 4}
 5
 6@Deprecated
 7public Handler(@Nullable Callback callback) {
 8    this(callback, false);
 9}
10//可用的构造方法1
11public Handler(@NonNull Looper looper) {
12    this(looper, null, false);
13}
14//可用的构造方法2
15public Handler(@NonNull Looper looper, @Nullable Callback callback) {
16    this(looper, callback, false);
17}
18
19/*
20* @hide
21*/
22@UnsupportedAppUsage
23public Handler(boolean async) {
24    this(null, async);
25}
26
27/*
28* @hide
29*/
30public Handler(@Nullable Callback callback, boolean async) {
31    if (FIND_POTENTIAL_LEAKS) {
32        final Class<? extends Handler> klass = getClass();
33        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
34            (klass.getModifiers() & Modifier.STATIC) == 0) {
35            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
36                  klass.getCanonicalName());
37        }
38    }
39
40    mLooper = Looper.myLooper();
41    if (mLooper == null) {
42        throw new RuntimeException(
43            "Can't create handler inside thread " + Thread.currentThread()
44            + " that has not called Looper.prepare()");
45    }
46    mQueue = mLooper.mQueue;
47    mCallback = callback;
48    mAsynchronous = async;
49}
50
51/*
52* @hide
53*/
54@UnsupportedAppUsage
55public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
56    mLooper = looper;
57    mQueue = looper.mQueue;
58    mCallback = callback;
59    mAsynchronous = async;
60}

举例说明

1//可用的构造方法1
2private final Handler mUIHandler = new Handler(Looper.getMainLooper()){
3    @Override
4    public void handleMessage(Message msg) {
5        Log.d(TAG, "->>>mUIHandler);
6    }
7};
1//可用的构造方法2
2private final Handler childHandler = new Handler(handlerThread.getLooper(), new Handler.Callback() {
3    @Override
4    public boolean handleMessage(@NonNull Message msg) {
5        Log.d(TAG, "->>>ChildCallback|handleMessage");
6        return true;
7    }
8});

其中,关于Looper.getMainLooper()和Looper.myloop()

Looper.myLooper()

获取当前进程的looper对象

Looper.getMainLooper()

获取主线程的Looper对象

通过Handler构造函数可以看出: 一个 Handler 中只能有一个 Looper。而一个 Looper 则可以对应多个 Handler,只要把 Looper 往 Handler 的构造方法里扔扔扔就好了。

2.handler发送消息

Handler发送消息的方法罗列 Handler发送消息的方法罗列

所有发送消息的方式都在上面罗列,一般常用的为直接或间接调用sendMessageDelayed方法,而其调用的sendMessageAtTime会内部判断消息队列是否为空,消息队列为空就不做处理。

并且这个msg中还会存在时间when,target还有setAsynchronous

1)其中Message的target属性在enqueMessage的时候会将msg.target = this,即Message持有Handler对象

2)其中在enqueMessage中的判断mAsynchronous,通常情况下都是同步消息,这个判断为false

3.handler处理消息

message回到handler中的流程

Looper.java//loop函数

msg.target.dispatchMessage(msg)—»

Handler.java//dispatchMessage函数

分三种情况

 1/**
 2 * Handle system messages here.
 3 */
 4public void dispatchMessage(@NonNull Message msg) {
 5    if (msg.callback != null) {
 6        handleCallback(msg);//第一种情况
 7    } else {
 8        if (mCallback != null) {
 9            if (mCallback.handleMessage(msg)) {
10                return;//第二种情况
11            }
12        }
13        handleMessage(msg);//第三种情况
14    }
15}

1.收发消息一体

Message通过post(Runnable)等方法进行发送走handleCallback(msg);

 1public class MainActivity extends AppCompatActivity {
 2    private TextView msgTv;
 3    private Handler mHandler = new Handler(Looper.getMainLooper());
 4
 5    @Override
 6    protected void onCreate(Bundle savedInstanceState) {
 7        super.onCreate(savedInstanceState);
 8        setContentView(R.layout.activity_main);
 9        msgTv = findViewById(R.id.tv_msg);
10
11          //消息收发一体
12        new Thread(new Runnable() {
13            @Override public void run() {
14                String info = "第一种方式";
15                mHandler.post(new Runnable() {
16                    @Override public void run() {
17                        msgTv.setText(info);
18                    }
19                });
20            }
21        }).start();
22    }
23}

2.收发分开,实现Callback接口

在Handler构造的时候传入Handler内部抽象类,优先处理。mCallback.handleMessage(msg)这个函数会回调到构造里面Callback,最后在结尾是返回一个boolean类性值,一般为true,即外部不需要在handleMessage一次,立刻结束本次的message传递。

1public interface Callback {
2    /**
3     * @param msg A {@link android.os.Message Message} object
4     * @return True if no further handling is desired
5     */
6    boolean handleMessage(@NonNull Message msg);
7}
 1public class MainActivity extends AppCompatActivity {
 2    private TextView msgTv;
 3    private Handler mHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
 4        //接收消息,刷新UI
 5        @Override public boolean handleMessage(@NonNull Message msg) {
 6            if (msg.what == 1) {
 7                msgTv.setText(msg.obj.toString());
 8            }
 9            //false 重写Handler类的handleMessage会被调用,  true 不会被调用
10            return false;
11        }
12    });
13
14    @Override
15    protected void onCreate(Bundle savedInstanceState) {
16        super.onCreate(savedInstanceState);
17        setContentView(R.layout.activity_main);
18        msgTv = findViewById(R.id.tv_msg);
19
20        //发送消息
21        new Thread(new Runnable() {
22            @Override public void run() {
23                Message message = Message.obtain();
24                message.what = 1;
25                message.obj = "第二种方式 --- 2"; 
26                mHandler.sendMessage(message);
27            }
28        }).start();
29    }
30}

3.重写Handler类

调用handler重新handleMessage方法,走handleMessage(msg);

 1public class MainActivity extends AppCompatActivity {
 2    private TextView msgTv;
 3    private Handler mHandler = new Handler(Looper.getMainLooper()) {
 4        //接收消息,刷新UI
 5        @Override public void handleMessage(@NonNull Message msg) {
 6            super.handleMessage(msg);
 7            if (msg.what == 1) {
 8                msgTv.setText(msg.obj.toString());
 9            }
10        }
11    };
12
13    @Override
14    protected void onCreate(Bundle savedInstanceState) {
15        super.onCreate(savedInstanceState);
16        setContentView(R.layout.activity_main);
17        msgTv = findViewById(R.id.tv_msg);
18
19        //发送消息
20        new Thread(new Runnable() {
21            @Override public void run() {
22                Message message = Message.obtain();
23                message.what = 1;
24                message.obj = "第三种方式 --- 3";
25                mHandler.sendMessage(message);
26            }
27        }).start();
28    }
29}

Handler消息传递

优先级队列

根据上述可以看到Handler发送消息,最终会调用enqueMessage

 1//handler#enqueMessage
 2private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
 3                               long uptimeMillis) {
 4    msg.target = this;
 5    msg.workSourceUid = ThreadLocalWorkSource.getUid();
 6
 7    if (mAsynchronous) {
 8        msg.setAsynchronous(true);
 9    }
10    return queue.enqueueMessage(msg, uptimeMillis);
11}

最终返回的是一个布尔值,queue.enqueMessage,其中queue是MessageQueue类型的

 1//MessageQueue#enqueueMessage
 2boolean enqueueMessage(Message msg, long when) {
 3    ...
 4    synchronized (this) { 
 5        msg.when = when;
 6        //这里的mMessages是一个Message类型的链表(Message中有next属性值)
 7        Message p = mMessages;
 8        boolean needWake;
 9        //三个判断指的是这个链表没有Message,传入的当前p为当前第一个Message
10        if (p == null || when == 0 || when < p.when) {
11            // New head, wake up the event queue if blocked.
12            msg.next = p;
13            mMessages = msg;
14            needWake = mBlocked;//mBlocked通常为true的时候是无idle handlers,纯等待
15        } else {
16            // Inserted within the middle of the queue.  Usually we don't have to wake
17            // up the event queue unless there is a barrier at the head of the queue
18            // and the message is the earliest asynchronous message in the queue.
19            //这个needWake中的后两个判断,第一个是用来判断当前MessageQueue是否开启同步屏障且传入的Message是异步的
20            needWake = mBlocked && p.target == null && msg.isAsynchronous();
21            Message prev;
22            //循环开始在适当的位置把传入的msg插入到mMessageQueue中
23            for (;;) {
24                prev = p;
25                p = p.next;
26                if (p == null || when < p.when) {
27                    break;
28                }
29                if (needWake && p.isAsynchronous()) {
30                    needWake = false;
31                }
32            }
33            msg.next = p; // invariant: p == prev.next
34            prev.next = msg;
35        }
36        // We can assume mPtr != 0 because mQuitting is false.
37        if (needWake) {
38            //用于唤醒next方法中的nativePollOnce
39            nativeWake(mPtr);
40        }
41        ...
42    }
43    return true;
44}

拆解上述方法,核心的就是其中两个判断if (p == null || when == 0 || when < p.when),正好对应两个形式的链表的插入,从而得出正是一个按照时间顺序排列的链表。

1)如果判断if (p == null || when == 0 || when < p.when)成立,则为下图

2)如果判断if (p == null || when == 0 || when < p.when)不成立,当前msg传入的when<p.when,则为下图

3)如果判断if (p == null || when == 0 || when < p.when)不成立,当前p=null,则为下图

那么既然有插入链表的操作,就有从MessageQueue中取出的操作,再来看next方法

 1//MessageQueue#enqueueMessage
 2Message next() {
 3    ...
 4    int pendingIdleHandlerCount = -1; // -1 only during first iteration
 5    for (;;) {
 6        if (nextPollTimeoutMillis != 0) {
 7            //当需要阻塞时,防止进程持有对象的时间超过需要的时间,做一个保护作用
 8            Binder.flushPendingCommands();
 9        }
10        //这个是用来阻塞的,两种方式解除
11        //1.nativeWake的时候继续执行下去。即直到euqueMessage中插入了一个比nextPollTimeoutMillis小的时间的msg
12        //2.nextPollTimeoutMillis==now,时间消耗完毕,开始取消阻塞
13        //3.退出消息队列所在的循环Looper,即mQuitting=true
14        nativePollOnce(ptr, nextPollTimeoutMillis);
15
16        synchronized (this) {
17            // Try to retrieve the next message.  Return if found.
18            final long now = SystemClock.uptimeMillis();
19            Message prevMsg = null;
20            Message msg = mMessages;
21            if (msg != null) {
22                if (now < msg.when) {
23                    // Next message is not ready.  Set a timeout to wake up when it is ready.
24                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
25                } else {
26                    // Got a message.
27                    mBlocked = false;
28                    //这个判断是用于同步屏障中异步消息的判断,这里暂时不考虑
29                    if (prevMsg != null) {
30                        prevMsg.next = msg.next;
31                    } else {
32                        mMessages = msg.next;
33                    }
34                    msg.next = null;
35                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
36                    msg.markInUse();
37                    return msg;
38                }
39            } else {
40                // No more messages.
41                nextPollTimeoutMillis = -1;
42            }
43
44            // Process the quit message now that all pending messages have been handled.
45            if (mQuitting) {
46                dispose();
47                return null;
48            }
49            //没有IdleHandler的时候,直接continue
50            if (pendingIdleHandlerCount <= 0) {
51                // No idle handlers to run.  Loop and wait some more.
52                mBlocked = true;
53                continue;
54            }
55        }
56        ...
57        // While calling an idle handler, a new message could have been delivered
58        // so go back and look again for a pending message without waiting.
59        nextPollTimeoutMillis = 0;
60    }
61}

拆解可知,这个链表是按照msg插入when的时间的排列的链表,符合先进先出,即为优先级队列

1)当前的now<msg.when,开始阻塞线程

这里边用到了nativePollOnce,用于阻塞线程,三种方式可以取消阻塞

1.nativeWake的时候继续执行下去。即直到euqueMessage中插入了一个比nextPollTimeoutMillis小的时间的msg 2..nextPollTimeoutMillis==now,时间消耗完毕,开始取消阻塞

3.退出消息队列所在的循环Looper

2)开始获取Message,返回圈红的Message

获取Message 获取Message

在MessageQueue里面Handler中的三个native方法,主要含义具体分析详见Ahab的文章

1//Handler中的三个native方法
2private native static long nativeInit(); //返回 ptr
3private native void nativePollOnce(long ptr, int timeoutMillis); //阻塞
4private native static void nativeWake(long ptr); //唤醒

Looper

根据上述可知Handler会通过enqueMessage来将消息根据延时的时间排列成优先级队列先进先出,那么谁去执行next让其每次一个个有序的将MessageQueue中的Message取出来呢?这时候需要用到Looper。

获取next方法

听名字就知道是一个循环,主要是看里面loop方法

 1//Looper.java#loop
 2public static void loop() {
 3    final Looper me = myLooper();
 4    final MessageQueue queue = me.mQueue;//mQueue = new MessageQueue(quitAllowed);默认是无法退出的
 5
 6    // Make sure the identity of this thread is that of the local process,
 7    // and keep track of what that identity token actually is.
 8    //重置当前线程上传入的IPC的身份,确保此线程的标识是本地进程的标识
 9    Binder.clearCallingIdentity();
10    final long ident = Binder.clearCallingIdentity();
11
12    for (;;) {
13        Message msg = queue.next(); // might block
14        if (msg == null) {
15            // No message indicates that the message queue is quitting.
16            return;
17        }
18        // Make sure the observer won't change while processing a transaction.
19        //observer是用于Handler分发操作的监听,监听产生问题的trace
20        //补充说明:这里面的sObserver是在LooperStateService.java里面设置下来的
21        //Looper.setObserver(enable ? mStates : null);
22        //其中mStates定义 LooperState mStates implement Looper.Observer
23        //这里的enable是通过系统属性来区分debug.sys.Looper_state_enabled
24        final Observer observer = sObserver;
25
26        Object token = null;
27        if (observer != null) {
28            token = observer.messageDispatchStarting();
29        }
30        long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
31        try {
32            //将获取到的msg分发到dispatchMessage
33            msg.target.dispatchMessage(msg);
34            if (observer != null) {
35                observer.messageDispatched(token, msg);
36            }
37            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
38        } catch (Exception exception) {
39            if (observer != null) {
40                observer.dispatchingThrewException(token, msg, exception);
41            }
42            throw exception;
43        } finally {
44            ThreadLocalWorkSource.restore(origWorkSource);
45            if (traceTag != 0) {
46                Trace.traceEnd(traceTag);
47            }
48        }
49        ...
50        //msg资源池回收机制    
51        msg.recycleUnchecked();
52    }
53}

拆解loop方法,其实就是做了几件事

1)重复循环获取next方法,因为next方法存在阻塞,所以queue.next()也会阻塞,且唯一退出这个方法的是就是获取一个null的Messgae

2)将获取到的msg分发到dispatchMessage,用这个方法来对消息进行处理,msg.target持有了Handler对象,那么依然会回到Handler中去使用。通过Looper使得整个消息队列形成了闭环。

3)处理完消息之后,通过msg.recycleUnchecked(),将消息回收到消息池里

这里的消息放到消息池,是运用了设计模式的享元模式

具体看一下Message的构成

1//Message.java的属性
2public static final Object sPoolSync = new Object();//用于消息池的上锁功能
3private static Message sPool;
4private static int sPoolSize = 0;//默认消息池的大小
5
6private static final int MAX_POOL_SIZE = 50;//消息池最大容量50
7
8private static boolean gCheckRecycle = true;//默认queueMessage的时候mQuitting=true的时候会回收

显然Message属性会持有一个next,spool是消息队列的头,属于链表

i) obtain

 1//Message.java#obtain获取方法
 2public static Message obtain() {
 3    synchronized (sPoolSync) {
 4        if (sPool != null) {
 5            Message m = sPool;
 6            sPool = m.next;
 7            m.next = null;
 8            m.flags = 0; // clear in-use flag
 9            sPoolSize--;
10            return m;
11        }
12    }
13    return new Message();
14}

方法拆解如下,类似上述的消息机制:

消息池中消息的获取 消息池中消息的获取

一般调用方式为如下所示,基本囊括了平时使用的发送消息的方法,一般我们自己使用的时候,也要多使用obtainMessage方法

 1@NonNull
 2public final Message obtainMessage()
 3{
 4    return Message.obtain(this);
 5}
 6@NonNull
 7public final Message obtainMessage(int what)
 8{
 9    return Message.obtain(this, what);
10}
11@NonNull
12public final Message obtainMessage(int what, @Nullable Object obj) {
13    return Message.obtain(this, what, obj);
14}
15public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
16    Message msg = Message.obtain();
17    msg.what = what;
18    return sendMessageDelayed(msg, delayMillis);
19}
20private static Message getPostMessage(Runnable r) {
21    Message m = Message.obtain();
22    m.callback = r;
23    return m;
24}

ii) recycleUnchecked

enqueMessage调用的mQuitting中的msg.recycle(),调用到这个方法,会将sPoolSize增加。这个方法用到的都是quit方法中直接或间接调用recycleUnchecked,不需要手动去调用该方法。

 1//Message.java#recycleUnchecked回收方法
 2@UnsupportedAppUsage
 3void recycleUnchecked() {
 4    // Mark the message as in use while it remains in the recycled object pool.
 5    // Clear out all other details.
 6    flags = FLAG_IN_USE;
 7    what = 0;
 8    arg1 = 0;
 9    arg2 = 0;
10    obj = null;
11    replyTo = null;
12    sendingUid = UID_NONE;
13    workSourceUid = UID_NONE;
14    when = 0;
15    target = null;
16    callback = null;
17    data = null;
18
19    synchronized (sPoolSync) {
20        if (sPoolSize < MAX_POOL_SIZE) {
21            next = sPool;
22            sPool = this;
23            sPoolSize++;
24        }
25    }
26}

方法拆解如下,这个Message加入消息池的方式是头部添加。

消息池中消息的回收 消息池中消息的回收

这里边唯一和消息队列的区别是,消息队列是先进先出,这里是先进后出,这个消息池类似压栈的过程。

退出looper方法

有两种退出Looper,quitSafely和quit,最终调用到MessageQueue中的quit方法,对应true和false

 1void quit(boolean safe) {
 2    if (!mQuitAllowed) {
 3        //ui主线程是不允许退出的
 4        throw new IllegalStateException("Main thread not allowed to quit.");
 5    }
 6
 7    synchronized (this) {
 8        if (mQuitting) {
 9            return;
10        }
11        mQuitting = true;
12
13        if (safe) {
14            //只移除所有尚未执行的消息,不移除时间戳等于当前时间的消息
15            removeAllFutureMessagesLocked();
16        } else {
17            //暴力移除所有消息
18            removeAllMessagesLocked();
19        }
20
21        // We can assume mPtr != 0 because mQuitting was previously false.
22        nativeWake(mPtr);
23    }
24}

拆解quit方法,主要做了以下事

1)设置标记位mQuitting=true

2)移除所有消息(安全退出和强制退出)

3)唤醒在next中的nativePollOnce,在next方法中通过if (mQuitting)返回了null,回到Looper.loop方法中。Looper.loop方法中获取的msg为null,最终也退出Looper,完成整一个退出流程。

重新新建looper方法

有退出就有重新建的方法,

1//Looper.java#prepare
2public static void prepare() {
3    prepare(true);
4}

这里的prepare执行,是可以进行退出操作,而Ui主线程中是在ActivityThread中执行prepareMainLooper,这个looper是不可以退出的,即只能在子线程中完成可以退出的looper。

举例说明

 1//子线程总如何使用Handler的例子
 2private Looper mLooper;
 3private void initHandler() {
 4    new Thread(new Runnable() {
 5        @SuppressLint("HandlerLeak")
 6        @Override
 7        public void run() {
 8            //子线程开启一个Looper
 9            mLooper = Looper.prepare();
10            handler = new Handler(Looper.myLooper(), new Handler.Callback() {
11                @Override
12                public boolean handleMessage(Message msg) {
13                    Log.d(TAG, "->>>handlermessage");
14                    if (null != mLooper)
15                    {
16                        //退出looper
17                        mLooper.quit();
18                    }
19                    return true;
20                }
21            });
22            //执行Looper.loop,即开启消息队列的无限循环模式
23            Looper.loop();
24        }
25    }).start();
26}

Handler流程图

根据Looper,Handler,Message和MessageQueue可以得知Handler流程,这个流程是典型的的生产者消费者模式。

Handler流程图 Handler流程图

问题回答

1.一个线程有几个handler

一个,ui主线程中可以有activty,service,他们都可以使用UI主线程

2.一个线程有几个looper,如何保证

一个looper。java中prepare函数如果存在threadlocal那么会抛出异常不能new looper,所以一个threadlocal对应一个looper。这个会在下一节重点分析

3.Handler会出现内存泄漏问题吗,怎么解决内存泄漏

会存在,原因是由于msg.target=this,持有了handler对象。另外,Looper对象在主线程中,整个生命周期都是存在的,MessageQueue是在Looper对象中,也就是消息队列也是存在在整个主线程中,因此Handler又是和Activity存在强引用关系。即内部类持有外部类引用。this调用ondestory并不能释放,jvm认为当前this正在被handler使用。

解决:使用静态内部类弱引Activity、清空消息队列

4.子线程如何使用Handler

子线程使用handler的例子在上面已经举例说明,不再重复。

参考

[1] 小呆呆666, 万字图文,带你学懂Handler和内存屏障, 2021.

[2] zcbiner, Handler机制与生产者消费者模式, 2018.

[3] 业志陈, 一文读懂 Handler 机制, 2021.

[4] Ahab, 介绍一下 Android Handler 中的 epoll 机制, 2020.

猜你想看

Handler2 Thread

Handler3 同步屏障

Handler4 HandlerThread

Handler5 IntentService