Glide手写实现之复用池

系列文章

Glide手写实现之资源封装
Glide手写实现之活动缓存
Glide手写实现之内存缓存
Glide手写实现之磁盘缓存
Glide手写实现之生命周期关联
Glide手写实现之网络图片加载实现
Glide手写实现之复用池

为什么要用复用池

Bitmap属于大对象,在频繁创建对象和回收对象时,会有内存抖动问题。为了解决这个问题,Glide中使用了复用池。在开辟新的Bitmap对象空间之前,从复用池中找寻是否有合适的内存空间可以直接使用。复用池仍然使用了LRU算法。

BitmapPool

复用池的标准接口

// 复用池 标准
public interface BitmapPool {

    /**
     * 存入到复用池
     * @param bitmap
     */
    void put(Bitmap bitmap);


    /**
     * 获取匹配可用复用的Bitmap
     * @param width
     * @param height
     * @param config
     * @return
     */
    Bitmap get(int width, int height, Bitmap.Config config);

}

BitmapPoolImpl

复用池的实现

public class BitmapPoolImpl extends LruCache<Integer, Bitmap> implements BitmapPool {

    private final String TAG = BitmapPoolImpl.class.getSimpleName();

    // 为了筛选出 合适的 Bitmap 容器
    private TreeMap<Integer, String> treeMap = new TreeMap<>();

    public BitmapPoolImpl(int maxSize) {
        super(maxSize);
    }

    // 存入复用池
    @Override
    public void put(Bitmap bitmap) {

        // todo 条件一 bitmap.isMutable() == true;
        if (!bitmap.isMutable()) {
            Log.d(TAG, "put: 条件一 bitmap.isMutable() == true 不满足,不能存入复用池..");
            return;
        }

        // todo 条件二
        // 计算Bitmap的大小
        int bitmapSize = getBitmapSize(bitmap);
        if (bitmapSize >= maxSize()) {
            Log.d(TAG, "put: 条件二 大于了maxSize 不满足,不能存入复用池..");
            return;
        }

        // todo bitmap 存入 LruCache
        put(bitmapSize, bitmap);

        // 存入 筛选 容器
        treeMap.put(bitmapSize, null); // 10000

        Log.d(TAG, "put: 添加到复用池...");
    }

    // 获取可用复用的Bitmap
    @Override
    public Bitmap get(int width, int height, Bitmap.Config config) {

        /**
         * ALPHA_8  理论上 实际上Android自动做处理的 只有透明度 8位  1个字节
         * w*h*1
         *
         * RGB_565  理论上 实际上Android自动做处理的  R red红色 5, G绿色 6, B蓝色 5   16位 2个字节 没有透明度
         * w*h*2
         *
         * ARGB_4444 理论上 实际上Android自动做处理 A透明度 4位  R red红色4位   16位 2个字节
         *
         * 质量最高的:
         * ARGB_8888 理论上 实际上Android自动做处理  A 8位 1个字节  ,R 8位 1个字节, G 8位 1个字节, B 8位 1个字节
         *
         * 常用的 ARGB_8888  RGB_565
         */
        // 常用的 4==ARGB_8888  2==RGB_565
        int getSize = width * height * (config == Bitmap.Config.ARGB_8888 ? 4 : 2);

        Integer key = treeMap.ceilingKey(getSize); // 可以查找到容器里面 和getSize一样大的,也可以 比getSize还要大的
        // 如果treeMap 还没有put,那么一定是 null
        if (key == null) {
            return null; // 没有找到合适的 可以复用的 key
        }

        // key == 10000     getSize==12000

        // 查找容器取出来的key,必须大于getSize 并且 小于 计算出来的 (getSize * 2 : )
        // if (key>=getSize && key <= (getSize * 2)) {
            Bitmap remove = remove(key);// 复用池 如果要取出来,肯定要移除,不想给其他地方用了
            Log.d(TAG, "get: 从复用池 里面获取了Bitmap...");
            return remove;
        // }
        // return null;
    }

    /**
     * 计算Bitmap的大小
     * @param bitmap
     * @return
     */
    private int getBitmapSize(Bitmap bitmap) {
        // 最早期的时候 getRowBytes() * getHeight();

        // Android 3.0 12 API  bitmap.getByteCount()
        // bitmap.getByteCount()

        // Android 4.4 19 API 以后的版本
        // bitmap.getAllocationByteCount();

        int sdkInt = Build.VERSION.SDK_INT;
        if (sdkInt >= Build.VERSION_CODES.KITKAT) {
            return bitmap.getAllocationByteCount();
        }
        return bitmap.getByteCount();
    }

    // 元素大小
    @Override
    protected int sizeOf(Integer key, Bitmap value) {
        // return super.sizeOf(key, value);
        return getBitmapSize(value);
    }

    // 元素被移除
    @Override
    protected void entryRemoved(boolean evicted, Integer key, Bitmap oldValue, Bitmap newValue) {
        super.entryRemoved(evicted, key, oldValue, newValue);
    }
}

MainActivity

测试代码:

public class MainActivity extends AppCompatActivity implements Runnable {

    private ImageView imageView;
    private BitmapPool bitmapPool = new BitmapPoolImpl(1024 * 1024 * 6);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        imageView = findViewById(R.id.image);
    }

    private final String PATH = "https://cn.bing.com/sa/simg/hpb/LaDigue_EN-CA1115245085_1920x1080.jpg";

    public void testAction(View view) {
        new Thread(this).start();
    }

    // 应用层 Http Https  ---> HttpURLConnection
    // 应用层 OkHttp 高效
    @Override
    public void run() {
        try {
            URL url = new URL(PATH);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setConnectTimeout(5000);
            int responseCode = connection.getResponseCode();
            if (HttpURLConnection.HTTP_OK == responseCode) {
                InputStream inputStream = connection.getInputStream();

                /*BitmapFactory.Options options = new BitmapFactory.Options(); // 拿到图片宽和高
                options.inJustDecodeBounds = true; // 只拿到周围信息,outXXX, outW,outH
                // options = null; 执行下面代码
                BitmapFactory.decodeStream(inputStream, null, options);
                int w = options.outWidth;
                int h = options.outHeight;*/

                int w = 1920;
                int h = 1080;

                BitmapFactory.Options options = new BitmapFactory.Options();

                // 拿到复用池  条件: bitmap.isMutable() == true;
                Bitmap bitmapPoolResult = bitmapPool.get(w, h, Bitmap.Config.RGB_565);

                // 如果设置为null,内部就不会去申请新的内存空间,无非复用,依然会照成:内存抖动,内存碎片
                options.inBitmap = bitmapPoolResult; // 把复用池的Bitmap 给 inBitmap
                options.inPreferredConfig = Bitmap.Config.RGB_565; // 2个字节
                options.inJustDecodeBounds = false;
                options.inMutable = true; // 符合 复用机制
                final Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, options); // 复用内存

                // 添加到复用池
                bitmapPool.put(bitmap);

                new Handler(Looper.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {
                        imageView.setImageBitmap(bitmap);
                    }
                });
            }

        } catch (Exception e) {
        }
    }
}

复用池的关键就是在使用的时候,
1.options.inMutable = true
2.options.inBitmap = bitmapPoolResult; 等于null,代表不复用

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值