Android开发人员必看的 Handler 消息处理机制(源码实战)_android 大量数据handle主线程处理

35 阅读6分钟
	//用来识别消息的字段
	public int what;
	//Message中可以携带的整形数据arg1和arg2
	public int arg1;
	public int arg1;
	//Message中可携带的对象
	public Object obj;
	//记录是哪个Handler发送的消息
	Handler target;
	//指针,指向下一个Message
	Message next;

	//缓存池
	private static Message sPool;
	//当前缓存池的大小
	private static int sPoolSize = 0;
	//缓存池最大容量
	private static final int MAX_POOL_SIZE = 50;

接下来看一下它的构造方法:

	public Message() {
    }

由源码中看到构造方法中什么都没有,然而在开发中并不建议通过 new 的方式创建出 Message 对象;Message 中由一个 static 类型的 sPool 字段,它代表了缓存池

也就是说当一个消息被处理掉之后并不会直接销毁,而是放在缓存池中,下次再需要时可以通过 Message 提供的 obtain 方法从池中得到,这样一来便减少了内存的开销,实现了资源的复用

obtain 方法有很多重载,这里只分析无参的重载,其他方法的原理是类似的:

	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();
    }

这个方法很简单,如果池子不为null则会取出池中第一个 Message(头节点)并将 sPoolSize 减一,否则返回一个 new 出来的 Message 对象

准备工作

在发送消息之前,要进行一些准备工作(初始化操作);这一点很好理解:想坐飞机要通过安检,因此首先要有一个安检机,所以在发消息之前要确保有一个 Looper 对象(MessageQueue 在 Looper 中创建);我们来看一下 Looper 的构造函数:

	private Looper(boolean quitAllowed) {
		//创建出消息队列
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

发送消息

发送消息是通过 Handler 提供的一系列 send 方法完成的,那么首先要有 Handler 对象才能发送,所以我们先来看一下 Handler 的构造方法:

	public Handler(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 " + Thread.currentThread() + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

有了 Handler 对象之后,就可以通过它提供的一系列 send、post 方法来发送消息了;不管用哪一个方法来发送消息,最终都会调用 Hander的 enqueueMessage 方法

Handle r的 enqueueMessage 方法如下所示:

	private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

可以看到不管是哪个方法,最终都会调用 MessageQueue 的 enqueueMessage 方法:

	boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            //如果目标为null抛异常
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            //如果已经在使用抛异常
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            //如果队列位null或者message的when=0或者message的when小于队列第一个元素的when
            if (p == null || when == 0 || when < p.when) {
                //此时应该将新消息插入到链表首部
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                //找到p和prev,其中p为message的下一个元素,prev为message的上一个元素
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                //将message插入到链表的对应位置
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

总的来说,一个线程中可以创建许多 Handler,然而这些 Handler 对象持有的 Looer、MessageQueue 引用指向的都是相同的对象(线程中唯一的的);这样一来,在其他线程中用这些 Handler 对象去发送消息(发出的消息持有发消息的 Handler 对象的引用),发出去的消息最终都是被放到了创建 Handler 线程中对应那个 MessageQueue 中

而创建 Handler 的线程中通过 Looper.loop 死循环不断地从消息队列中取出消息,这样便实现了线程之间的通信由于对消息的入队和出队操作都是加了锁的,因此便保证了通信的安全性

好了,以上就是今天要分享的内容,大家觉得有用的话,可以点赞分享一下;如果文章中有什么问题欢迎大家指正;欢迎在评论区或后台讨论哈~

作者:Android小丁
链接:juejin.cn/post/714833…

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
在这里插入图片描述
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

一、架构师筑基必备技能

1、深入理解Java泛型
2、注解深入浅出
3、并发编程
4、数据传输与序列化
5、Java虚拟机原理
6、高效IO
……

在这里插入图片描述

二、Android百大框架源码解析

1.Retrofit 2.0源码解析
2.Okhttp3源码解析
3.ButterKnife源码解析
4.MPAndroidChart 源码解析
5.Glide源码解析
6.Leakcanary 源码解析
7.Universal-lmage-Loader源码解析
8.EventBus 3.0源码解析
9.zxing源码分析
10.Picasso源码解析
11.LottieAndroid使用详解及源码解析
12.Fresco 源码分析——图片加载流程

在这里插入图片描述

三、Android性能优化实战解析

  • 腾讯Bugly:对字符串匹配算法的一点理解
  • 爱奇艺:安卓APP崩溃捕获方案——xCrash
  • 字节跳动:深入理解Gradle框架之一:Plugin, Extension, buildSrc
  • 百度APP技术:Android H5首屏优化实践
  • 支付宝客户端架构解析:Android 客户端启动速度优化之「垃圾回收」
  • 携程:从智行 Android 项目看组件化架构实践
  • 网易新闻构建优化:如何让你的构建速度“势如闪电”?

在这里插入图片描述

四、高级kotlin强化实战

1、Kotlin入门教程
2、Kotlin 实战避坑指南
3、项目实战《Kotlin Jetpack 实战》

  • 从一个膜拜大神的 Demo 开始
  • Kotlin 写 Gradle 脚本是一种什么体验?
  • Kotlin 编程的三重境界
  • Kotlin 高阶函数
  • Kotlin 泛型
  • Kotlin 扩展
  • Kotlin 委托

img img img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化资料的朋友,可以戳这里获取