安卓基础组件Looper - 01 通讯机制简介

发布于:2025-03-04 ⋅ 阅读:(83) ⋅ 点赞:(0)

为什么需要 Looper

main函数/普通函数执行完后,整个进程/线程也就结束了。为了让处理可执行对象的进程/线程长时间运行,需要无限循环加事件通知的机制

int main()
{
    while(true)
    {
        1. 线程进入休眠状态,等待通知;
        2. 其它地方给当前线程发送通知,线程从休眠中唤醒,读取通知,处理通知
        3. 进入下一个循环
    }

    return 0;
}

成员

MessageQueue

MessageQueue:装message的容器。

  • 使用 pool 享元设计模式。

  • 有消息处理,数量不能过多。pool会超出容量。

  • 根据时间排序,

    • message queue队列满的时候,阻塞,直到用户通过next取出消息。

    • 当出队 next方法 被调用,通知MessagQueue可以进行消息的入队。

    // frameworks/base/core/java/android/os/MessageQueue.java
        boolean enqueueMessage(Message msg, long when) {
    

生产者消费者

  • 入队 生产者:子线程 向消息队列添加消息和Handler

  • 出队 消费者工作线程:工作线程依此轮循,轮询到了MSG就会执行

    • java 执行了Looper.loop() 的线程(往往是主线程。因此MSG的消费,执行,都在主线程中)
    • cpp Looper::prepare/new Looper的线程

数量关系:(有说法是每个进程只有一个MessageQueue。但我看起来是觉得每个工作线程都有且只有一个MessageQueue)

  • java

    // frameworks/base/core/java/android/os/Looper.java
    
    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));
    }
    
    // sThreadLocal.get() will return null unless you've called prepare().
    @UnsupportedAppUsage
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    
  • native

    class Looper : public RefBase {
        Vector<MessageEnvelope> mMessageEnvelopes; // guarded by mLock
    

Looper

在线程(一般是主线程)中执行Looper.loop(),则这个线程就是“工作线程”。可以认为是MSG的消费者:所有的MSG的消费,执行都是在该线程中。

  • 调用了 Looper::loop() 的线程,会从进程的MessageQueue中取出、处理MSG,并执行Hanlder的重载。

  • 因为 MessageQueue 是线程安全的,所以可以有多个线程调用 Looper::loop(),这些线程并行的处理Hanlder。

对于app来说,这个“工作线程”就是主线程。app启动/挂断,一定是从主线程的 main函数 开始的

// frameworks/base/core/java/android/app/ActivityThread.java
public static void main(String[] args) {
    // 准备loop
    Looper.prepareMainLooper();
    Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));
    // 让loop运行起来. 一直循环,保障进程一直执行,如果退出,说明程序关闭
    Looper.loop();

数量关系:进程中可以在进程中指定一个/多个工作线程,每个工作线程拥有一个Looper

  • 一个线程只允许有一套Handler通讯机制,只有一个Looper

    在native层 一个线程new多个Looper是不建议的,但神奇的是这往往并不会出错,符合能跑就行的思想。

  • 一个进程可以有多个工作线程,从而一个进程中可有多个Looper

Handler lambda

狭义的来说

  • Handler是一个类,一个线程有多个 Handler对象 ——设计模式命令模式
  • 定义 Handler时,需要指定 Looper 意味着于此同时确定了对应的MessageQueue和工作线程。
  • 各个线程怎么区分其中的msg该让哪个Handler执行呢?
    • Handler.sendMessage时会把this(也就是哪个Handler)传递给MessageQueue。
    • 所以,才能让对应 Handler 执行其 handleMessage方法

广义来说:Handler是一种线程通信的方案。

  • 作为方案,MessageQueue和Looper也包含其中

  • 实际上是内存共享的方案

    • MessageQueue这个容器 在同一进程中的线程间是 共享 的,

    • 主线程可以通过loop死循环去不断的访问 MessageQueue。

为什么 wait/notify用处不大:因为handler已经将需要这部分功能进行了Linux层的封装 使用epoll多路IO复用进行管理。

机制图

解决线程之间的通讯:(并不跨进程)

其他跨线程通信:retrofit,eventbus,rxjava都是使用了主线程MainThread的,底层都使用了Handler机制(Looper机制)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传


网站公告

今日签到

点亮在社区的每一天
去签到