跨线程通信-Handler

2022-07-25  本文已影响0人  未子涵

从通信机制角度看应用启动过程

从通信机制角度看应用启动过程.png

首先,让我们结合通信机制,来看看通过Launcher启动App的过程(假设为冷启动)。结合上图,其完整流程如下:

如何保证SystemServer进程一直活跃

SystemServer 是由 Zygote 进程创建的第一个进程,并且由 Zygote 反射调用其 main() 方法启动。

// SystemServer.java
/**
 * The main entry point from zygote.
 */
public static void main(String[] args) {
    new SystemServer().run();
}

private void run() {
    ...
    // Prepare the main looper thread (this thread).
    android.os.Process.setThreadPriority(
            android.os.Process.THREAD_PRIORITY_FOREGROUND);
    android.os.Process.setCanSelfBackground(false);
    Looper.prepareMainLooper();
    Looper.getMainLooper().setSlowLogThresholdMs(
            SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
    ...
    // Loop forever.
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

可以看到,在其 run() 方法中,有一个 looper ,并且是 MainLooper ,其中的无限循环,就是为了让进程一直活跃。也就是说,Looper 的价值就是让进程一直活跃

App进程启动时,在 ActivityThread 的 main() 方法中,也会启动一个 MainLooper ,这两个 Looper 有什么关系?

没有关系,它们根本就是两个不同进程的 MainLooper 。

Activity 的生命周期,都是运行在主线程上,事实上,每一个生命周期的代码,都是属于某一个 Message。为什么这么说呢,我们从 Activity 的启动来分析:

Activity详细启动流程.png

AMS 调度 Activity 的过程,最终会走到 realStartActivityLocked() 中。

Activity启动流程.png

最终又走到了 ApplicationThread 的 scheduleTransaction() 中。

// ActivityThread.java
final H mH = new H();
...
@Override
public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
    // 会走到成员变量 mH 这个 Handler 的 EXECUTE_TRANSACTION 中
    ActivityThread.this.scheduleTransaction(transaction);
}

class H extends Handler {
    ...
    public static final int EXECUTE_TRANSACTION = 159;
    ...

    String codeToString(int code) {
        if (DEBUG_MESSAGES) {
            switch (code) {
                ...
                case EXECUTE_TRANSACTION: return "EXECUTE_TRANSACTION";
                ...
            }
        }
        return Integer.toString(code);
    }
    public void handleMessage(Message msg) {
        if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
        switch (msg.what) {
            ...
            case EXECUTE_TRANSACTION:
                final ClientTransaction transaction = (ClientTransaction) msg.obj;
                mTransactionExecutor.execute(transaction);
                if (isSystem()) {
                    // Client transactions inside system process are recycled on the client side
                    // instead of ClientLifecycleManager to avoid being cleared before this
                    // message is handled.
                    transaction.recycle();
                }
                // TODO(lifecycler): Recycle locally scheduled transactions.
                break;
            ...
        }
        Object obj = msg.obj;
        if (obj instanceof SomeArgs) {
            ((SomeArgs) obj).recycle();
        }
        if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
    }
}

上面的 mTransactionExecutor.execute(transaction); 说明它就是一个消息。

为什么Acitivyt、Service生命周期都运行在主线程上,设计目的是什么?

统一规划UI的展示。

子线程:handler → sendMessage() → MessageQueue.enqueueMessage() 入队列
主线程:Looper.loop() → MessageQueue.next() → handler.dispatchMessage()

子线程将数据封装成一个 Message 对象,send 到主线程内部的 MessageQueue 中,等待被执行。

应用的启动入口 ActivityThread.main() ,做的两件最重要的事就是:将应用自身的 binder attach 到 AMS 中,以及启动主线程的 Looper。

// ActivityThread.java
public static void main(String[] args) {
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

    // Install selective syscall interception
    AndroidOs.install();

    // 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();

    // Make sure TrustedCertificateStore looks in the right place for CA certificates
    final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
    TrustedCertificateStore.setDefaultUserDirectory(configDir);

    // Call per-process mainline module initialization.
    initializeMainlineModules();

    Process.setArgV0("<pre-initialized>");

    // 准备 MainLooper
    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()));
            }
        }
    }
    // 将应用自身的 binder attach 到 AMS 中
    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);
    // 启动 MainLooper
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}

Handler 机制源码分析

Android的消息处理机制主要分为四个部分:

主要涉及三个类:

创建消息队列

整个创建过程涉及到两个类:MessageQueueLooper。它们在C++层有两个对应的类:NativeMessageQueueLooper

// Looper.java
// 注意,这个 sThreadLocal 是静态的
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static void prepare() {
    prepare(true);
}

private static void prepare(boolean quitAllowed) {
    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) {
    // 新建一个MessageQueue对象
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}
// MessageQueue.java
MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    // 通过JNI初始化C++层的NativeMessageQueue对象
    mPtr = nativeInit();
}
private native static long nativeInit();

创建过程如下所示:

  1. Looper的prepare或者prepareMainLooper静态方法被调用,将一个Looper对象保存在ThreadLocal里面。
  2. Looper对象的初始化方法里,首先会新建一个MessageQueue对象。
  3. MessageQueue对象的初始化方法通过JNI初始化C++层的NativeMessageQueue对象。
  4. NativeMessageQueue对象在创建过程中,会初始化一个C++层的Looper对象。
  5. C++层的Looper对象在创建的过程中,会在内部创建一个管道(pipe),并将这个管道的读写fd都保存在mWakeReadPipeFd和mWakeWritePipeFd中。
    然后新建一个epoll实例,并将两个fd注册进去。
  6. 利用epoll的机制,可以做到当管道没有消息时,线程睡眠在读端的fd上,当其他线程往管道写数据时,本线程便会被唤醒以进行消息处理。

总结得出,其关系如下图所示:

      +------------+     +------+
      |MessageQueue+----^+Looper|
      +-----+------+     +------+
            |                    
            |                    
            |                    
+-----------+------+     +------+
|NativeMessageQueue+^----+Looper|
+------------------+     +------+

    A----^B表示B中保存A的引用

消息循环

// Looper.java
public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    if (me.mInLoop) {
        Slog.w(TAG, "Loop again would have the queued messages be executed"
                + " before this one completed.");
    }

    me.mInLoop = true;

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

    me.mSlowDeliveryDetected = false;

    for (;;) {
        if (!loopOnce(me, ident, thresholdOverride)) {
            return;
        }
    }
}

private static boolean loopOnce(final Looper me,
        final long ident, final int thresholdOverride) {
    // 调用 MessageQueue.next() 方法读取 Message,该方法会堵塞线程直到有消息到来为止
    Message msg = me.mQueue.next(); // might block
    if (msg == null) {
        // No message indicates that the message queue is quitting.
        return false;
    }

    // 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 {
        // 通过 Handler.dispatchMessage() 分发消息
        msg.target.dispatchMessage(msg);
        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 (me.mSlowDeliveryDetected) {
            if ((dispatchStart - msg.when) <= 10) {
                Slog.w(TAG, "Drained");
                me.mSlowDeliveryDetected = false;
            }
        } else {
            if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                    msg)) {
                // Once we write a slow delivery log, suppress until the queue drains.
                me.mSlowDeliveryDetected = 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();

    return true;
}

消息循环过程如下:

  1. 首先通过调用Looper的loop方法开始消息监听。loop方法里会调用MessageQueue的next方法。next方法会堵塞线程直到有消息到来为止。
  2. next方法通过调用nativePollOnce方法来监听事件。next方法内部逻辑如下所示(简化):
    • 进入死循环,以参数timout=0调用nativePollOnce方法。
    • 如果消息队列中有消息,nativePollOnce方法会将消息保存在mMessage成员中。nativePollOnce方法返回后立刻检查mMessage成员是否为空。
    • 如果mMessage不为空,那么检查它指定的运行时间。如果比当前时间要前,那么马上返回这个mMessage,否则设置timeout为两者之差,进入下一次循环。
    • 如果mMessage为空,那么设置timeout为-1,即下次循环nativePollOnce永久堵塞。
  3. nativePollOnce方法内部利用epoll机制在之前建立的管道上等待数据写入。接收到数据后马上读取并返回结果。

总结得出,其流程如下图所示:

          +------+    +------------+  +------------------+  +--------------+                    
          |Looper|    |MessageQueue|  |NativeMessageQueue|  |Looper(Native)|                    
          +--+---+    +------+-----+  +---------+--------+  +-------+------+                    
             |               |                  |                   |                           
             |               |                  |                   |                           
+-------------------------------------------------------------------------------+               
|[msg loop]  |   next()      |                  |                   |           |               
|            +------------>  |                  |                   |           |               
|            |               |                  |                   |           |               
|            |               |                  |                   |           |               
|            |               | nativePollOnce() |                   |           |               
|            |               |    pollOnce()    |                   |           |               
|            |               +----------------> |                   |           |               
|            |               |                  |                   |           |              
|            |               |                  |                   |           |               
|            |               |                  |                   |           |               
|            |               |                  |                   |           |               
|            |               |                  |     pollOnce()    |           |               
|            |               |                  +-----------------> |           |               
|            |               |                  |                   |           |               
|            |               |                  |                   | epoll_wait()              
|            |               |                  |                   +--------+  |               
|            |               |                  |                   |        |  |               
|            |               |                  |                   |        |  |               
|            |               |                  |                   | <------+  |               
|            |               |                  |                   | awoken()  |               
|            +               +                  +                   +           |               
|                                                                               |               
|                                                                               |               
+-------------------------------------------------------------------------------+               

消息发送

在讲消息发送前,我们先看一下消息的创建:

// Handler.java
public final Message obtainMessage()
{
    // 注意这里的this,就是当前 Handler 的实例
    return Message.obtain(this);
}
// Message.java
public static Message obtain(Handler h) {
    Message m = obtain();
    // 该 Message 的 target 保存了传入的 Handler
    m.target = h;

    return m;
}

消息发送过程主要由 Handler 对象来驱动。具体就是其 sendMessage() 方法。

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

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

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

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

    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    // 将消息压入MessageQueue
    return queue.enqueueMessage(msg, uptimeMillis);
}
// MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }

    synchronized (this) {
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        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.
            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.
            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.
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

private native static void nativeWake(long ptr);
  1. Handler对象在创建时会保存当前线程的looper和MessageQueue,如果传入Callback的话也会保存起来。
  2. 用户调用handler对象的sendMessage方法,传入msg对象。handler通过调用MessageQueue的enqueueMessage方法将消息压入MessageQueue。
  3. enqueueMessage方法会将传入的消息对象根据触发时间(when)插入到message queue中。然后判断是否要唤醒等待中的队列。
    • 如果插在队列中间。说明该消息不需要马上处理,不需要由这个消息来唤醒队列。
    • 如果插在队列头部(或者when=0),则表明要马上处理这个消息。如果当前队列正在堵塞,则需要唤醒它进行处理。
  4. 如果需要唤醒队列,则通过nativeWake方法,往前面提到的管道中写入一个"W"字符,令nativePollOnce方法返回。

总结得出,其流程如下图所示:

          +-------+     +------------+   +------------------+   +--------------+                        
          |Handler|     |MessageQueue|   |NativeMessageQueue|   |Looper(Native)|                        
          +--+----+     +-----+------+   +---------+--------+   +-------+------+                        
             |                |                    |                    |                               
             |                |                    |                    |                               
sendMessage()|                |                    |                    |                               
+----------> |                |                    |                    |                               
             |                |                    |                    |                               
             |enqueueMessage()|                    |                    |                               
             +--------------> |                    |                    |                               
             |                |                    |                    |                               
             |                |                    |                    |                               
             |                |                    |                    |                               
             |                |  nativeWake()      |                    |                               
             |                |    wake()          |                    |                               
             |                +------------------> |                    |                               
             |                |                    |                    |                               
             |                |                    |    wake()          |                               
             |                |                    +------------------> |                               
             |                |                    |                    |                               
             |                |                    |                    |                               
             |                |                    |                    |write(mWakeWritePipeFd, "W", 1)
             |                |                    |                    |                               
             |                |                    |                    |                               
             |                |                    |                    |                               
             |                |                    |                    |                               
             |                |                    |                    |                               
             +                +                    +                    +                               

消息处理

在“消息循环”的源码中,我们提到了 Looper 对象的loop方法里面的queue.next方法如果返回了message,那么handler的dispatchMessage会被调用。

public void dispatchMessage(@NonNull Message msg) {
    if (msg.callback != null) {
        // 如果通过 Message 传入了 Callback 实例,则由其响应
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            // 如果新建Handler的时候传入了callback实例,那么callback的handleMessage方法会被调用
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        // 否则handler方法的handleMessage会被调用
        handleMessage(msg);
    }
}

public void handleMessage(@NonNull Message msg) {
}

总结得出,其流程如下图所示:

         +------+       +-------+                                                                   
         |Looper|       |Handler|                                                                   
         +--+---+       +---+---+                                                                   
            |               |                                                                       
            |               |                                                                       
loop()      |               |                                                                       
[after next()]              |                                                                       
+---------> |               |                                                                       
            |               |                                                                       
            |dispatchMessage()                                                                      
            +-------------> |                                                                       
            |               |                                                                       
            |               |                                                                       
            |               | handleMessage()                                                       
            |               +-------+                                                               
            |               |       |                                                               
            |               |       |                                                               
            |               | <-----+                                                               
            |               |   (callback or subclass)                                              
            |               |                                                                       
            +               +                                                                       

Handler 面试那些事儿

Handler 跨线程通信的原理

子线程到主线程通信,其实是利用了“线程间共享内存”。先看下图——Handler工作流程:

Handler工作流程.png

跨线程的核心原理:

跨线程的核心原理.png

从上图可以看到,跨线程通信明显是一个生产者消费者的设计模式。

Handler 内存泄露的原因

匿名内部类默认持有外部类的引用(activity → handler),匿名内部类只是表象,这个问题实际上是考察 JVM 的知识。

handerl 泄漏时的引用链:activity → handler → Message → MessageQueue → Looper → static sThreadLocal。static 变量就是一个 GC Root。

GC 算法

  • 引用记数法:缺陷是对象相互引用(形成一个循环)时,无法计算。
  • 可达性分析法:直接或间接的被 GC Root 持有引用。

子线程中维护的 Looper ,消息队列无消息时的处理方案是什么?有什么用?主线程呢?

这其实也是考量内存泄漏的。 虽然无消息时,并不会占用cpu,因为 Looper 中有睡眠机制,会停止 cpu 使用,但线程本身需要占用内存(比如每个线程都会有各自的工作内存),且每个应用可创建的线程是有限的,如果一个子线程中维护了一个 Looper ,而 Looper 中必然有一个死循环,只要循环在运行,该子线程就一直存在,如果在无消息时仍然不退出 looper ,就意味着这个线程本身所占用的内存也被泄漏了,并且该线程会成为一个 GCRoot ,被该线程引用的其他对象,也都会被泄漏。

// MessageQueue.java
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 会传给 native 层,当传入的是 -1 时,就会“睡眠”
        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
            ...
            if (msg != null) {
                if (now < msg.when) {
                    // Next message is not ready.  Set a timeout to wake up when it is ready.
                    // 根据最近一条消息要等待执行的时间,通过 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 {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    msg.markInUse();
                    return msg;
                }
            } else {
                // No more messages.
                // 无消息时,此变量赋值为 -1,下次循环时传入 nativePollOnce()
                nextPollTimeoutMillis = -1;
            }
            ...
    }
}

nativePollOnce() 最终会调用 native层的 MessageQueue 中的 epoll() 机制,“-1”就代表睡眠。

Handler 如何处理发送延迟消息

先看看消息队列添加消息和取消息的过程:

MessageQueue的主要函数.png

Handler 没有消息处理时是阻塞的还是非阻塞的?为什么不会有 ANR 产生?

首先要明白什么是 ANR ?以及 ANR 的分类、触发原理。

ANR超时阈值.png

其中,按键事件(input) 5秒钟未处理完时,其实不一定会触发 ANR ,只有当为获得相应时,不停点击按钮,也就是第一次按钮5秒内未处理完,又点击第二次按钮时,才会触发 ANR。

Service ANR触发机制.png

其实 ANR 是一个以 Message 展示出来的 UI。

我们使用 Message 时,应该如何创建它?

通过 obtain 获取 Message ,为什么要这么做呢,看看代码:

// Handler.java
/**
 * Returns a new {@link android.os.Message Message} from the global message pool. More efficient than
 * creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this).
 *  If you don't want that facility, just call Message.obtain() instead.
 */
@NonNull
public final Message obtainMessage()
{
    return Message.obtain(this);
}
// Message.java
/**
 * Return a new Message instance from the global pool. Allows us to
 * avoid allocating new objects in many cases.
 */
public static Message obtain() {
    synchronized (sPoolSync) {
        // 优先从 Message pool 中获取
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}

当我们看到 sPool 时,就应该明白,通过 obtain() 获取时,是优先从 Message 内部自己维护的一个 Message Pool 中获取 Message 实例的。类似于 Thread Pool 机制,这里的 Message Pool 是为了高效的消息复用,而复用则是为了避免内存抖动。这里采用了享元设计模式

内存抖动有什么问题?
内存抖动 → 内存碎片 → OOM
内存抖动 → 频繁GC(频繁STW) → 应用卡顿

Thread Pool 和 Message Pool ,都是运用享元模式,创建了一个共享内存池。享元模式的使用非常广,比如地图开发,对于接收到的位置信息,不能每次都 new 一个 Java Bean 出来,而是应该创建一个 Java Bean 的内存池出来,合理复用。再比如股票类应用、 ViewPager、RecyclerView 等等。

上一篇下一篇

猜你喜欢

热点阅读