Android进阶学习——Message复用机制原理

最近在学习Android消息机制方面的原理,到了Message这块的时候发现少有文章进行分析,本着“生死看淡不服就干”的原则,我深入了源码层面,对Message巧妙的复用机制进行了解析,并写下这篇文章,供大家分享学习,如果感觉对你有帮助,欢迎点赞,私信,关注我,您的鼓励是对我最大的帮助。

在日常的Android开发中,我们经常会使用Handler来进行主子线程之间的消息传递,其中我们用的最多的就是Message对象。通过查阅官方文档或者代码注释我们可以发现,对于Message,官方建议我们使用Message.obtain来进行Message对象的获取。

那么问题来了,为什么Google会建议我们使用Message.obtain来进行对象的获取而不是让我们自己new一个Message出来呢?或者说,通过Message.obtain来获取Message对象有没有什么好处呢?

其实答案都在源码里了,谈到这里,我们就不得不提一种设计模式——享元模式,这是一种和单例设计模式很相似的设计模式,具体的细节在这里就不讲了,感兴趣的话欢迎移步菜鸟教程——享元模式

下面还是让我们从源码开始吧,Message复用原理的入口在Message.obtain中:

/**
        从Message池中返回一个Message对象,并且允许分配新的实例
     */
    public static Message obtain() {
        //先是加了一把对象锁,这个锁是使用Object来实现的
        synchronized (sPoolSync) {
            if (sPool != null) {
                //如果Message池中非空,就从池子里取出一个Message
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        //如果Message池为空,就新建一个Message对象出来
        return new Message();
    }

在obtain的代码中,我们可以看到,先是判断了一把Message Pool是不是为空,如果非空的话就从池子里取出之前用过的Message进行复用,并且异步地对池子里 Message的数量进行了更新。但是如果Message池为空的话说明还没有Message被放进去,那么就new出来一个Message对象好了。

到这里,仅仅是对之前使用过的Message进行了取出,或者新建一个Message的动作,还没有看到Message到底是怎么被回收的,别急,让我们移步Message的recycle方法。

public void recycle() {
        if (isInUse()) {
            if (gCheckRecycle) {
                throw new IllegalStateException("This message cannot be recycled because it "
                        + "is still in use.");
            }
            return;
        }
        //最终调用到了这个方法
        recycleUnchecked();
    }

    /**
     * Recycles a Message that may be in-use.
     * Used internally by the MessageQueue and Looper when disposing of queued Messages.
        这句话的意思是:一般来说这个方法是被内部的MessageQueue和Looper在分发排序的Message时候来使用的
     */
    void recycleUnchecked() {
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
        //先把这个Message标记为使用状态,防止被二次回收
        //然后把这个Message的相关状态进行了手动复位
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;
        //Message的复用机制使用了链表数据结构来实现
        //但神奇的是这个链表没有借助任何一种数据结构如LinkedList等实现,而是通过指针的形式,将不        
        //同的Message对象进行了串联
        //在这里,先是判断了一把,如果当前Message池子的大小还没到限制值
        //那么就把当前Message池子的头指向了当前Message实例的sPool字段,也就是Message池的头        
        //部,当然,next字段和Message池子都属于Message类型
        //并且更新了池子的大小
        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

从代码中我们可以看到,Message的复用机制没有使用任何一种数据结构,如LinkedArrayList,而是通过Message对象内部的spool和next字段,通过指针的方式来进行对象管理,不得不说,是一种非常巧妙的设计方式,一来降低了设计复杂度,而且由于没有创建额外的数据容器来管理对象,减轻了内存的压力,实现了轻量化的目的。

具体的实现过程我找到了一幅图来进行说明:

在Message池初始状态下,如果一个Message来到了池子里,他肯定先是被清空了数据,然后将他的sPool字段指向了Message池的头部,而next字段指向了空,当第二个Message被回收进来之后,m2的next字段指向了m1,原来m1的next字段仍然保持不变,而Message池子的头部也就是sPool指向了m2。

这样周而复始地循环,也就完成了Message的链表,进而实现了Message的复用目的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值