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机制及源码

唠叨一下:

Android大部分底层机制并不难,但在学习过程中如果运用线性思维且抓不住重点就容易陷入到海量的源码中。具有化繁为简、理解原理并非死记硬背的能力才能在繁杂的Android体系中逐步建立自己的知识框架。

Handler定义:

Handler官方定义的两个主要功能: (1)定时执行Message和runnable (2)将任务插入另一个线程的队列中

Handler流程:

构造Handler需要一个Looper,每个线程只能有一个Looper,Looper中包含一个MessageQueue。Handler发送消息时调用了MessageQueue的enqueueMessage方法将消息入列。Looper通过loop方法取出消息,并在Handler的handleMessage方法中处理。

Handler构造函数:

Handler的构造方法最后都会调用两个方法 Handler(Callback callback, boolean async) 和Handler(Looper looper, Callback callback, boolean async)。第一个方法会去获取当前线程的Looper,未找到则报错。

Looper:

非主线程默认没有消息队列,需要调用Looper.prepare方法去创建。下图可以看到通过sThreadLocal.set() 和 get()可以创建和获取当前线程的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");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

ThreadLocal:

ThreadLocal提供线程本地变量,这些变量是线程隔离的。下图可见Thread和Looper是通过键对值的方式储存在ThreadLocalMap中的。ThreadLocalMap的源码比较难搞,继续了解的话推荐一篇博客ThreadLocal源码解读 - 活在夢裡 - 博客园

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

?消息入列:

Handler调用post、postDelay等方法最后均会走到MessageQueue的enqueMessage方法。下图if()中当消息队列为空或该消息不是延迟消息或时间早于头消息则将该消息做为头消息。其情况进入else()则遍历整个队列,找到合适的位置插入。

boolean enqueueMessage(Message msg, long when) {
            ...
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                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;
                ...
            }

        }
        return true;
    }

消息取出:

Looper通过loop方法取出消息,关键代码如下。这里的target指要发送的Handler

public static void loop() {
  ...
  for (;;) {
     Message msg = queue.next(); 
     ...
     try {
          msg.target.dispatchMessage(msg);
     }
 }

}

上图next方法调用的是MessageQueue的next()。关键代码如下

Message next() {
                if (msg != null) {
                    if (now < msg.when) {
                        // 时间未到,设置timeout继续休眠
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // 消息出列
                        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 {
                    // 队列没有消息,永久休眠
                    nextPollTimeoutMillis = -1;
                }
}

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-10-22 11:03:19  更:2021-10-22 11:05:10 
 
开发: 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/24 0:46:24-

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