Android线程间通信机制Handler介绍

1、handler简介

handler作为android线程间通信的机制,使用广泛,作用关键,是其基于消息驱动的根本,甚至没有handler机制UI线程都无法正常运行(后面会讲述)。

1.1 handler核心类

handler的核心类包括:消息Message、消息队列MessageQueue、处理者Handler、循环器Looper。

其中,Message 是一个存放数据的容器,包括字段(int what,Object obj,int arg1,int arg2),what是用户定义的消息代号,以便handler知道这是关于什么的,obj是用来传输任意对象的,arg1和arg2都是用来传递一些整数类型的参数;MessageQueue 是一个存放Message的单向链表;Handler 是一个处理发送消息和处理消息的媒介;Looper是一个轮询的工具,负责取出message,交给handler。

简而言之,它们彼此之间的关系为handler主动发送message到messageQueue中,Looper循环从messageQueue中取出message,再交给handler做处理。功能如下图,可作为参考:

1.2 handler使用

(1)创建handler:

java 复制代码
Handler handler = new Handler();

(2)发送消息:send或post等。

java 复制代码
handler.sendEmptyMessageDelayed(1, 1000);
handler.sendEmptyMessage(1);
handler.sendMessage(Message.obtain());
handler.sendMessageAtTime(Message.obtain(), 1000);
handler.sendMessageAtFrontOfQueue(Message.obtain());
handler.post(runnable);
handler.postDelayed(runnable, 1000);
handler.postAtTime(runnable, 1000);
handler.postAtFrontOfQueue(runnable);
...

(3)处理消息:

java 复制代码
Handler handler = new Handler(new Handler.Callback() {
  @Override
  public boolean handleMessage(Message msg) {
    if (msg.what == 1) {
​
    }
    return false;
  }
});

2、handler原理

2.1 handler运行原理

首先,将分析handler的运行原理。如下图所示,可将handler的运行状态想象为一台传送装置,Looper作为一个发动机提供动力,使得传送带MessageQueue一直运行,在传送带上放置的是一个个货物Message,这些货物由Handler放入,最后又由这个Handler接收并处理。

接下来,本文将从源码角度分析handler运行的过程。首先消息由handler的send或post方法发出,由如下源码可知,无论哪种方法都会执行sendMessageAtTime(),然后将message放入MessageQueue。

java 复制代码
    //sendEmptyMessageDelayed
    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }
    
    //sendMessageDelayed
    public final boolean sendMessageDelayed(Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    
    //sendMessageAtTime
    public boolean sendMessageAtTime(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);
    }
    
    //postDelayed
    public final boolean postDelayed(Runnable r, long delayMillis) {
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }
    
    //postAtTime
    public final boolean postAtTime(Runnable r, long uptimeMillis) {
        return sendMessageAtTime(getPostMessage(r), uptimeMillis);
    }
    
    //enqueueMessage
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //将message放入单链表的操作
        return queue.enqueueMessage(msg, uptimeMillis);
    }

而将message放入messageQueue的设计如下:

java 复制代码
--MessageQueue--  
  boolean enqueueMessage(Message msg, long when) {  
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            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) {
                // New head, wake up the event queue if blocked.
                //如果消息队列里面没有消息或者消息的执行时间比里面存在的消息还要早,消息队列会把这条消息
                //设置成第一个消息,后续首先处理这条消息。但是一般不会出现这种情况,因为系统总会有许多消
                //息需要处理。比如AMS、WMS在系统初就会创建,而里面就会有许多消息发送。
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } 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.
                //如果消息队列里面有消息(正常的情况下),那么这条消息就会被调到链表相应的地方。因为
                //messageQueue是一个按照时间大小排序的优先级队列,所以下方的for死循环就是把当前消息和
                //所有已存在的消息逐个比对时间,满足条件则放入其位置,并退出循环。
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }
​
            // We can assume mPtr != 0 because mQuitting is false.
            //如果looper阻塞或休眠中,则唤醒looper循环机制处理消息
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
​

这样handler发送消息的操作就完成了,接下来分析永动机Looper从MessageQueue中循环取出message的源码,Looper执行loop()方法实现"永动"。

java 复制代码
--Looper--
  public static void loop() {
      //从threadlocal中取到Looper
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
      //通过Looper取到MessageQueue
        final MessageQueue queue = me.mQueue;
​
        // 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();
​
        // Allow overriding a threshold with a system prop. e.g.
        // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
        final int thresholdOverride =
                SystemProperties.getInt("log.looper."
                        + Process.myUid() + "."
                        + Thread.currentThread().getName()
                        + ".slow", 0);
​
        boolean slowDeliveryDetected = false;
​
      //死循环保持程序一直运行
        for (;;) {
            //不断地从MessageQueue中取出Message         
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                //没有message就退出循环,一般不会没有消息的,不然系统就不运行了。
                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);
            }
​
            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;
            try {
                //使用Message中保存的target(Handler),执行分发消息的操作
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                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);
            }
​
            //对已经分发了的message进行回收操作,实际上是将message里面的数据清空(具体后面会再做介绍)
            msg.recycleUnchecked();
        }
    }

接下来本文将上述Looper循环从MessageQueue中取出Message,以及使用Handler分发消息的操作。首先介绍前者,如下源码所示:

java 复制代码
--Looper--
  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; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
​
            //休眠nextPollTimeoutMillis,底层使用epoll机制(linux方法)
            nativePollOnce(ptr, nextPollTimeoutMillis);
​
            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                //messageQueue中的当前message
                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.
                        //msg.when就是send或post方法传入的延迟,计算与当前时间的差值,下次循环时再            //执行nativePollOnce
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            //取出message,链表移除当前message,并向后移动一个
                            mMessages = msg.next;
                        }
                        //只保留链表头message
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        //标记为正在使用
                        msg.markInUse();
                        //返回这个message
                        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;
                }
​
                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }
​
                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }
​
            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler
​
                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }
​
                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }
​
            // 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;
        }
    }

Handler分发消息的源码如下:

java 复制代码
--Handler-- 
  public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                //在回调中实现处理消息的逻辑
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
​
-----------------------------------------------------------------------------------------
    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);
    }
​

综上所述,可以通过下图来表示handler运行的实现:

2.2 handler核心类原理

本文将再探讨handler核心类Looper、Message、MessageQueue的设计原理,对进一步理解handler提供帮助。

2.2.1 Looper

Looper最重要的特性是Looper存在于每个线程中,且每个线程只有一个Looper,我们使用的Looper主要是主线程中的。在开启app的过程中,android会通过反射执行ActivityThread的main()方法,就会创建并运行Looper,请看下面的源码分析:

java 复制代码
--ActivityThread--
 
  public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
​
        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);
​
        Environment.initForCurrentUser();
​
        // Set the reporter for event logging in libcore
        EventLogger.setReporter(new EventLoggingReporter());
​
        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);
​
        Process.setArgV0("<pre-initialized>");
​
      //创建并获取Looper
        Looper.prepareMainLooper();
​
        // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
        // It will be in the format "seq=114"
        long startSeq = 0;
        if (args != null) {
            for (int i = args.length - 1; i >= 0; --i) {
                if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                    startSeq = Long.parseLong(
                            args[i].substring(PROC_START_SEQ_IDENT.length()));
                }
            }
        }
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);
​
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
​
        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
​
        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    
      //执行loop()方法,开启循环
        Looper.loop();
​
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

创建Looper的方法Looper.prepareMainLooper();

java 复制代码
  public static void prepareMainLooper() {
        //创建Looper
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            //获取Looper
            sMainLooper = myLooper();
        }
    }
​
-----------------------------------------------------------------------------------------
    private static void prepare(boolean quitAllowed) {
      //判断ThreadLocal中有没有Looper,如果有就抛出异常
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //创建Looper并放入ThreadLocal
        sThreadLocal.set(new Looper(quitAllowed));
    }
​
-----------------------------------------------------------------------------------------
    private Looper(boolean quitAllowed) {
      //创建Looper时会创建一个MessageQueue
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
​
-----------------------------------------------------------------------------------------
    public static @Nullable Looper myLooper() {
      //从ThreadLocal获取刚刚放入的Looper
        return sThreadLocal.get();
    }
​

从以上源码就能看出每个线程只有一个Looper的原因,通过ThreadLocal存放Looper,如果其中已经存在了Looper就会抛出异常"Only one Looper may be created per thread",并且prepare()方法多次执行也会抛出异常"Only one Looper may be created per thread"。下面再分析一下ThreadLocal对于Looper唯一性的设计。

java 复制代码
--Looper--
    //每个Looper中都唯一存在着一个ThreadLocal
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
-----------------------------------------------------------------------------------------
​
--ThreadLocal--
  //获取value
    public T get() {
      //当前线程id作为key
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
-----------------------------------------------------------------------------------------
    
    //设置value
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            //key为ThreadLocal,value为Looper
            map.set(this, value);
        else
            createMap(t, value);
    }
-----------------------------------------------------------------------------------------
    
    //获取Thread中的ThreadLocalMap
    ThreadLocalMap getMap(Thread t) {
      //threadLocals为Thread中的ThreadLocalMap
        return t.threadLocals;
    }
-----------------------------------------------------------------------------------------
​
    //创建ThreadLocalMap
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
​

从上述源码得知,ThreadLocal中ThreadLocalMap的数据结构类似于hashMap,由键值对组成,key为当前ThreadLocal(this),value为Looper。每一个线程,都有一个ThreadLocalMap,它的key存放的是当前ThreadLocal,value存放的是Looper,Looper就和ThreadLocal绑定了,所以一个线程唯一对应一个Looper。

接着本文讨论一下Looper.loop()中死循环是否会导致应用卡死的疑问。其实Looper死循环与应用卡死(ANR)完全无关。对于线程即是一段可执行的代码,当可执行代码执行完成后,线程生命周期便该终止了,线程退出。而对于主线程,我们是绝不希望会被运行一段时间,自己就退出,那么如何保证能一直存活呢?简单做法就是可执行代码是能一直执行下去的,死循环便能保证不会被退出,例如,binder线程也是采用死循环的方法,通过循环方式与Binder驱动进行读写操作,当然并非简单地死循环,无消息时会休眠。但这里可能又引发了另一个问题,既然是死循环又如何去处理其他事务呢?通过创建新线程的方式。而导致ANR的原因是5秒触摸事件无响应、10秒广播接收无响应或者20秒服务无响应,Looper.loop本身不会导致应用卡死。

主线程的死循环一直运行是不是特别消耗CPU资源呢? 其实不然,这里就涉及到Linux pipe/epoll机制,简单说在主线程的MessageQueue没有消息时,便阻塞在Loop的queue.next()中的nativePollOnce() 方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。

通过上述分析得知,主线程中已经为用户做好了Looper的准备和运行工作,那么如果在子线程中使用handler,能够正常工作吗?其实不能,因为还没有创建并运行Looper,需要如下操作:

java 复制代码
        new Thread(new Runnable() {
            @Override
            public void run() {
                //需添加才能使用handler
                Looper.prepare();
                Looper.loop();
                
                Handler handler = new Handler(new Handler.Callback() {
                    @Override
                    public boolean handleMessage(Message msg) {
                        return false;
                    }
                });
            }
        });

2.2.2 MessageQueue

MessageQueue实际上是一个按时间排序的优先级队列,代码中使用单链表的形式体现。MessageQueue存放Message,并且存放Message的个数没有上限。

如上图所示,如果放入message中的延迟时间最小,则直接设置为链表头部,而如果message中的延迟时间小于其中一个节点,则将其放在这个节点之前。

java 复制代码
--MessageQueue--  
  boolean enqueueMessage(Message msg, long when) {
        ...
        needWake = mBlocked && p.target == null && msg.isAsynchronous();
        Message prev;
        for (;;) {
            prev = p;
            p = p.next;
            //放入的msg的延迟时间比messageQueue当前节点的延迟时间小,退出循环
            if (p == null || when < p.when) {
                break;
            }
            if (needWake && p.isAsynchronous()) {
                needWake = false;
            }
        }
      //将放入的msg,添加在当前节点的前面
      msg.next = p;
        //将放入的msg,添加在当前节点原前节点的后面
      prev.next = msg;
      ...
  }

2.2.3 Message

Message作为封装了许多参数的消息容器,它的参数有很多,最常用的有what、arg1、arg2、obj,全部参数见下图:

然而,通常创建Message的形式并不是通过new,而是这样的:

java 复制代码
  Message msg = Message.obtain();
  msg.what = MSG_HANDLER;
  msg.obj = "handler";
  msg.arg1 = 1;
  msg.arg2 = 2;

为什么message要这样设计呢?进入message的obtain()方法探寻一番:

java 复制代码
--Message--    
  public static Message obtain(Message orig) {
        Message m = obtain();
      //传递各个参数
        m.what = orig.what;
        m.arg1 = orig.arg1;
        m.arg2 = orig.arg2;
        m.obj = orig.obj;
        m.replyTo = orig.replyTo;
        m.sendingUid = orig.sendingUid;
        if (orig.data != null) {
            m.data = new Bundle(orig.data);
        }
        m.target = orig.target;
        m.callback = orig.callback;
​
        return m;
    }
-----------------------------------------------------------------------------------------
  
    //创建Message
    public static Message obtain() {
        synchronized (sPoolSync) {
            //查看消息复用池是否为空
            if (sPool != null) {
                //不为空则直接使用空的Message
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
      //消息复用池为空,则创建一个Message
        return new Message();
    }
-----------------------------------------------------------------------------------------
​
    //清空Message
  void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
    
      //回收Message,只需将其中的各个参数置空
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;
​
        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

原来Message使用了享元模式,利用消息复用池,如果复用池中没有message则创建,否则找寻空的message,并将我们需要的各个参数传递进去。这样的设计相对于new Message(),避免了频繁地创建和销毁message可能导致的内存抖动,这样会频繁触发GC,而垃圾回收机制会STW(stop the world),停止所有线程的运行,界面会卡顿、卡死。android系统其实无时不刻都在发送处理handler,如果频繁GC、界面卡顿,用户体验极差。

2.3 Handler内存泄露

handler会内存泄露大家都知道,那么本文就探讨一下为什么handler会内存泄露。首先,内存泄露发生的原因笼统来说就是长生命周期的对象持有了短生命周期的对象,当短生命周期的对象已经销毁,而它又被持有,所以它所占的内存其实没有释放,即对象没有正确回收。

下面本文将梳理handler的引用链。如果在Acitvity中使用如下方式创建handler,匿名内部类会持有外部类引用,即handler持有Activity

java 复制代码
    private final Handler myHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            return false;
        }
    });

从之前的介绍中得知,message持有handler,这也是goole为获取message是由哪个handler发送而设计的。

java 复制代码
--Message--
  public final class Message implements Parcelable {
        ...
        /*package*/ Handler target;
        ...
    }

然后message又会被MessageQueue 持有,而MessageQueue是Looper创建的,即Looper持有MessageQueue ,最后ThreadLocal也持有Looper。ThreadLocal是static final的常量,会被GC Root关联,不会被垃圾回收,所以即使Activity被销毁,但是ThreadLocal还没有被回收,因此内存泄露了。

java 复制代码
--MessageQueue--
  public final class MessageQueue {
        ...
        Message mMessages;
        ...
  }
-----------------------------------------------------------------------------------------
    
--Looper--
    public final class Looper {
        ...
      final MessageQueue mQueue;
        static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
        ...
            
      private Looper(boolean quitAllowed) {
          mQueue = new MessageQueue(quitAllowed);
          mThread = Thread.currentThread();
      }
    }
​

总结一下,handler的整条引用链为ThreadLocal--->Looper--->MessageQueue--->Message--->Handler--->Activity。接下来介绍一下几种常用解决handler内存泄露的方法。

(1)使用静态内部类

已知匿名内部类会持有外部类引用,所以handler持有了Activity,防止内存泄露就可以打断这里的引用。使用静态内部类就可以不持有外部类的引用,从而让handler不持有Activity。

java 复制代码
    private static class MyHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            
        }
    }
(2)使用弱引用

handler通过弱引用的方式持有Activity,当GC执行垃圾回收时,遇到Activity就会回收并释放所占据的内存单元。这样就不会发生内存泄露了。

java 复制代码
    private class MyHandler extends Handler {
        WeakReference<HelloWorldActivity> weakReference;
​
        public MyHandler(HelloWorldActivity helloWorldActivity) {
            weakReference = new WeakReference<HelloWorldActivity>(helloWorldActivity);
        }
​
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            HelloWorldActivity helloWorldActivity = weakReference.get();
            if (helloWorldActivity != null) {
​
            }
        }
    }
(3)退出时回收消息和回调
复制代码
当Activity退出后,将handler的回调和消息移除。在onStop或者onDestroy中执行即可。 
java 复制代码
    @Override
    protected void onStop() {
        super.onStop();
        myHandler.removeCallbacksAndMessages(null);
    }

传入null是移除所有回调和消息,查看handler源码可知:

java 复制代码
--Handler--    
  public final void removeCallbacksAndMessages(Object token) {
        mQueue.removeCallbacksAndMessages(this, token);
    }
-----------------------------------------------------------------------------------------
​
--MessageQueue--
    void removeCallbacksAndMessages(Handler h, Object object) {
        if (h == null) {
            return;
        }
​
        synchronized (this) {
            Message p = mMessages;
​
            // Remove all messages at front.
            //清空Message
            while (p != null && p.target == h
                    && (object == null || p.obj == object)) {
                Message n = p.next;
                mMessages = n;
                //清空Message数据
                p.recycleUnchecked();
                p = n;
            }
​
            // Remove all messages after front.
            //把message从链表中移除
            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;
            }
        }
    }

2.4 Handler延迟机制

handler的延迟机制是nativePollOnce() 开始调用的,它是native方法,然后再通过JNI调用到linux的epoll执行。接下来就简单看一下底层的实现。

java 复制代码
--android_os_MessageQueue.cpp--
    static voidandroid_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
        jintptr, jint timeoutMillis)
    NativeMessageQueue* nativeMessageQueue =
                           reinterpret_cast<NativeMessageQueue*>(ptr);
    // 取出NativeMessageQueue对象,并调用它的pollOnce
   nativeMessageQueue->pollOnce(timeoutMillis);
}
-----------------------------------------------------------------------------------------
    
    void NativeMessageQueue::pollOnce(inttimeoutMillis) {
        mLooper->pollOnce(timeoutMillis); // 重任传递到Looper的pollOnce函数
    }
-----------------------------------------------------------------------------------------
​
--Looper.cpp--
int Looper::pollOnce(int timeoutMillis, int*outFd, int* outEvents, void** outData) {
    intresult = 0;
    for(;;) { // 死循环
        ...
        // 调用pollInner函数。注意,它在for循环内部
       result = pollInner(timeoutMillis);
    }
}
-----------------------------------------------------------------------------------------
​
int Looper::pollInner(int timeoutMillis) {
    ...
#ifdef LOOPER_USES_EPOLL  // 只讨论使用epoll进行I/O复用的方式
    structepoll_event eventItems[EPOLL_MAX_EVENTS];
    // 调用epoll_wait,等待感兴趣的事件或超时发生
    inteventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS,
                                     timeoutMillis);
#else
    ......//使用别的方式进行I/O复用
#endif
        ...
}
​
​

从上述源码可得, Looper::pollOnce() 使用了有4个参数的pollOnce函数:

c 复制代码
int pollOnce(int timeoutMillis, int* outFd, int*outEvents, void** outData)

timeoutMillis参数为超时等待时间。如果为-1,则表示无限等待,直到有事件发生为止。如果值为0,则无需等待立即返回。

outFd用来存储发生事件的文件描述符。

outEvents用来存储在该文件描述符上发生了哪些事件,目前支持可读、可写、错误和中断4个事件。这4个事件其实是从epoll事件转化而来。

outData用于存储上下文数据,这个上下文数据是由用户在添加监听句柄时传递的,用来传递用户自定义的数据。

参考文档:www.kancloud.cn/alex_wsc/an...

相关推荐
BD_Marathon6 小时前
【MySQL】函数
android·数据库·mysql
西西学代码6 小时前
安卓开发---耳机的按键设置的UI实例
android·ui
maki07710 小时前
虚幻版Pico大空间VR入门教程 05 —— 原点坐标和项目优化技巧整理
android·游戏引擎·vr·虚幻·pico·htc vive·大空间
千里马学框架11 小时前
音频焦点学习之AudioFocusRequest.Builder类剖析
android·面试·智能手机·车载系统·音视频·安卓framework开发·audio
fundroid14 小时前
掌握 Compose 性能优化三步法
android·android jetpack
TeleostNaCl15 小时前
如何在 IDEA 中使用 Proguard 自动混淆 Gradle 编译的Java 项目
android·java·经验分享·kotlin·gradle·intellij-idea
旷野说16 小时前
Android Studio Narwhal 3 特性
android·ide·android studio
maki0771 天前
VR大空间资料 01 —— 常用VR框架对比
android·ue5·游戏引擎·vr·虚幻·pico
xhBruce1 天前
InputReader与InputDispatcher关系 - android-15.0.0_r23
android·ims
领创工作室1 天前
安卓设备分区作用详解-测试机红米K40
android·java·linux