Android 基础学习(3) ---- Android 消息处理机制Handler和Looper

Android 应用消息处理机制

Android 的应用程序都是通过消息来驱动的,Android 每个线程在启动的时候,都可以在内部创建一个消息队列,然后再进入一个无限循环之中,不断检查消息队列中是否有新的消息需要处理,如果有,则会将它从消息队列中取出来,对它进行处理,否则线程就会进入睡眠等待状态,直到有新的消息需要处理为止
一个线程拥有消息队列之后,就可以进入一个消息循环之中,其他线程和这个线程本身都可以往消息队列发送消息

Android 系统主要通过MessageQueue、Looper 和 Handler 三个类来实现对Android 应用消息的处理
MessageQueue :用于描述消息队列
Looper :创建消息队列,进入消息循环
Handler :用于发送消息和处理消息

Android 应用消息处理模型.png

Looper 和 Handler 原理分析

典型的Android 消息队列Demo

代码路径:Androidxxx\cts\tests\tests\os\src\android\os\cts\LooperTest.java
在下面的 testLoop() 函数中,包含了消息队列创建,插入消息和循环处理消息的流程

    public void testLoop() throws Throwable {
        Thread t = new Thread(new Runnable() {
            public void run() {
                Looper.prepare();

                MockRunnable run = new MockRunnable();

                Handler handler = new Handler();
                Message msg = Message.obtain(handler, run);
                handler.sendMessageAtTime(msg, 0);
                assertFalse(run.runCalled);
                Looper.loop();
                assertTrue(run.runCalled);
            }
        });
        t.start();
        t.join();
    }

    private class MockRunnable implements Runnable {
        public boolean runCalled = false;

        public void run() {
            runCalled = true;
            Looper.myLooper().quit();
        }

        public void stop() {
            Looper.myLooper().quit();
        }
    }
创建消息队列

Handler 和Looper 的实现包含Native 层和 Java 层两个部分,Native 层也可以直接为线程创建Messagequeue,Java 层消息对列的创建是通过下面两个接口:
AndroidCode\frameworks\base\core\java\android\os\Looper.java

  • prepareMainLooper:为主进程创建消息队列
  • prepare:为其他子线程创建消息队列
    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, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {@link #prepare()}
     */
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

Native 层也有一个相应的 Looper类和MessageQueue类,java层中的Looper 和Messagequeue 本质上通过native 层的Looper 类和NativeMessagequeue 实现的,他们的关系如下:


Looper-Handler 类关系图.png

Java层的每个Looper 对象都包含一个MessageQueue类型的成员mQueue,每个Java层的MessageQueue 对象又包含了一个int 类型的变量 mPtr,它保存了C++ 中一个NativeMessageQueue对象的地址值,这样Java层中的MessageQueue 对象就可以和 NativeMessageQueue对象关联起来
native 层的Looper 对象还有两个int 类型的成员变量mWakeReadPipeFd,mWakeWritePipeFd,它们分别用来描述一个管道的读端文件描述符和写端文件描述符,当一个线程的消息队列没有消息需要处理的时候,它就会在这个管道读端文件描述符上进行睡眠等待,知道其他线程通过这个管道的写端文件描述符唤醒它为止
Java 层的 Looper 对象在创建的时候会同时创建一个MessageQueue 对象,创建Java 层的 MessageQueue 对象的时候,又会调用它的成员函数native_init在 native 层创建一个NativeMessagequeue 和Looper 对象,native 层的 Looper在创建的时候,会创建一个管道,读写端的文件描述符就保存在 mWakeReadPipeFd,mWakeWritePipeFd中

Native 层创建Looper:AndroidCodePath\frameworks\base\core\jni\android_os_MessageQueue.cpp

NativeMessageQueue::NativeMessageQueue() :
        mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
    mLooper = Looper::getForThread();
    if (mLooper == NULL) {
        mLooper = new Looper(false);
        Looper::setForThread(mLooper);
    }
}
消息队列循环流程

调用Java 层的 Looper.loop() 就可以进入消息循环,判断消息队列中是否有新的消息,否则进入睡眠状态


线程消息循环过程.png

Looper的loop 方法会不断调用MesssageQueue 的next 函数,判断是否有新的消息;next 函数的关键逻辑是调用nativePollOnce函数,这里传入了nextPollTimeoutMillis,用来描述消息队列中没有新的消息需要处理的时候,当前线程进入睡眠等待的时间

  • nextPollTimeoutMillis = 0,表示消息队列中没有新的消息要处理,当前线程也不进入睡眠等待状态
  • nextPollTimeoutMillis = -1,表示消息队列中没有新的消息要处理,当前线程无限处于睡眠等待状态
Message msg = mMessages;
if (msg != null) {
    if (now < msg.when) {
        // Next message is not ready.  Set a timeout to wake up when it is ready.
        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
    } else {
        // Got a message.
        mBlocked = false;
        if (prevMsg != null) {
            prevMsg.next = msg.next;
        } else {
            mMessages = msg.next;
        }
        msg.next = null;
        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
        msg.markInUse();
        return msg;
    }
} else {
    // No more messages.

上面的代码中包含了对Msg 的处理时间的判断,没有消息都会指定一个消息处理时间;
如果消息处理时间小于系统时间,表示此消息要马上处理,
如果消息处理时间大于系统时间,表示未来时间要处理该消息,计算出nextPollTimeoutMillis,在特定的时间唤醒该线程

消息队列发送过程

Android 提供了一个Handler 类,用于向线程发送消息,Handler 中包含 mLooper 和 mQueue 两个重要变量

  • final Looper mLooper;当前线程的Looper 实例,保存在ThreadLocal 中
  • final MessageQueue mQueue;当前线程Looper 实例 的MessageQueue 对象
    重要成员函数:
public final boolean sendMessage(Message msg)
/**
 * Subclasses must implement this to receive messages.
 */
public void handleMessage(Message msg) {
}
线程消息发送过程.png

这里的关键逻辑在 MessageQueue 的enqueueMessage,由于一个消息队列的消息是按照它们的处理时间从小到大进行排序的,因此,发送消息到消息对列的时候,要根据这个消息的处理时间找到它在目标消息队列的合理位置,然后再将它插入到目标消息队列中,我们分为四种情况讨论:

  • 目标消息队列为空
  • 插入消息队列的处理时间为0(需要立刻处理)
  • 插入消息队列的消息处理时间戳小于目标队列头消息的时间戳
  • 插入消息队列的消息处理时间戳大于目标队列头消息的时间戳

前三种情况,插入目标消息队列头部即可,最后一种需要插入到合适的位置上

消息队列处理过程

消息队列的处理函数可以通过 Handler 的post 接口指定,消息由 Runnable r 的 run 函数处理


线程消息处理过程.png
    /**
     * 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(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

本质都是在Message 对象中指定,或者在 Handler 创建时指定Callback,都可以得到处理

    public static Message obtain(Handler h, Runnable callback) {
        Message m = obtain();
        m.target = h;
        m.callback = callback;
        return m;
    }

    public Handler(Callback callback) {
        this(callback, false);
    }

Handler 中dispatchMessage 的关键逻辑:

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

msg.callback本质上就是 Message 对象创建时传入的Runable 类型的变量,代表Message 对应的处理函数:
dispatchMessage 判断此消息有无对应的处理函数,如果有直接进入处理函数;如果没有就会调用到Handler 的
handleMessage函数,一般我们会创建myHandler 继承Handler,进一步完善处理逻辑

/*package*/ Runnable callback;
    /**
     * Same as {@link #obtain(Handler)}, but assigns a callback Runnable on
     * the Message that is returned.
     * @param h  Handler to assign to the returned Message object's <em>target</em> member.
     * @param callback Runnable that will execute when the message is handled.
     * @return A Message object from the global pool.
     */
    public static Message obtain(Handler h, Runnable callback) {
        Message m = obtain();
        m.target = h;
        m.callback = callback;

        return m;
    }

常见的Looper 异常log

android 的logcat 系统中常见下面的Warning log:
Looper : Slow dispatch took 229ms android.ui h=com.android.server.am.ActivityManagerService$UiHandler c=null m=53
异常包含dispatch 异常和delivery异常

  • dispatch 异常:表示msg 的处理函数占用了太长时间
  • delivery异常:表示从Message 的send 到dispatch 到处理函数中间占用了太长时间
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
try {
    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;
        }
    }
}

private static boolean showSlowLog(long threshold, long measureStart, long measureEnd,
        String what, Message msg) {
    final long actualTime = measureEnd - measureStart;
    if (actualTime < threshold) {
        return false;
    }
    // For slow delivery, the current message isn't really important, but log it anyway.
    Slog.w(TAG, "Slow " + what + " took " + actualTime + "ms "
            + Thread.currentThread().getName() + " h="
            + msg.target.getClass().getName() + " c=" + msg.callback + " m=" + msg.what);
    return true;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容