Handler线程通信机制:实战、原理、性能优化!

阿木木 阿木木 | 106 | 2022-08-19

Handler的使用
我们知道,Android中,不允许应用程序在子线程中更新UI,UI的处理必须在UI线程中进行,这样Android定制了一套完善的线程间通信机制——Handler通信机制。Handler作为Android线程通信方式,高频率的出现在我们的日常开发工作中,我们常用的场景包括:使用异步线程进行网络通信、后台任务处理等,Handler则负责异步线程与UI线程(主线程)之间的交互。

我们来看Handler的使用示例:

    public static final int LOAD_COM = 1;//加载任务的id标志
    
    private Handler mHandler = new MyHandler(MainActivity.this);

    private static class MyHandler extends Handler{
        private final WeakReference<MainActivity> mActivity;

        private MyHandler(MainActivity activity) {
            this.mActivity = new WeakReference(activity);
        }

        @Override
        public void handleMessage(@NonNull Message msg) {//ui线程中,负责消息返回的处理逻辑
            super.handleMessage(msg);
            switch (msg.what){
                case LOAD_COM:
                    Log.d("TestHandler", msg.obj.toString());
                    MainActivity mainActivity = mActivity.get();
                    if (mainActivity != null){
                        mainActivity.mTextView.setText(msg.obj.toString());
                    }
                    break;
            }
        }
    };
    
    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.start_load:
                new Thread(){
                    @Override
                    public void run() {//后台线程中执行逻辑
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        Message message = Message.obtain();
                        message.what = LOAD_COM;
                        message.obj = "加载完成";
                        mHandler.sendMessage(message);//从后台线程中,发送消息给UI线程
                    }
                }.start();
                break;
        }
    }

demo逻辑解析:
在Activity中,创建了一个Handler对象。
当按钮start_load点击时,启动一个后台线程,模拟一个后台加载过程(线程休眠1秒)。
后台任务完成后,使用Handler对象的sendMessage方法发送消息(一个Messaage对象)给UI线程。
UI线程中,Handler对象的handleMessage方法负责处理消息的返回。
Demo中的例子是我们在Android开发中经常使用到的方式,Handler非常简单的帮助我们实现了UI线程和后台线程之间的通信。那么Handler是如何做到线程间通信的呢?

接下来我们以源码来分析Handler的实现机制。

Handler的实现机制&源码分析
Handler机制介绍
Handler机制是Android通信机制的一个重要组成部分。在Android中,应用程序是消息驱动的,每个应用程序的UI线程(主线程)都会维护一个消息队列,并且会不断的从这个消息队列中取出消息进行处理。Handler机制实现了消息在线程之间的通信。

Handler消息处理机制包括了3个组成部分:消息循环、消息发送、消息处理,这其中涉及到几个重要类:Handler、Message、Looper和MessageQueue。

一个线程,想要处理Hander发送的消息,必须做好以下几点准备:
首先要有一个消息循环,也就是要创建一个Looper对象。
将Looper对象绑定到当前线程上,也就是必须要调用Looper.prepare()方法。
开启Looper的循环,也就是要调用Looper的loop方法。
我们接下来以Handler创建、消息的发送、消息循环、消息处理的顺序来展开分析。

Handler对象的创建
要想使用Handler进行通信,首先要创建一个Handler对象。

Handler的构造函数:

    public Handler() {//版本1
        this(null, false);
    }
    public Handler(@Nullable Callback callback) {//版本2
        this(callback, false);
    }
    public Handler(@NonNull Looper looper) {//版本3
        this(looper, null, false);
    }
    public Handler(@NonNull Looper looper, @Nullable Callback callback) {//版本4
        this(looper, callback, false);
    }
    @UnsupportedAppUsage
    public Handler(boolean async) {//版本5。不支持App使用
        this(null, async);
    }
    public Handler(@Nullable Callback callback, boolean async) {//版本6。主要实现逻辑在这里
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    @UnsupportedAppUsage
    public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {//版本7。不支持App直接使用,系统内部使用
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

Handler的构造函数有多个重载版本,但逻辑实现都在版本6和版本7中。在版本7中,只是简单的属性赋值,这里我们来分析版本6的实现。

逻辑解析:
Handler有多个重载版本,但可供我们使用版本的async参数都是默认值false。
首先根据布尔值FIND_POTENTIAL_LEAKS判断是否需要进行泄漏的检测,默认是false。
将Looper.myLooper()赋值给mLooper对象。
将looper.mQueue赋值给mQueue对象。
将参数callback、async赋值给相应属性。
Looper.myLooper()返回一个Looper对象,我们来看它的实现。

Looper.myLooper()方法
myLooper方法(位置:android.os.Looper.java):

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

这里直接返回了当前线程的一个线程本地对象(线程本地对象的变量,在每个线程中都是独立存储的)。

我们来看sThreadLocal的初始化:

    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();//线程的本地对象,存储当前线程对应的Looper
    
    public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {//prepare方法只能调用一次
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));//创建Looper对象并绑定到当前线程。
    }

逻辑解析:
sThreadLocal是一个线程本地对象,存储了当前线程对应的Looper对象。
Looper类对外提供了prepare()静态方法来初始化Looper,prepare无参方法调用了prepare的有参方法。
prepare方法的参数quitAllowed:表示是否允许MessageQueue退出循环,默认参数是true,表示允许退出。
每个线程中只能调用一次prepare方法。
prepare方法最终会创建一个Looper对象,并使用线程本地对象sThreadLocal,绑定到当前线程中。
prepare方法负责Looper对象的创建以及将Looper对象绑定到当前线程,所以要想使用Handler机制,Looper.prepare方法必须在使用前调用。

Looper的构造函数:

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

构造函数做了2件事:
初始化MessageQueue对象,MessageQueue负责管理消息队列。MessageQueue使用一个单链表的数据结构来管理消息的添加和删除。
获取当前线程的引用。
小结:
Handler有多个重载版本,但可供我们使用版本的async参数都是默认值false。
Handler机制要想正常使用,必须在初始化时,调用Looper.prepare()方法。
Handler构造函数完成后,我们也就准备好了Looper对象,以及MessageQueue对象。
消息的发送
在Demo中,我们创建完成了Handler对象之后,实现了handleMessage方法来接收后台线程发来的消息。消息的发送是由Handler对象的sendMessage方法完成的,我们先来分析它的实现。

sendMessage方法:

    public final boolean sendMessage(@NonNull Message msg) {
        return sendMessageDelayed(msg, 0);
    }

这里直接调用了sendMessageDelayed方法,第二个参数为0,表示不作延迟。sendEmptyMessage等发送消息的方法,最终也会调用sendMessageDelayed方法。

sendMessageDelayed方法:

    public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
        if (delayMillis < 0) {//矫正延迟时间
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

这里调用了sendMessageAtTime方法,参数1:msg;参数2:SystemClock.uptimeMillis() + delayMillis,其中SystemClock.uptimeMillis()表示从开机到现在的毫秒数。

这里将延迟时间delayMillis,转换为了绝对时间,传递给了sendMessageAtTime方法。

sendMessageAtTime方法:

    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);
    }

逻辑解析:
这里有2个参数,msg表示要发送的消息,uptimeMillis表示要处理消息的时间(这里是绝对时间)。
获取消息队列,消息队列不允许未初始化。
调用enqueueMessage方法,让消息进入消息队列中。
enqueueMessage方法:

    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

逻辑解析:
将当前对象赋给msg的target属性。
将调用者的uid赋给msg的workSourceUid属性。
判断是否是异步消息,如果是异步,则调用setAsynchronous方法。我们在Handler初始化中得知,我们通常的应用使用的都是默认同步的,最后调用queue.enqueueMessage。
MessageQueue的enqueueMessage方法:

    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {//msg.target不能为null。
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {//msg不能是正在使用中
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {//同步处理
            if (mQuitting) {//消息循环正在退出
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();//消息标记为正在使用
            msg.when = when;
            Message p = mMessages;//表示队列头的消息
            boolean needWake;//该变量表示是否需要唤醒线程执行消息处理
            if (p == null || when == 0 || when < p.when) {//如果队列头消息为空,或者当前消息是插队的消息(time值为0时),或者当前消息的执行时间比队列头的要早。这里会优先执行当前消息。
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;//表示是否需要阻止唤醒loop循环
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();//如果队列头的消息是异步的,并且target为空,并且当前线程处理是阻塞的,则表示需要唤醒。
                Message prev;
                for (;;) {//这里将当前消息插入到队列中的合适位置
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {//如果消息为空或者未到执行时间,则退出循环,找到了合适的位置了
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {//如果需要唤醒属性是true并且队列头消息是异步的,则不需要唤醒。
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;//插入当前消息
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {//如果需要唤醒,则调用nativeWake方法
                nativeWake(mPtr);
            }
        }
        return true;
    }

逻辑解析:
消息的target为空,或者msg正在处理,则抛出error错误。
接下来进行同步块的处理。
当消息循环正在退出时,将当前消息回收,并退出。
对msg进行处理,设置msg的when,将msg状态置位正在使用。
将队列头消息取出。
如果队列头消息为空,或者当前消息是插队的消息(time值为0时),或者当前消息的执行时间比队列头的要早,将消息插入到队列头。
如果消息不需要立即执行,则需要插入到消息队列中。
for循环中,将当前消息插入到队列中的合适位置。
如果需要唤醒线程,则调用nativeWake方法(nativeWake是一个native方法)。
小结:
我们使用Handler对象的sendMessage()等方法进行消息的发送。
最终消息会发送到MessageQueue的enqueueMessage方法中。
如果消息不需要立即执行,则把消息添加到MessageQueue的消息队列中的合适位置。消息队列的顺序是按执行时间(绝对时间)顺序排列的。
如果消息需要立即执行,则将消息添加到队列头,并赋值给mMessages(队列头指针)。
判断线程是否需要唤醒,如果需要,则调用nativeWake(mPtr)执行唤醒。
消息循环
在消息的发送部分,消息发送后,会将消息添加到消息队列中,如果满足立即执行条件,则唤醒线程执行。

那么到了这里,你也许会问,消息的处理在哪进行的呢?将消息添加到消息队列之后,接下来怎么处理呢?

不要着急,消息的处理逻辑不在Handler里面,而是在Looper的loop方法中进行处理的,Looper和MessageQueue负责我们的消息循环部分。

Looper的loop方法:
消息循环是在调用了Looper.loop方法之后开始的,我们来看:

    public static void loop() {
        final Looper me = myLooper();//获取当前线程的Looper对象
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;//获取MessageQueue对象

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();//清空远程调用端的uid和pid,用当前本地进程的uid和pid替代

        ……

        for (;;) {
            Message msg = queue.next(); // 从消息队列中获取消息
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }
            // Make sure the observer won't change while processing a transaction.
            final Observer observer = sObserver;

            final long traceTag = me.mTraceTag;
            long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
            long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
            if (thresholdOverride > 0) {
                slowDispatchThresholdMs = thresholdOverride;
                slowDeliveryThresholdMs = thresholdOverride;
            }
            final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
            final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);

            final boolean needStartTime = logSlowDelivery || logSlowDispatch;
            final boolean needEndTime = logSlowDispatch;

            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }

            final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
            final long dispatchEnd;
            Object token = null;
            if (observer != null) {
                token = observer.messageDispatchStarting();
            }
            long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
            try {
                msg.target.dispatchMessage(msg);//执行消息处理,将消息发送给Handler对象进行处理。这里的target,是当前消息的Handler对象
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } catch (Exception exception) {
                if (observer != null) {
                    observer.dispatchingThrewException(token, msg, exception);
                }
                throw exception;
            } finally {
                ThreadLocalWorkSource.restore(origWorkSource);
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (logSlowDelivery) {
                if (slowDeliveryDetected) {
                    if ((dispatchStart - msg.when) <= 10) {
                        Slog.w(TAG, "Drained");
                        slowDeliveryDetected = false;
                    }
                } else {
                    if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                            msg)) {
                        // Once we write a slow delivery log, suppress until the queue drains.
                        slowDeliveryDetected = true;
                    }
                }
            }
            if (logSlowDispatch) {
                showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
            }

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }

逻辑解析:
获取当前线程的Looper对象。
清空远程调用端的uid和pid。
消息处理是在一个无限循环的for循环中处理的。
在for循环中,首先从消息队列中获取消息。
若消息为null,则表示消息循环队列已退出,则结束循环。
否则,执行性能相关的日志输出,我们通过logging可以实现message处理的性能检测,我们将在后续文章中分析它的实现。
调用msg.target.dispatchMessage(msg)执行消息处理,将消息发送给Handler对象进行处理。这里的target,是当前消息的Handler对象。
最后将消息对象进行回收处理。
loop方法调用Handler对象的dispatchMessage进行消息的处理,消息处理部分我们稍后分析,我们先来看消息获取的queue.next()方法。

MessageQueue的next方法
MessageQueue是一个消息队列,内部是使用单向链表来实现的,Message对象的next属性保存列表中的下一个,MessageQueue对象的mMessages属性是链表头,也就是当前要处理的消息。

MessageQueue的next方法:

    @UnsupportedAppUsage
    Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // 闲置任务的数量
        int nextPollTimeoutMillis = 0;//消息循环,休眠时间
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            nativePollOnce(ptr, nextPollTimeoutMillis);//设置休眠时间

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {//表示添加了一个消息屏障。这个屏障之后的所有同步消息都不会被执行,即使时间已经到了也不会执行。
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());//该循环会忽略同步消息,查找到异步第一个异步消息
                }
                if (msg != null) {
                    if (now < msg.when) {//没到执行时间,则设置休眠时间
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {//有消息需要处理,则返回将要处理的消息
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {//表示存在消息屏障
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {//表示队列中已经没有消息需要处理了,可以进行无限休眠
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {//如果消息循环已退出,则进行资源销毁
                    dispose();
                    return null;
                }

                ……
                //进行闲置时间的任务处理。

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }

逻辑解析:
MessageQueue的next方法负责了从消息队列中取出消息,如果没有要执行的消息,则进行休眠,直到有消息需要执行为止。

for循环内首先调用nativePollOnce(ptr, nextPollTimeoutMillis),这是一个native方法,通过Native层的MessageQueue实现休眠。

nextPollTimeoutMillis表示休眠时间:

nextPollTimeoutMillis == -1,一直休眠不会超时。
nextPollTimeoutMillis == 0,不会休眠。
nextPollTimeoutMillis > 0,最长休眠时间不超过nextPollTimeoutMillis毫秒,如果期间有程序唤醒会立即返回。
如果msg.target为null,则表示添加了一个消息屏障。这个屏障之后的所有同步消息都不会被执行,即使时间已经到了也不会执行。

如果存在消息屏障,则该循环会忽略同步消息,查找到异步第一个异步消息,并进行处理。

如果消息存在,并且当前消息没到执行时间,则设置休眠时间。否则消息需要处理,则返回将要处理的消息。

如果队列中已经没有消息需要处理了,可以进行无限休眠,等待唤醒,而唤醒操作是调用native方法nativeWake()来实现的。

如果当前队列中没有任务需要处理,则可以利用空闲资源处理一些非紧急任务(闲置任务),以执行一些非紧急的任务。

消息处理
我们来分析消息的处理,消息处理逻辑是在Looper中的loop方法调用Handler的dispatchMessage方法中进行的。

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

如果Message对象的callback属性存在,则执行callback的run方法,否则,调用handleMessage进行消息处理。

Handler的handleMessage方法

    public void handleMessage(@NonNull Message msg) {
    }

Handler的handleMessage方法是一个空方法,需要我们使用时,在子类中实现,就像demo中一样。

UI线程Handler消息机制的初始化
经过Handler的原理分析,我们了解到Handler机制在初始化阶段,需要调用Looper的prepare方法进行Looper初始化操作,并且需要调用Lopper.loop()开启消息循环。

但是我们在UI线程中使用时,并没有进行这些初始化操作,那么UI线程中,Handler机制是如何进行初始化的呢?

在Looper的方法中,我们发现有一个叫prepareMainLooper()的方法,貌似是执行UI线程Handler初始化的地方?

prepareMainLooper()方法:

    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

这里直接调用了prepare()方法,并且参数为false,表示不允许消息队列循环退出;然后进行了线程校验,如果sMainLooper已经存在,则会抛出Error;最后将Looper对象赋值给sMainLooper。

那么该方法是在什么地方?什么时候调用的呢?

ActivityThread的main方法
我们来看源码:

    public static void main(String[] args) {
        ……

        Looper.prepareMainLooper();

        ……
        Looper.loop();

        ……
    }

ActivityThread的main方法是在一个进程被创建的时候调用的,也即是说,进程创建过程中,会执行Handler的相关初始化工作。

这里调用了Looper.prepareMainLooper()方法,初始化了Looper,并关联了UI线程。
调用了Looper.loop()开启了Looper的循环。

Handler的性能问题
在使用Handler的时候,我们通常会因为使用时的不小心,造成一些内存泄漏等方面的性能问题。

下面来进行原因分析及如何来避免。

Handler的内存泄漏
原因
Handler的内存泄漏问题,通常是因为对象存活的生命周期不一致问题导致的,具体原因如下:

Handler持有了外部Activity的引用。
在Activity中启动了一个后台线程,执行耗时任务,后台线程持有Handler的引用,当任务执行时间很长时。
Activity在耗时任务执行期间结束执行了,这时,因为后台线程中持有了Handler对象,Handler对象持有Activity的引用,所以就会造成Activity在GC时,回收不掉的问题。
造成了内存泄漏。
思路
内存泄漏的原因是因为我们在执行异步任务时,持有了Activity的引用,从而导致内存泄漏问题。

那么如果我们的Handler不持有外部类的引用不就可以了吗?那么我们如何做到不持有外部Activity的引用呢?

解决方法一
解决方法参考本章开头的Demo:

Handler一定要实现一个自定义的静态内部类,这里为什么是静态内部类呢?因为非静态内部类会持有外部类的引用,而静态内部类是不持有外部类引用的。
Handler的handleMessage方法中,如果需要用到Activity的属性或对象,这时我们不得不持有Activity的引用,怎么办呢?那就是像上面我们的Demo一样,使用一个弱引用来保存Activity,原理就是,弱引用在GC时,当内存紧张时会进行回收。
Activity在Handler中使用时,一定要判断是否为null(是否已经回收)。
解决方法二
如果我们在Activity进行退出时,把相应的异步消息都进行注销,这样Handler都成为可回收状态后,我们的Activity自然也就是可回收的了,也就不会内存泄漏了。

我们可以在Activity的onDestory中调用Handler对象的removeCallbacksAndMessages()方法,参数一定要传null。
注意,需要每一个Handler对象都要执行该方法。
Handler的removeCallbacksAndMessages()方法

    public final void removeCallbacksAndMessages(@Nullable Object token) {
        mQueue.removeCallbacksAndMessages(this, token);
    }

这里调用了MessageQueue的removeCallbacksAndMessages方法。

MessageQueue的removeCallbacksAndMessages方法

    void removeCallbacksAndMessages(Handler h, Object object) {
        if (h == null) {
            return;
        }

        synchronized (this) {
            Message p = mMessages;

            // Remove all messages at front.
            while (p != null && p.target == h
                    && (object == null || p.obj == object)) {
                Message n = p.next;
                mMessages = n;
                p.recycleUnchecked();
                p = n;
            }

            // Remove all messages after front.
            while (p != null) {
                Message n = p.next;
                if (n != null) {
                    if (n.target == h && (object == null || n.obj == object)) {
                        Message nn = n.next;
                        n.recycleUnchecked();
                        p.next = nn;
                        continue;
                    }
                }
                p = n;
            }
        }
    }

该方法会清除掉MessageQueue队列中的所有消息,并且对Message进行回收。

Message的recycleUnchecked方法

    void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = UID_NONE;
        workSourceUid = UID_NONE;
        when = 0;
        target = null;//移除了handler的引用
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

该方法对Message对象进行资源回收。

总结
Android中的线程通信机制使用了Handler来实现的。
Android机制的初始化,需要调用Looper.prepare()方法进行Looper的创建及关联到当前线程,然后调用Looper.loop()方法实现消息的循环。
消息的发送是通过Handler的sendMessage()或post()等方法执行的,然后调用了MessageQueue的enqueueMessage()方法来把消息添加到消息队列中,如果需要唤醒操作,则调用native方法nativeWake进行唤醒。
MessageQueue是消息队列,内部采用单链表的数据结构来存储消息列表,链表的头是mMessages对象。
MessageQueue实现了消息队列的添加、消息的获取、消息循环的休眠、消息循环的唤醒等。
消息循环的启动是通过Looper.loop()来实现的,它启动了一个无限循环来进行消息的处理。
Looper.loop()每次循环都会调用MessageQueue的next()方法来实现消息的获取,当然如果当前没有消息需要执行,则会在next方法中调用native方法nativePollOnce()进行休眠。
MessageQueue的next()方法中实现了消息屏障机制,可以通过添加消息屏障来阻塞消息队列中同步消息的执行。
MessageQueue的next()方法中也实现了空闲任务执行机制,当线程空闲时,我们可以执行一些非紧急任务。
我们可以调用Looper.quit()或Looper.quitSafely()方法来退出消息循环,但是UI线程不可退出消息循环。
UI线程是在ActivityThread的main方法中执行的Handler机制的初始化工作,调用了Looper.prepareMainLooper()方法进行初始化和Looper.loop()方法开启消息循环。
Handler使用不当常常会导致内存泄漏等性能问题。Handler内存泄漏的原因通常是因为引用的持有及生命周期不一致导致的。
我们可以通过弱引用+静态内部类的方式来解决Handler内存泄漏的问题,或者在Activity结束时,调用Handler的removeCallbacksAndMessages(null)方法来回收Handler及消息。
另外,Message对象过多的创建也会导致内存过大,GC频繁等问题,我们可以通过Message类提供的obtain()方法来获取Message对象,obtain内部使用了一个对象池来减少新对象的创建。
通过上面的分析,我们已经对如何使用Handler消息机制和它的原理有了很清晰的了解。但是,在Looper循环的时候,loop()方法中的无限循环是如何做到不占用系统资源的呢?它的休眠和唤醒又是什么原理实现的呢?我们将在下一章进行分析~

文章标签: Android安卓优化
推荐指数:

真诚点赞 诚不我欺~

Handler线程通信机制:实战、原理、性能优化!

点赞 收藏 评论