Android 内存优化-对象池★

1.对象池

内存优化不仅要防止内存泄露,也要注意频繁GC卡顿、内存抖动以及不必要的内存开销造成的内存需求过大或者内存泄露。

比如,如果有大量临时对象的创建该如何处理呢?

首先要确定问题发生的原因,对象频繁创建一般有以下几种可能:

①在循环操作中创建对象

②在频繁被调用的方法中创建对象

例如:在onDraw中使用一些对象,由于onDraw方法会被程序频繁调用,所以不能在onDraw方法里创建对象实例,可以考虑在onDraw方法外提前初始化这些对象。

能直接避免对象的频繁创建当然最好,但是有时候这些对象的初始化是不可避免的,这时就要考虑对象的复用,可以采用对象池来解决问题。对象池可以很好的复用对象,避免频繁创建和销毁。

对象池的目的就是减少频繁创建和销毁对象带来的成本,实现对象的缓存和复用。当创建对象的成本比较大,并且创建比较频繁时,就需要使用对象池了。

Android中对象池的应用有很多,比如:

①Handler处理的Message(在handler发送消息时,Message的初始化经常会用 Message.obtain()来实例化Message对象)

②线程池执行器ThreadPoolExecutor

③Glide

④控制TypedArray的Resources

虽然它们对线程池的实现方式不同(Message使用链表、Glide使用Map),但原理是一样的,即初始化一个固定大小的池子,每次创建对象时先去池子中找有没有,如果有直接取出,没有就new出来,使用后还到池子里。这样便可达到对象复用的目的。

以一个简单的获取StyledAttributions代码为例,展示一下对象池的应用:

final TypedArray ta = context.obtainStyledAttributes(mTabTextAppearance,R.styleable.TextAppearance);

try {

    mTabTextSize = ta.getDimensionPixelSize( R.styleable.TextAppearance_android_textSize, 0);

    mTabTextColors = ta.getColorStateList( R.styleable.TextAppearance_android_textColor);

} finally {

    ta.recycle();

}

每次使用TypedArray时要特别注意:TypedArray结束的时候,一定要调用它的recycle()方法。而调用recycle()方法的原因就是因为它使用了对象池。调用者通过obtain()方法从对象池中获取对象,使用完毕后,就需要使用recycle()方法返还给对象池。

 

2.对象池工作过程

3d3189514bab4bf287404db4734ca068.jpg

Pool表示对象池,用于存储可重复利用的对象。

第②步操作就是取出对象。

第③步操作是使用取出的对象来完成一些任务。这时候并不需要对象池再做什么,但是这也意味着该对象将被租借一段时间并且不能再被其他组件借出。

第④步操作就是归还,组件归还借出的对象,这样可以继续满足其他的租借请求。

在一个多线程应用中,②、③、④操作都有可能发生并发操作。多线程的组件中分享对象导致了潜在的并发问题。也存在一种情况就是当所有对象都被借出时不能满足接下来的请求,对象池必须应对这些请求,可以是告诉组件已经没有对象可借,或者是允许组件等待直到有归还的对象。

注意:

①使用的时候,申请 (obtain) 和释放 (recycle) 成对出现,使用一个对象后一定要释放还给池子。

②池子的大小要根据实际情况合理指定。池子太大会使对象不释放时占用的内存很大;池子太小对象过多而且因为操作耗时而不能立即释放还给池子时候,池子满了,后续对象还是不能复用。所以,根据项目实际场景制定合理的大小是很必要的。

 

3.对象池的实现

(1)通过数组实现--Pools

在Android的v4包中有个类Pools,其中定义了一个对象池接口Pool,具体的实现有两个SimplePool和SynchronizedPool,一个是非线程安全的,另一个是线程安全的。

①Pool接口类

public interface Pool<T> {

    T acquire();

    boolean release(T instance); 

}

acquire(): 从对象池请求对象。注意:有可能会返回null。

release(): 释放对象并存入对象池。return true表示释放的对象成功放入对象池;return false表示对象没有成功地放到对象池中。如果参数对象 instance已经在对象池中,则会抛出IllegalStateException异常。

注意:如果只是一味地从对象池中获取对象(调用acquire()),而不向对象池中放入对象(调用release(T instance)),那么通过acquire()得到的对象有可能为null,也有可能是new出来的,并没有达到缓存对象的目的,所以在使用完对象以后,要调用release(T instance)及时地将对象释放到对象池。

②SimplePool

这是Android官方对象池的简单实现,也是用得最多的实现。这是对象池的非同步实现。

原理:使用了 “懒加载” 的思想。当SimplePool初始化时,不会生成N个T类型的对象存放在对象池中。而是当每次外部调用release()时,才把释放的T类型对象存放在对象池中。要先放入,才能取出来。

public static class SimplePool<T> implements Pool<T> {

    private final Object[] mPool; //对象池中真正用于存储对象的数组(使用数组实现对象池)

    private int mPoolSize; //对象池内的对象个数

    public SimplePool(int maxPoolSize) {

        if (maxPoolSize <= 0) {

            throw new IllegalArgumentException( "The max pool size must be > 0");

        }

        mPool = new Object[maxPoolSize]; //初始化对象数组

    }

     //从mPool数组中取出mPoolSize - 1位置上的对象

    @Override

    public T acquire() {

        if (mPoolSize > 0) {

            final int lastPooledIndex = mPoolSize - 1;

            T instance = (T) mPool[lastPooledIndex];

            mPool[lastPooledIndex] = null;

            mPoolSize--;

            return instance;

        }

        return null;

    }

    //回收的对象放入mPool数组的mPoolSize位置

    @Override

    public boolean release(T instance) {

        if (isInPool(instance)) {

            throw new IllegalStateException( "Already in the pool!");

        }

        if (mPoolSize < mPool.length) {

            mPool[mPoolSize] = instance;

            mPoolSize++;

            return true;

        }

        return false;

    }

    //判断对象是否已存在于对象池中

    private boolean isInPool(T instance) {

        for (int i = 0; i < mPoolSize; i++) {

            if (mPool[i] == instance) {

                return true;

            }

        }

        return false;

    }

}

acquire()方法,首先判断此时对象池中有没有缓存的对象。如果有&

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值