首页 虚拟现实

Android主线程Looper消息循环机制深度解析:从源码到应用

分类:虚拟现实
字数: (5382)
阅读: (5024)
内容摘要:Android主线程Looper消息循环机制深度解析:从源码到应用,

相信每个 Android 开发者都听说过 主线程的 Looper 消息循环,但是真正能把它彻底搞明白的人可能不多。作为一个拥有 10 年经验的 Android 后端架构师,我经常遇到一些性能问题,追根溯源,很多都跟对 Looper 机制理解不透彻有关。今天,我们就来彻底扒一扒 Android 主线程 Looper 消息循环的底层原理,再结合具体的代码和配置解决方案,帮你构建更流畅的 Android 应用,并总结一些实战避坑经验。

问题场景重现:ANR 异常的罪魁祸首

在开发过程中,我们经常会遇到 ANR (Application Not Responding) 异常。ANR 的根本原因就是主线程被阻塞,无法及时处理消息队列中的消息。例如,我们在 onCreate 方法中执行了一个耗时的网络请求,导致主线程卡顿超过 5 秒,就会触发 ANR。 又或者,在 ViewonTouchEvent 中进行了复杂的计算,同样容易导致 ANR。 这些场景都与 Looper 消息循环息息相关,理解 Looper 的运作方式,才能更好地避免这些问题。

Looper 消息循环底层原理深度剖析

Looper 消息循环是 Android 应用程序的核心机制之一,它负责管理和调度应用程序中的消息。Android 的 UI 更新、事件处理等都依赖于主线程的 Looper 消息循环。它主要包含以下几个关键类:

  • Looper: 负责管理消息队列 (MessageQueue) 和线程,每个线程最多只能有一个 Looper。
  • MessageQueue: 消息队列,用于存储待处理的消息。采用先进先出 (FIFO) 的顺序。
  • Message: 消息对象,包含需要处理的数据和 Handler 对象。
  • Handler: 消息处理器,负责将消息发送到消息队列,并在 Looper 线程中处理消息。

Looper 的创建和初始化

在 Android 应用程序启动时,ActivityThread 会创建一个 Looper 实例并将其关联到主线程。具体来说,Looper.prepareMainLooper() 方法用于为主线程创建一个 Looper 实例。Looper.loop() 方法则启动消息循环,从消息队列中不断取出消息并进行处理。

Android主线程Looper消息循环机制深度解析:从源码到应用
public class Looper {
    // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class

    final MessageQueue mQueue;
    final Thread mThread;

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

    /**
     * Initialize the current thread as a looper.
     * This gives you a chance to create handlers that then reference
     * this Looper, before actually starting the loop.
     *
     * Be sure to call {@link #loop()} after calling this method, and
     * end it by calling {@link #quit()}.
     *
     * @throws RuntimeException if the Looper has already been prepared.
     */
    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");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

    /**
     * Initialize the current thread as a looper that will not permit
     * the Looper to be quit.  This is used for threads that run for a
     * long period of time and are not associated with an activity.  It
     * prevents the Looper from being accidentally quit.  The caller should
     * be careful to ensure that the thread is stopped when no longer needed.
     */
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

    /**
     * Return the Looper associated with the current thread.
     * Returns null if the current thread is not a Looper.
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

    /**
     * Start looping the message queue in the current thread.
     * Be sure to call {@link #quit()} to end the loop.
     */
    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        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();

        for (;;) {
            Message msg = queue.next(); // might block
            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);
            }

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getComponentName() + " " +
                        msg.what);
            }
            try {
                msg.target.dispatchMessage(msg);
                if (logging != null) {
                    logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
                }
            } catch (Exception exception) {
                if (logging != null) {
                    logging.println("!!!!! Exception to " + msg.target + " " + msg.callback + ": " + exception);
                }
                throw exception;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

            final int end = (userStart == 0) ? 0 : Trace.traceEndSection();
            if (end != 0) {
                synchronized (sLock) {
                    if (sTrimForeground) {
                        sTrimForeground = false;
                        MemoryTrimRequest.dispatchTrimMemory();
                    }
                }
            }

            msg.recycleUnchecked();
        }
    }

    /**
     * Return the main Looper, which lives in the main thread of the process.
     */
    public static @NonNull Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }


    /**
     * Quits the looper.  Causes the {@link #loop} method to terminate
     * when it next tries to obtain a message.  Pending messages may not
     * be delivered before the Looper terminates.
     *
     * You should only call this method on a Looper that you've created.
     */
    public void quit() {
        MessageQueue queue = mQueue;
        if (queue == null) {
            return;
        }
        queue.quit(false);
    }

    /**
     * Like {@link #quit()}, but safely discards any pending messages in the
     * message queue.  
     *
     * You should only call this method on a Looper that you've created.
     */
    public void quitSafely() {
        MessageQueue queue = mQueue;
        if (queue == null) {
            return;
        }
        queue.quit(true);
    }

    /**
     * Return the Thread associated with this Looper.
     */
    public @NonNull Thread getThread() {
        return mThread;
    }

    /**
     * Return the {@link MessageQueue} associated with this Looper.
     */
    public @NonNull MessageQueue getQueue() {
        return mQueue;
    }

    /**
     * Add a custom logging printer that prints the messages that the looper
     * is about to dispatch.
     */
    public void setLogging(Printer printer) {
        mLogging = printer;
    }

    /** @hide */
    public void setTraceTag(long traceTag) {
        mTraceTag = traceTag;
    }

    /** @hide */
    public @Nullable Printer getLogging() {
        return mLogging;
    }

    @Override
    public String toString() {
        return "Looper (" + mThread.getName() + ") {" + Integer.toHexString(System.identityHashCode(this)) + "}";
    }

    private final Printer mLogging;
    private final long mTraceTag;
}

MessageQueue 的enqueueMessage和next

MessageQueue 中有两个核心方法:enqueueMessagenext

  • enqueueMessage: 用于将消息添加到消息队列中。Handler 通过该方法将消息发送到 Looper 线程的消息队列。
  • next: 用于从消息队列中取出消息。如果消息队列为空,该方法会阻塞,直到有新的消息到达或者 Looper 退出。

Handler 的作用

Handler 是 Looper 机制中非常重要的一个组件。它负责将消息发送到消息队列,并在 Looper 线程中处理消息。Handler 内部持有一个 Looper 实例,通过 Looper 实例将消息发送到消息队列。

public class Handler {
    /*
     * Create a new Handler whose posted messages and runnables are not associated with
     * any particular Looper. This Handler will be bound to the Looper associated
     * with the thread it is running in.  If a Looper does not already exist for the
     * thread, one will be created for you.
     */
    public Handler() {
        this(null, false);
    }

    /**
     * Create a new Handler whose posted messages and runnables are sent to the
     * message queue of a Looper on the current thread.
     *
     * @param callback The callback interface in which to handle the message, or null.
     */
    public Handler(@Nullable Callback callback) {
        this(callback, false);
    }

    /**
     * Create a new Handler whose posted messages and runnables are sent to the
     * message queue of a Looper on the current thread.
     *
     * @param async If true, the {@link #sendMessage(Message)} methods will behave as if
     *        {@link Message#setAsynchronous(boolean)} is true for all messages posted to
     *        this handler. The {@link #post(Runnable)} methods and the
     *        {@link #sendMessageAtFrontOfQueue(Message)} methods will not be affected.
     */
    public Handler(@Nullable Callback callback, boolean async) {
        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 that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

    /**
     * Create a new Handler that can call back into the specified Looper.
     *
     * @param looper The Looper, whose message queue to use.
     */
    public Handler(@NonNull Looper looper) {
        this(looper, null, false);
    }

    /**
     * Create a new Handler that can call back into the specified Looper.
     *
     * @param looper The Looper, whose message queue to use.
     * @param callback The callback interface in which to handle the message, or null.
     */
    public Handler(@NonNull Looper looper, @Nullable Callback callback) {
        this(looper, callback, false);
    }

    /**
     * Create a new Handler that can call back into the specified Looper.
     *
     * @param looper The Looper, whose message queue to use.
     * @param callback The callback interface in which to handle the message, or null.
     * @param async If true, the {@link #sendMessage(Message)} methods will behave as if
     *        {@link Message#setAsynchronous(boolean)} is true for all messages posted to
     *        this handler. The {@link #post(Runnable)} methods and the
     *        {@link #sendMessageAtFrontOfQueue(Message)} methods will not be affected.
     */
    public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

    /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(@NonNull Message msg) {
    }

    /**
     * Handle system messages here.
     */
    public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

    /**
     * Causes the Runnable r to be added to the message queue. The runnable will
     * be run on the thread to which this handler is attached.
     *
     * @param r The Runnable that will be executed.
     * @return Returns true if the Runnable was successfully placed in to the
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean post(@NonNull Runnable r) {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

    /**
     * Causes the Runnable r to be added to the message queue. The runnable will
     * be run on the thread to which this handler is attached.
     *
     * @param r The Runnable that will be executed.
     * @param token A token identifying this task. This can be used to remove
     *        future posts of this Runnable from the queue using
     *        {@link #removeCallbacksAndMessages(Object)}.
     * @return Returns true if the Runnable was successfully placed in to the
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     * @hide
     */
    public final boolean post(@NonNull Runnable r, @Nullable Object token) {
        return sendMessageDelayed(getPostMessage(r, token), 0);
    }

    /**
     * Causes the Runnable r to be added to the message queue, to be run
     * at a specific time given by <var>uptimeMillis</var>.
     *
     * <p><b>Note:</b> The system imposes a maximum wake-up alarm frequency.
     * To reduce power consumption, the system may batch alarms together.
     * As a result, the time at which the Runnable executes may be delayed
     * (possibly significantly) behind the requested time. See
     * {@link android.os.PowerManager#setAlarmClock(android.app.AlarmManager.AlarmClockInfo, android.app.PendingIntent) setAlarmClock()}
     * for more information about time tolerances.</p>
     *
     * @param r The Runnable that will be executed.
     * @param uptimeMillis The absolute time at which the callback should run,
     *         using the {@link SystemClock#uptimeMillis()} clock.
     * @return Returns true if the Runnable was successfully placed in to the
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     *         Note that the implementation may enqueue the runnable even in cases
     *         where the looper has shut down so always check the return value.
     */
    public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
        return sendMessageAtTime(getPostMessage(r), uptimeMillis);
    }

    /**
     * Causes the Runnable r to be added to the message queue, to be run
     * at a specific time given by <var>uptimeMillis</var>.
     *
     * <p><b>Note:</b> The system imposes a maximum wake-up alarm frequency.
     * To reduce power consumption, the system may batch alarms together.
     * As a result, the time at which the Runnable executes may be delayed
     * (possibly significantly) behind the requested time. See
     * {@link android.os.PowerManager#setAlarmClock(android.app.AlarmManager.AlarmClockInfo, android.app.PendingIntent) setAlarmClock()} for more information about time tolerances.</p>
     *
     * @param r The Runnable that will be executed.
     * @param token A token identifying this task. This can be used to remove
     *        future posts of this Runnable from the queue using
     *        {@link #removeCallbacksAndMessages(Object)}.
     * @param uptimeMillis The absolute time at which the callback should run, using the
     *         {@link SystemClock#uptimeMillis()} clock.
     * @return Returns true if the Runnable was successfully placed in to the
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     * @hide
     */
    public final boolean postAtTime(@NonNull Runnable r, @Nullable Object token, long uptimeMillis) {
        return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
    }

    /**
     * Causes the Runnable r to be added to the message queue, to be run
     * after the specified amount of time elapses.
     *
     * @param r The Runnable that will be executed.
     * @param delayMillis The delay (in milliseconds) until the Runnable
     *            will be executed.
     * @return Returns true if the Runnable was successfully placed in to the
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }

    /**
     * Causes the Runnable r to be added to the message queue, to be run
     * after the specified amount of time elapses.
     *
     * @param r The Runnable that will be executed.
     * @param token A token identifying this task. This can be used to remove
     *        future posts of this Runnable from the queue using
     *        {@link #removeCallbacksAndMessages(Object)}.
     * @param delayMillis The delay (in milliseconds) until the Runnable
     *            will be executed.
     * @return Returns true if the Runnable was successfully placed in to the
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     * @hide
     */
    public final boolean postDelayed(@NonNull Runnable r, @Nullable Object token, long delayMillis) {
        return sendMessageDelayed(getPostMessage(r, token), delayMillis);
    }

    /**
     * Remove any pending posts of Runnable r that are in the message queue.
     */
    public final void removeCallbacks(@NonNull Runnable r) {
        mQueue.removeMessages(this, r, null);
    }

    /**
     * Remove any pending posts of Runnable r with Object token that are in
     * the message queue.
     *
     * @hide
     */
    public final void removeCallbacks(@NonNull Runnable r, @Nullable Object token) {
        mQueue.removeMessages(this, r, token);
    }

    /**
     * Remove any pending posts of callbacks and clear sent messages whose
     * {@link Message#what what} value is token.
     *
     * @param token The value to match against {@link Message#what what}.
     */
    public final void removeMessages(int what) {
        mQueue.removeMessages(this, what, null);
    }

    /**
     * Remove any pending posts of callbacks and clear sent messages whose
     * {@link Message#what what} value is token.
     *
     * @param what The value to match against {@link Message#what what}.
     * @param object to match against.
     */
    public final void removeMessages(int what, @Nullable Object object) {
        mQueue.removeMessages(this, what, object);
    }

    /**
     * Remove any pending posts of callbacks and sent messages whose
     * {@link Message#obj obj} is <var>object</var>.
     * @param object The object to match against.
     */
    public final void removeCallbacksAndMessages(@Nullable Object object) {
        mQueue.removeCallbacksAndMessages(this, object);
    }

    /**
     * Check if there are any pending posts of callbacks/messages with token in
     * the message queue.
     */
    public final boolean hasMessages(int what) {
        return mQueue.hasMessages(this, what, null);
    }

    /**
     * Check if there are any pending posts of callbacks/messages with token in
     * the message queue.
     */
    public final boolean hasMessages(int what, @Nullable Object object) {
        return mQueue.hasMessages(this, what, object);
    }

    /**
     * Check if there are any pending posts of the runnable r in the message queue.
     */
    public final boolean hasCallbacks(@NonNull Runnable r) {
        return mQueue.hasMessages(this, r, null);
    }

    /**
     * Send a Message to the Handler.
     *
     * @param msg The Message to send.
     * @return Returns true if the message was successfully placed in to the
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean sendMessage(@NonNull Message msg) {
        return sendMessageDelayed(msg, 0);
    }

    /**
     * Send a Message to the Handler, arranging for it to be delivered after the
     * specified amount of time elapses.
     *
     * @param msg The Message to send.
     * @param delayMillis The delay (in milliseconds) until the message will be delivered.
     * @return Returns true if the message was successfully placed in to the
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

    /**
     * Send a Message to the Handler, arranging for it to be delivered at the
     * given uptimeMillis.  You can think of uptimeMillis as the absolute time
     * at which the message should be delivered.
     *
     * @param msg The Message to send.
     * @param uptimeMillis The absolute time at which the message should be delivered,
     *         using the {@link SystemClock#uptimeMillis()} clock.
     * @return Returns true if the message was successfully placed in to the
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    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);
    }

    /**
     * Enqueue a message into the MessageQueue after all pending messages
     * before the front of the queue.
     * @param msg The Message to send.
     * @return Returns true if the message was successfully placed in to the
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     */
    public final boolean sendMessageAtFrontOfQueue(@NonNull Message msg) {
        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, 0);
    }

    /**
     * Perform enqueuing of message into the message queue.
     *
     * This is separated from the sendMessageAtTime function so that
     * we can prevent the end-user from overriding it.
     *
     * @param queue The MessageQueue to enqueue into.
     * @param msg The Message to enqueue.
     * @param uptimeMillis The absolute time at which the message should be delivered,
     *         using the {@link SystemClock#uptimeMillis()} clock.
     * @return Returns true if the message was successfully placed in to the
     *         message queue.  Returns false on failure, usually because the
     *         looper processing the message queue is exiting.
     *         Note that the implementation may enqueue the runnable even in cases
     *         where the looper has shut down so always check the return value.
     *
     * @hide
     */
    protected boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) {
        msg.target = this;
        msg.asynchronous = mAsynchronous;
        return queue.enqueueMessage(msg, uptimeMillis);
    }

    /**
     * Callback interface you can use when instantiating a Handler to avoid
     * having to subclass it to define the handling behavior.
     */
    public interface Callback {
        /**
         * @param msg A {@link Message Message} object
         * @return True if no further handling is desired
         */
        boolean handleMessage(@NonNull Message msg);
    }

    /**
     * Field mLooper
     */
    private final @NonNull Looper mLooper;
    /**
     * Field mQueue
     */
    final @NonNull MessageQueue mQueue;
    /**
     * Field mCallback
     */
    final @Nullable Callback mCallback;
    final boolean mAsynchronous;
    /**
     * Field FIND_POTENTIAL_LEAKS
     */
    private static final boolean FIND_POTENTIAL_LEAKS = false;
    private static final String TAG = "Handler";

    private static Handler.Callbacks[] sCallbacks = null;

    /**
     * @hide
     */
    protected Handler getHandler() {
        return this;
    }

    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

    private static Message getPostMessage(Runnable r, Object token) {
        Message m = Message.obtain();
        m.obj = token;
        m.callback = r;
        return m;
    }

    /**
     * @hide
     */
    public interface Callbacks {
        void binderDied(Handler h);
    }
}

具体的代码/配置解决方案:优化消息处理

  1. 避免在主线程执行耗时操作: 这是最重要的一点。任何可能阻塞主线程的操作,例如网络请求、I/O 操作、复杂计算等,都应该放在子线程中执行。可以使用 AsyncTaskExecutorServiceIntentService 等方式来创建子线程。

    Android主线程Looper消息循环机制深度解析:从源码到应用
  2. 使用 HandlerThread 处理后台任务: 如果需要在后台线程中执行一些循环任务,可以使用 HandlerThreadHandlerThread 会创建一个带有 Looper 的线程,可以避免手动管理线程的生命周期。

    HandlerThread handlerThread = new HandlerThread("MyHandlerThread");
    

handlerThread.start(); Handler handler = new Handler(handlerThread.getLooper()) { @Override public void handleMessage(Message msg) { // 在后台线程中处理消息 } };

// 发送消息到后台线程 handler.sendMessage(message); ```

Android主线程Looper消息循环机制深度解析:从源码到应用
  1. 使用 Message 的 target 属性减少 Handler 创建: 创建大量的 Handler 实例会消耗资源。可以通过复用 Message 对象,并设置其 target 属性来减少 Handler 的创建。

    // 创建一个 Handler
    

Handler handler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { // 处理消息 } };

// 创建一个 Message Message message = Message.obtain(); message.target = handler; // 设置 Message 的 target 为 Handler message.what = 1; message.obj = "Hello";

Android主线程Looper消息循环机制深度解析:从源码到应用

// 将消息发送到消息队列 message.sendToTarget(); // 调用 sendToTarget() 方法

```
  1. 使用线程池优化并发: 使用 ExecutorService 可以更灵活地管理线程,避免频繁创建和销毁线程带来的开销。 配合 Future 对象可以获取异步任务的结果。 在实际应用中,线程池的配置(核心线程数、最大线程数、队列容量等)需要根据应用的负载情况进行调整,监控 CPU 利用率、线程数量等指标, 达到最佳性能。

实战避坑经验总结

  • 不要在 Handler 的 handleMessage 方法中执行耗时操作handleMessage 方法运行在 Looper 线程中,如果在该方法中执行耗时操作,会导致 Looper 线程阻塞,影响 UI 的响应速度。
  • 注意 Handler 的内存泄漏问题: 如果 Handler 是非静态内部类,并且持有 Activity 的引用,可能会导致 Activity 无法被回收,造成内存泄漏。可以将 Handler 定义为静态内部类,并使用 WeakReference 来持有 Activity 的引用。
  • 合理设置 Message 的优先级: 可以通过 Message.when 属性来设置消息的优先级,确保重要的消息能够及时被处理。
  • 避免过度使用 postDelayed: 大量使用 postDelayed 可能导致消息堆积,影响消息的及时处理。 应该尽量使用精确的定时器, 例如 AlarmManager
  • 使用 TraceView 和 Systrace 分析性能瓶颈:当应用出现性能问题时,可以使用 TraceView 和 Systrace 等工具来分析性能瓶颈,找出导致主线程阻塞的原因。这些工具能够提供详细的 CPU 使用情况、函数调用栈、线程状态等信息, 帮助开发者快速定位问题。

掌握 主线程的 Looper 消息循环 的原理,熟练运用 Handler、MessageQueue 和 ThreadLocal 等关键类,并结合实战经验,才能写出高性能、高可靠性的 Android 应用。 希望这篇文章能帮助你对 Looper 机制有更深入的理解。

Android主线程Looper消息循环机制深度解析:从源码到应用

转载请注明出处: 键盘上的咸鱼

本文的链接地址: http://m.acea2.store/blog/266068.SHTML

本文最后 发布于2026-04-16 09:56:32,已经过了11天没有更新,若内容或图片 失效,请留言反馈

()
您可能对以下文章感兴趣
评论
  • 彩虹屁大师 2 天前
    HandlerThread 这个技巧很实用,之前都是自己手动创建带 Looper 的线程,太麻烦了。
  • 橘子汽水 11 小时前
    有没有关于 Looper 源码分析的推荐书籍或者博客?想更深入地了解。
  • 红豆沙 6 天前
    文章深度够,但是代码示例如果能更贴近实际项目就好了,比如结合 Retrofit + RxJava 的使用场景。
  • 路过的酱油 3 天前
    HandlerThread 这个技巧很实用,之前都是自己手动创建带 Looper 的线程,太麻烦了。