文档章节

android6.0源码解析-handler、message和looper(一)-message

117
 117
发布于 2017/01/07 11:44
字数 1674
阅读 38
收藏 0

 

   message相对handler来说,是一个携带数据的消息类。但是本身message是一个(后进先出)链表结构的类。设计成链表的形式是为了做一个对象池,减少对象的创建和销毁,提高效率。

   在message源码里面,最主要的方法只有两个。一个是获取对象对obtain(),一个是回收对象的recycle()。其他的方法都很简单。

    obtain的作用是判断当前的Message的对象池中,是否还有对象。如果没有则创建,如果有则取一个Message对象,初始化变量,再返回。

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

这是一个静态方法,首先进入sPoolSync同步块,然后判断静态变量sPool(该变量就是message对象池这个链表的表头)是否是null。如果是null,则说明当前根本没有创建有Message对象(注意:obtain是静态方法,所Message在没有被创建时,也可以被调用)。此时直接返回一个new Message()。如果不是null,则在对象池的链表头部,取出一个对象,并返回。

 

   recycle()的作用就是检查对象是否可以被回收。是,则将对象里面的数据初始化,然后存入对象池。否,则不做任何处理(版本信息不同,可能会有异常,涉及到外部代码的调用。暂时讲解,后续遇到会详细介绍)。recycle()源码如下:

    public void recycle() {
        if (isInUse()) {//判断标识flags,是否处于被使用状态,是则返回true
            if (gCheckRecycle) {
                throw new IllegalStateException("This message cannot be recycled because it "
                        + "is still in use.");
            }
            return;
        }
        recycleUnchecked();
    }

进入方法,首先会判断当前message是否在使用状态中。其中被使用,会调用if(isInUse()),然后什么都不做,直接返回。其中gCheckRecycle如果为true会抛出异常。gCheckRecycle在message初始化时,默认是为true。只有在updateCheckRecycle(int targetSdkVersion)方法时,才会被设置为false。

    public static void updateCheckRecycle(int targetSdkVersion) {
    //当前版本低于21时,gCheckRecycle为false,否则gCheckRecycle为true
        if (targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
            gCheckRecycle = false;
        }
    }

  代码很简单,版本号低于21时,    gCheckRecycle会设置为false。至于什么时候,以及为什么会调用方法updateCheckRecycle,后续我们涉及到的时候会再详细讲解。

当isInUse()返回false时,会调用方法recycleUnchecked();真正将对象回收到对象池中,就是通过该方法。

    void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

从源代码中可以看出,当前对象被回收到对象池中,flags会被重置为被使用中(为什么不重置为没有被使用的状态了?),其他变量都进行了初始化。

进入同步模块了(sPoolSync),对象池中,对象的数量不大于MAX_POOL_SIZE(值是50)的数量,则将新对象放在链表的头部。

这里的sPool是一个Message对象,如果值不是null,则sPool是指向当前对象。next也是一个Message对象,如果值不为null,则next是指向下一个Message对象。通过next,就形成了一个单向链表。用图来尸解一下这个过程

如果使用new Message()来创建对象,sPool和next都是null,没什么可以分析的。我们只分析对象池中有对象的情况。

 

OK,现在两个方法都分析了,现在开始我们将单个对象从被创建,然后存入对象池,然后再被循环使用的流程画出来。

1,第一次调用obtain时,首先判断sPool表头是null,表明此时没有Message对象。我们通过new Message()获得一个对象。

2,此时Message对象被回收到对象池中(调用方法recycleUnchecked()),此时的sPool作为对象池的表头,就指向Message对象。

3,好了,此时如果我们再调用obtain方法。进入方法的同步模块,会首先判断sPool是否为null,如果不是,则重置flag标识为未被使用,将sPool传递给一个新的对象m(对象m没有被创建,只是一个索引,和sPool指向同一个对象。),再将next传递给sPool(next一直都是null)。此时,m拥有了一个对象,mSpool和next再次变为null。

此时obtain返回message对象。

 

什么情况下,会在对象池中产生多个对象了?

1,此时对象池中没有任何对象,我们调用obtain,会new Message()。此时的message对象如果没有调用方法recycle(),则我们再调用obtain时,又会new Message();多次调用obtain,此时内存中产生了N个message的对象,但是互相并没有联系。就像下面这样。

2,下面,其中一个(比如对象1)对象被调用了recycleUnchecked()方法,表示该对象可以放在对象池了。则变成了下面的样子

看没,表头sPool指向了对象1。

3,如果此时,对象2也被调用了recycleUnchecked()方法,代码还记得嘛?

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }

先将表头sPool指向的对象赋值给对象2的next,然后再让表头sPool指向对象2.变成了下面的样子

后进去的,排在最前面。

4,依次调用对象3、对象4的recycleUnchecked()方法,则此时的对象池是下面这样

5,这下对象池是不是有对象了?好吧,此时我们开始调用obtain了,还记得对象池有对象时,调用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;
            }
        }

首先检查表头sPool是指向的null还是指向一个对象。我们是有对象的人,对吧?所以将sPool指向的对象(看上面4图,现在sPool指向的是对象4)从对象池中提取出来并返回。

看没,对象4被obtain返回了,不在对象池中。此时的对象池只剩下对象3、对象2、对象1.

依次再调用obtain了,还会从对象池中取出对象。直到对象池中再无对象,obtain就会创建新的对象,如果被回收,则会再次被放在对象池中。

message源码的分析,大体就到这。其他方法没有分析,是因为其他方法简单,一看就能看懂。

© 著作权归作者所有

117

117

粉丝 11
博文 50
码字总数 41049
作品 0
福州
私信 提问
Android异步消息处理机制完全解析-Handler详解

参考资料 - 官方介绍文档 - Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系 - Android异步消息处理机制完全解析,带你从源码的角度彻底理解 - 慕课网课程-Androi...

javen205
2017/03/25
0
0
源码解析--深入理解 Looper、Handler、Message三者关系

Android 异步消息处理机制 1.概念:异步消息处理机制也可称作线程之间的通信 Handler 、 Looper 、Message 这三者都与Android异步消息处理线程相关的概念。那么什么叫异步消息处理线程呢? ...

android-key
2016/11/22
6
0
Android Handler 机制 - Looper,Message,MessageQueue

Android Studio 2.3 API 25 从源码角度分析Handler机制。有利于使用Handler和分析Handler的相关问题。 Handler 简介 一个Handler允许发送和处理Message,通过关联线程的 MessageQueue 执行 ...

rustfisher
2017/08/02
0
0
android基础知识02——线程安全3:Message,MessageQueue,Handler,Looper

android的UI操作不是线程安全的,同时也只有主线程才能够操作UI,同时主线程对于UI操作有一定的时间限制(最长5秒)。为了能够做一些比较耗时的操作(比如下载、打开大文件等),android提供...

迷途d书童
2012/03/23
737
0
Android Looper、Message、Handler、MessageQueue源码解析

在Android开发过程中,和是经常会使用到的类,我们对它们广泛的认知是为了在异步任务中更新主线程的UI,因为在主线程处理耗时的任务是会ANR的。和普遍使用的场景是HTTP请求,HTTP请求可能会造...

thanatosx
2016/10/10
96
0

没有更多内容

加载失败,请刷新页面

加载更多

Bash 和 Python 编程语言优缺点分析

Bash 和 Python 是大多数自动化工程师最喜欢的编程语言。它们都各有优缺点,有时很难选择应该使用哪一个。所以,最诚实的答案是:这取决于任务、范围、背景和任务的复杂性。 让我们来比较一下...

xiangyunyan
29分钟前
4
0
Kubernetes从懵圈到熟练:读懂这一篇,集群节点不下线

排查完全陌生的问题,完全不熟悉的系统组件,是售后工程师的一大工作乐趣,当然也是挑战。今天借这篇文章,跟大家分析一例这样的问题。排查过程中,需要理解一些自己完全陌生的组件,比如sys...

阿里云云栖社区
34分钟前
10
0
解决exe4打包出现的问题

https://blog.csdn.net/gem_yaorao/article/details/48626155

南桥北木
51分钟前
1
0
SpringBoot高级篇JdbcTemplate之数据更新与删除

前面介绍了JdbcTemplate的插入数据和查询数据,占用CURD中的两项,本文则将主要介绍数据更新和删除。从基本使用上来看,姿势和前面的没啥两样 <!-- more --> I. 环境准备 环境依然借助前面一...

小灰灰Blog
今天
3
0
Filecoin 编译问题

https://github.com/filecoin-project/go-filecoin/issues/2503 Error go run ./build build command from root I've faced on this error Building go-filecoin...git log -n 1 --forma......

怎当她临去时秋波那一转
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部