跨线程通信-Handler

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

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

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

  • Launcher 通过所持有的 AMS 的 Binder 调用 AMS 的 startActivity() 方法 → Binder 通信
  • AMS 检查目标 App 进程是否已启动,若没有启动,则向 Zygote 进程发送创建新进程的请求 → Socket 通信
  • Zygote 进程 fork 一个子进程(即 App 进程),之后并非立即启动,而是经历了以下流程:
    • 创建 App 进程的 Binder
    • 通过反射创建 App 进程入口函数(main)的 Method 对象,并返回给上层调用方
  • App 进程启动过程中,将自己的 binder 反向 attach 到 AMS 中管理起来 → Binder 通信
  • AMS 通过 App 的 binder 执行其 Activity 的启动流程 → Binder 通信
  • 当进入到 App 进程内部后,之后的通信就可以通过 Handler 进行了

如何保证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的消息处理机制主要分为四个部分:

  • 创建消息队列
  • 消息循环
  • 消息发送
  • 消息处理

主要涉及三个类:

  • MessageQueue
  • Looper
  • Handler

创建消息队列

整个创建过程涉及到两个类: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会被调用。

  • 如果新建Handler的时候传入了callback实例,那么callback的handleMessage方法会被调用。
  • 如果是通过post方法向handler传入runnable对象的,那么runnable对象的run方法会被调用。
  • 其他情况下,handler方法的handleMessage会被调用。
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 ,被该线程引用的其他对象,也都会被泄漏。

  • 子线程没有消息时的处理方案:调用 looper.quit() 退出循环
  • 用处:停止循环,退出线程
  • 主线程没有消息时怎么处理:不能退出循环,只能让其休眠。
// 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

  • 消息队列中的消息按待执行时间排序
  • 队头是最早要执行的消息
  • 当队头消息还未到达执行时间时,计算剩余等待时间,并通过 nativePollOnce 传给 native 层的 epoll() ,到时自动唤醒

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

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

ANR超时阈值.png

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

Service ANR触发机制.png

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

  • 没有消息处理时是“阻塞”的,这才才会“睡眠”,释放 cpu。
  • ANR 的产生是因为做了个“定时炸弹”,而 handler 中没有做定时炸弹,它的阻塞就是因为没有消息处理了,它要休眠,交出 cpu

我们使用 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 等等。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

未子涵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值