IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android Handler 底层原理 -> 正文阅读

[移动开发]Android Handler 底层原理

先需要简单理解Handler
Looper 不断的从 MessageQueue 中取出 Message 交给 Handler 来处理.
每个 Thread 对应一个 Looper, 每一个 Looper 只对应一个 MessageQueue,
每一个 MessageQueue 中有N个 Message, 每个 Message 只能定义一个 Handler 来处理。
①一个线程中只能有一个Looper,只能有一个MessageQueue,可以有多个Handler,多个Messge;
②一个Looper只能维护唯一一个MessageQueue,可以接受多个Handler发来的消息;
③一个Message只能属于唯一一个Handler;
④同一个Handler只能处理自己发送给Looper的那些Message;

1: 消息循环机制的初始化

1.1: 主线程为什么会不断循环不退出

ActivityThread.java
main(){
Looper.prepareMainLooper();// 
Looper.loop();// 这里 Looper不断的循环,所以主线程会永远的循环下去,不可退出,见 2.1
}
Looper.java
public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

1.2: 创建 MessageQueue(消息队列)

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));
    }
private Looper(boolean quitAllowed) {// 这里的 boolean=true 表示可以退出  false表示不可以退出,我们传入的是false,就表示永远循环

        mQueue = new MessageQueue(quitAllowed);// 这里创建了一个消息队列
        mThread = Thread.currentThread();
    }

MessageQueue.java
 MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();// 这里调用 Native层实现了, 这里初始化了一个 NativeMessageQueue 对象,把首地址返回保存在 mPtr里面。
    }

这样一个消息队列就创建和初始化完成了。

2: 消息循环机制

2.1:Looper的循环机制

Looper.java
public static void loop() {
for (;;) {
        Message msg = queue.next(); // queue 就是 MessageQueue 的对象,不断的取出 messag
            if (msg == null) {
  // 当 mag 为空时退出, 前面说Looper永远循环不退出,这里怎么退出了。等会会讲到。见 2.3
                return;
            }
       
       msg.target.dispatchMessage(msg);// 当mag不为空就开始分发消息, msg.target就是 Handler
}
}

2.2: Handler处理消息
Handler.java
public void dispatchMessage(@NonNull Message msg) {
      if (msg.callback != null) {// callback就是 post传进来的 Runnable
            handleCallback(msg);// 这里会调用 Runnable的 run函数,这就是 post方式发送消息的消息的处理。
        } else {
            if (mCallback != null) {// mCallback 是初始化传进来的参数,所以不为空。
                if (mCallback.handleMessage(msg)) {// 这里返回 false
                    return;
                }
            }
            handleMessage(msg);// 所以会执行这里,就是回调 app中的这个函数接受消息。
        }
}

2.3: Message msg = queue.next(); 
Looper.java
public static void loop() {
    for (;;) {
                          Message msg = queue.next();// 分析一下这里到底可不可以为空
        if (msg == null) {
           
                             return;
                          }
     }
}
MessageQueue.java
Message next() {
     final long ptr = mPtr;
        if (ptr == 0) {// mPtr 是Native层返回的一个 MessageQueue 的对象指针,ptr=0就表示这个对象空位,直接返回 return;
            return null;
        }
       int nextPollTimeoutMillis = 0;// 表示下个消息要在未来多久执行。
     for(;;){
      nativePollOnce(ptr, nextPollTimeoutMillis);// 将 nextPollTimeoutMillis 传入底层,
      // nextPollTimeoutMillis 等于0时,nativePollOnce不会阻塞,小于0会阻塞一直到wake, 大于0会等待底层指定时间,如果等待超时就返回。

        msg = msg.next;// 取出一个新消息
         if(msg != null){// 如果不为空
         msg.next = null;// 将消息从队列中移除
         return msg;// 并返回消息
           }else{
            nextPollTimeoutMillis = -1;// 如果消息为空,将这个值为 -1,就一直阻塞
           }
    }
}

3: 消息池

我们从发送一个空消息来分析消息池。

Handler.java
public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    }

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();// 重点分析这里,为什么不 new 一个 Message
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }
Message.java
public static Message obtain() {
        synchronized (sPoolSync) {
            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();
    }

// 从这里可以看出先判断消息池里有没有消息,有就从消息池里面拿,没有就 new Message。所以处理完消息并不是直接被回收了,只有达到设置的最大值消息才能被回收。这样
// 可以重复利用消息。

4:消息入队

MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
      if (msg.target == null) {// target 为空时抛出异常,这里跟消息屏障(异步消息不一样)
            throw new IllegalArgumentException("Message must have a target.");
        }
      Message p = mMessages;// mMessages 为消息池的消息。
      if (p == null || when == 0 || when < p.when) {
        msg.next = p;
                mMessages = msg;
                needWake = mBlocked;// 如果消息池中的消息为空,则把新消息放在队头
       }else{
         // 否则遍历链表按时间插入消息。
       }
      
       if (needWake) {// 根据条件来调用 Native 层唤醒 Looper。
                nativeWake(mPtr);
              }
消息队列退出时, 插入消息时, 移除同步屏障时 这三种情况会唤醒 Looper}

5: 异步消息即—同步屏障

Hnadler 消息分为同步消息和异步消息。我们一般用的就是同步消息。
同步和异步消息区别就是 Message 中的target不一样。同步消息 Message 没有 target,异步消息 Message 有 target。

5.1: 加入同步屏障

Handler.java
public Handler(boolean async);
public Handler(Callback callback, boolean async);
public Handler(Looper looper, Callback callback, boolean async);

创建Handler时 async 这个参数传 true, 这时 handler 发送的消息都是异步消息。

MessageQueue.java
public int postSyncBarrier() {// 在发送异步消息之前,先调用这个函数添加同步屏障
   return postSyncBarrier(SystemClock.uptimeMillis());
}

private int postSyncBarrier(long when) {
      synchronized (this) {
                msg.next = p;
                mMessages = msg;// 向消息队列中添加一个没有 target 的 Message。
       }
    return token;// 这里返回一个 token;
}

5.2: 移除同步屏障

MessageQueue.java
public void removeSyncBarrier(int token) {// 传入设置返回的 token。
               prev.next = p.next;
                needWake = false;
               if (needWake && !mQuitting) {// 移除异步消息后唤醒 Looper。
                nativeWake(mPtr);
            }
}

5.3: 异步消息的处理

Message next() {
     for (;;) {
             if (msg != null && msg.target == null) {// 如果遇到有同步屏障

                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());// 就遍历出最近一条的异步消息
                }
                ..........
                msg
                ..........
                else {
               mBlocked = false;// 处理完异步消息
               msg.next = null;// 移除异步消息. native 层就会调用 MessageQueue中的removeSyncBarrier移除同步屏障。
               }
                
      }
}

小结:发送异步消息前先添加一个同步屏障,在消息队列在取出消息时,遇到同步屏障就会优先处理异步消息,异步消息处理完后再处理同步消息。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-09-18 10:17:43  更:2021-09-18 10:17:55 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 16:30:32-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码