美文网首页
glide-手写glide

glide-手写glide

作者: cain07 | 来源:发表于2021-10-18 11:13 被阅读0次

1.BitmapFactory

一.Glide手写实现之资源封装

资源封装
Key -- 对Value的唯一性进行描述
Value -- Bitmap的封装(+1, -1, 释放)

1.1代码

/**
 * 唯一的描述
 */
public class Key {

    private String key; // 例如:ac037ea49e34257dc5577d1796bb137dbaddc0e42a9dff051beee8ea457a4668

    /**
     * sha256(https://cn.bing.com/sa/simg/hpb/LaDigue_EN-CA1115245085_1920x1080.jpg)之前
     * ac037ea49e34257dc5577d1796bb137dbaddc0e42a9dff051beee8ea457a4668 处理后
     * @param key
     */
    public Key(String key) {
        //this.key = key;
        this.key = Tool.getSHA256StrJava(key);
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }
}
/**
 * 对Bitmap的封装
 */
public class Value {

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

    private Value() {}

    // 单利模式
    private static Value value;

    public static Value getInstance() {
        if (null == value) {
            synchronized (Value.class) {
                if (null == value) {
                    value = new Value();
                }
            }
        }
        return value;
    }

    private Bitmap mBitmap;

    // 使用计数
    private int count;

    // 监听
    private ValueCallback callback;

    // 定义key
    private String key;

    public Bitmap getmBitmap() {
        return mBitmap;
    }

    public void setmBitmap(Bitmap mBitmap) {
        this.mBitmap = mBitmap;
        Log.d(TAG, "setmBitmap: VVVVVVVV setBitmap:" + mBitmap.isMutable());
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public ValueCallback getCallback() {
        return callback;
    }

    public void setCallback(ValueCallback callback) {
        this.callback = callback;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }


    /**
     * TODO 使用一次 就 加一
     */
    public void useAction() {
        Tool.checkNotEmpty(mBitmap);

        if (mBitmap.isRecycled()) { // 已经被回收了
            Log.d(TAG, "useAction: 已经被回收了");
            return;
        }
        Log.d(TAG, "useAction: 加一 count:" + count);

        count ++;
    }

    /**
     * TODO 使用完成(不使用) 就 减一
     * count -- <= 0  不再使用了
     */
    public void nonUseAction() {
        count--;
        if (count <= 0 && callback != null) {
            // 回调告诉外界,不再使用了
            Log.d(TAG, "nonUseAction: VVVVVV mBitmap.isMutable():" + mBitmap.isMutable());
            callback.valueNonUseListener(key, value);
        }
        Log.d(TAG, "useAction: 减一 count:" + count);
    }

    /**
     * TODO 释放
     */
    public void recycleBitmap() {
        if (count > 0) {
            Log.d(TAG, "recycleBitmap: 引用计数大于0,证明还在使用中,不能去释放...");
            return;
        }

        if (mBitmap.isRecycled()) { // 被回收了
            Log.d(TAG, "recycleBitmap: mBitmap.isRecycled() 已经被释放了...");
            return;
        }

        mBitmap.recycle();

        value = null;

        System.gc();
    }
}
/**
 * 专门给Value,不再使用了,的回调接口
 */
public interface ValueCallback {

    /**
     * 监听的方法(Value不再使用了)
     * @param key
     * @param value
     */
    public void valueNonUseListener(String key, Value value);

}

二.Glide手写实现之活动缓存

2.1回收机制:GC扫描的时候回收,移除容器(GC被动移除)(弱引用)
2.2容器管理方式:资源的封装 Key ----- (弱引用<Value>)
2.3手动移除的区分
2.4关闭线程
2.5Value监听加入

2.1 代码

/**
 * 活动缓存 -- 真正被使用的资源
 */
public class ActiveCache {

    // 容器
    private Map<String, CustomoWeakReference> mapList = new HashMap<>();
    private Map<String, Bitmap> mapValueList = new HashMap<>();

    private ReferenceQueue<Value> queue; // 目的:为了监听这个弱引用 是否被回收了
    private boolean isCloseThread;
    private Thread thread;
    private boolean isShoudonRemove;
    private ValueCallback valueCallback;

    public ActiveCache(ValueCallback valueCallback) {
        this.valueCallback = valueCallback;
    }

    /**
     * TODO 添加 活动缓存
     * @param key
     * @param value
     */
    public void put(String key, Value value) {
        Tool.checkNotEmpty(key);

        // 没有问题
        // Log.d("activeCache", "put: Inputkey:" + key + " --- value:" + value.getmBitmap() + "对应 key:" + value.getKey());

        // 绑定Value的监听 --> Value发起来的(Value没有被使用了,就会发起这个监听,给外界业务需要来使用)
        value.setCallback(valueCallback);

        // 存储 --》 容器
        mapList.put(key, new CustomoWeakReference(value, getQueue(), key));
        mapValueList.put(key, value.getmBitmap());
    }

    /**
     * TODO 给外界获取Value
     * @param key
     * @return
     */
    public Value get(String key) {
        CustomoWeakReference valueWeakReference =  mapList.get(key);
        // Log.d("activeCache", "get: 自定义的弱引用" +  valueWeakReference); // 不一样的
        if (null != valueWeakReference) {
            Value value = valueWeakReference.getValue(); // 返回Value
            value.setmBitmap(mapValueList.get(key));
            value.setKey(key);
            // bug每次 value信息一致
            Log.d("activeCache", "get: Inputkey:" + key + " --- value:" + value.getmBitmap() + "对应 key:" + value.getKey());
            return value;
        }
        return null;
    }

    /**
     * TODO 手动移除
     * @param key
     * @return
     */
    public Value remove(String key) {
        isShoudonRemove = true;
        WeakReference<Value> valueWeakReference = mapList.remove(key);
        isShoudonRemove = false; // 还原 目的是为了 让 GC自动移除 继续工作
        if (null != valueWeakReference) {
            return valueWeakReference.get();
        }
        return null;
    }

    /**
     * TODO 释放 关闭线程
     */
    public void closeThread() {
        isCloseThread = true;
        /*if (null != thread) {
            thread.interrupt(); // 中断线程
            try {
                thread.join(TimeUnit.SECONDS.toMillis(5)); // 线程稳定安全 停止下来
                if (thread.isAlive()) { // 证明线程还是没有结束
                    throw new IllegalStateException("活动缓存中 关闭线程 线程没有停止下来...");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }*/

        mapList.clear();

        System.gc();
    }

    /**
     * 监听弱引用 成为弱引用的子类  为什么要成为弱引用的子类(目的:为了监听这个弱引用 是否被回收了)
     */
    public static final class CustomoWeakReference extends WeakReference<Value> {

        private String key;
        private Value value;

        public CustomoWeakReference(Value value, ReferenceQueue<? super Value> queue, String key) {
            super(value, queue);
            this.key = key;
            this.value = value;

            // Log.d("activeCache", "构造 put: Inputkey:" + key + " --- value:" + this.value.getmBitmap() + "对应 key:" + this.value.getKey());
        }

        public Value getValue() {
            return this.value;
        }
    }

    /**
     * 为了监听 弱引用被回收,被动移除的
     * @return
     */
    private ReferenceQueue<Value> getQueue() {
        if (queue == null) {
            queue = new ReferenceQueue<>();

            // 监听这个弱引用 是否被回收了
            thread =  new Thread(){
                @Override
                public void run() {
                    super.run();

                    while (!isCloseThread) {

                        try {
                            if (!isShoudonRemove) {
                                // queue.remove(); 阻塞式的方法

                                Reference<? extends Value> remove = queue.remove(); // 如果已经被回收了,就会执行到这个方法
                                CustomoWeakReference weakReference = (CustomoWeakReference) remove;
                                // 移除容器     !isShoudonRemove:为了区分手动移除 和 被动移除
                                if (mapList != null && !mapList.isEmpty() && !mapValueList.isEmpty()) {
                                    mapList.remove(weakReference.key);
                                    mapValueList.remove(weakReference.key);
                                }
                            }
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                    }

                }
            };
            thread.start();
        }
        return queue;
    }
}

三.Glide手写实现之内存缓存(LRU算法)

LRU算法:最近没有使用的元素,会自动被移除掉
职责:
活动缓存:给正在使用的资源存储的,弱引用
内存缓存:为第二次缓存服务,LRU算法
LRU算法: ---> 最近没有使用的元素,会自动被移除掉
把最近没有使用到的元素给移除
LruCache v4:
利用LinkedHashMap<K, V>,
LinkedHashMap: true==拥有访问排序的功能 (最少使用元素算法-LRU算法)

3.1.lru算法 图示

image.png

3.2内存缓存 代码

public class MemoryCache extends LruCache<String, Value> {

    MemoryCacheCallBack memoryCacheCallBack;

    private boolean shoudongRemove;

    public MemoryCacheCallBack getMemoryCacheCallBack() {
        return memoryCacheCallBack;
    }

    public void setMemoryCacheCallBack(MemoryCacheCallBack memoryCacheCallBack) {
        this.memoryCacheCallBack = memoryCacheCallBack;
    }

    public void shoudongRemove(String key) {
        shoudongRemove = true;
        remove(key);
        shoudongRemove = false;
    }

    /**
     * 传入最大值
     */
    public MemoryCache(int maxSize) {
        super(maxSize);
    }


    /**
     * 存储的 键值对 key
     * @param key
     * @param value
     * @return
     */
    @Override
    protected int sizeOf(String key, Value value) {
        Bitmap bitmap = value.getBitmap();
        int sdkInt = Build.VERSION.SDK_INT;
        if (sdkInt >= Build.VERSION_CODES.KITKAT) {
            return bitmap.getAllocationByteCount();
        }
        return bitmap.getByteCount();
    }

    /**
     * lru 删除的回调
     * @param evicted
     * @param key
     * @param oldValue
     * @param newValue
     */
    @Override
    protected void entryRemoved(boolean evicted, String key, Value oldValue, Value newValue) {
        super.entryRemoved(evicted, key, oldValue, newValue);
        if (memoryCacheCallBack != null && !shoudongRemove) {
            memoryCacheCallBack.entryRemoveMemoryCache(key,oldValue);
        }
    }
}

四 Glide手写实现之磁盘缓存

保存时长比较长:保存在本地磁盘 文件的形式存储 (不再是保存在运行内存中,而是磁盘中)
LRU算法: ---> 最近没有使用的元素,会自动被移除掉
DiskLruCache --- Android中没有提供了 --> DiskLruCache
DiskLruCache:回收方式:LRU算法, 访问排序 面向磁盘文件保存

4.1 图示

image.png

4.2 磁盘存储代码 主要用到了第三方的 DiskLruCache 类 写到 put 和 get 方法

/**
 * 磁盘 存储
 */
public class DiskLruCacheImpl {

    private final String Tag = this.getClass().getSimpleName();

    private DiskLruCache diskLruCache;

    private final String File_Url = Environment.getExternalStorageState() + File.separator + "cain_disk_cache";

    private final int appVersion = 1;

    private final int valueCount = 1;
    private final long maxSize = 1024 * 1024 * 10;


    public DiskLruCacheImpl() {
        try {
            File  file = new File(File_Url);
            diskLruCache = DiskLruCache.open(file,appVersion,valueCount,maxSize);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 存入数据 写入 磁盘中
     * @param key
     * @param value
     */
    public void put(String key, Value value) {
        OutputStream outputStream = null;
        DiskLruCache.Editor edit = null;
        try {
            edit = diskLruCache.edit(key);
            outputStream = edit.newOutputStream(0);

            Bitmap bitmap = value.getBitmap();
            bitmap.compress(Bitmap.CompressFormat.PNG,100,outputStream);

            outputStream.flush();

        } catch (IOException e) {
            e.printStackTrace();
            try {
                edit.abort();
            } catch (IOException ex) {
                ex.printStackTrace();
                Log.e(Tag,"put:edit.abort() e:" + e.getMessage());
            }
        }finally {

            try {
                edit.commit();

            } catch (IOException e) {
                e.printStackTrace();
                Log.e(Tag,"put:edit.commit() e:" + e.getMessage());
            }

            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
                Log.e(Tag,"put:outputStream.close() e:" + e.getMessage());
            }
        }
    }

    /**
     * 取数据 从磁盘中取数据
     * @param key
     * @return
     */
    public Value get(String key) {
        Tool.checkNotEmpty(key);

        InputStream inputStream = null;
        try {
            Value value = Value.getInstance();
            DiskLruCache.Snapshot snapshot = diskLruCache.get(key);
            inputStream = snapshot.getInputStream(0);
            Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
            value.setBitmap(bitmap);
            value.setKey(key);

            return value;

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (null != inputStream) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    Log.e(Tag,"get:inputStream.close() e:" + e.getMessage());
                }
            }
        }
        return null;
    }
}

五 Glide手写实现之生命周期

组拼接之前的所有内容 --> Glide
生命周期的管理:Application不能去管理,FragmentActivity可以去管理,Activity也可以去管理
管理的方式:在Activity组件上 附件Fragment,通过Fragment监听组件的生命周期

Activity ---> app Fragment
AppCompatActivity --> V4包 Fragment
为什么发送一次Handler?
我们的Android基于Handler消息的,LAUNCH_ACTIVITY,为了让我们的fragment,不要再排队中,为了下次可以取出来
移除Handler

5.1 主要代码

public class RequestManager {

    private final String Fragment_Activity_NAME = "Fragment_Activity_NAME";
    private final String Activity_NAME = "Activity_NAME";
    private final int NEXT_HANDLE_MSG = 99545;

    private Context requestManagerContext;

    private RequestTargetEngine requestTargetEngine;

    {
        if (requestTargetEngine == null) {
            requestTargetEngine = new RequestTargetEngine();
        }
    }


    public RequestManager(Activity mainActivity) {
        this.requestManagerContext = mainActivity;

        android.app.FragmentManager fragmentManager = mainActivity.getFragmentManager();

        android.app.Fragment fragmentByTag = fragmentManager.findFragmentByTag(Activity_NAME);

        if (fragmentByTag == null ) {
            fragmentByTag = new ActivityFragmentManager(requestTargetEngine);

            fragmentManager.beginTransaction().add(fragmentByTag,Activity_NAME).commitAllowingStateLoss();
        }

        myHandler.sendEmptyMessage(NEXT_HANDLE_MSG);
    }

    public RequestManager(FragmentActivity mainActivity) {
        this.requestManagerContext = mainActivity;

        FragmentManager supportFragmentManager = mainActivity.getSupportFragmentManager();

        Fragment fragmentByTag = supportFragmentManager.findFragmentByTag(Fragment_Activity_NAME);

        if (fragmentByTag == null) {
            fragmentByTag = new FragmentActivityFragmentManager(requestTargetEngine);
            supportFragmentManager.beginTransaction().add(fragmentByTag,Fragment_Activity_NAME).commitAllowingStateLoss();
        }

        myHandler.sendEmptyMessage(NEXT_HANDLE_MSG);

    }

    private Handler myHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message message) {
            return false;
        }
    });

    public RequestManager(Context mainActivity) {

        this.requestManagerContext = mainActivity;

    }

    public RequestTargetEngine load(String path) {
        requestTargetEngine.loadValueInitAction(path,requestManagerContext);
        return requestTargetEngine;
    }
}

5.2

public class Glide {
    private RequestManagerRetriever requestManagerRetriever;

    public Glide(RequestManagerRetriever requestManagerRetriever) {
        this.requestManagerRetriever = requestManagerRetriever;
    }

    public static RequestManager with(Activity mainActivity) {
        return getRetriever(mainActivity).get(mainActivity);
    }

    public static RequestManager with(FragmentActivity mainActivity) {
        return getRetriever(mainActivity).get(mainActivity);
    }

    public static RequestManager with(Context mainActivity) {
        return getRetriever(mainActivity).get(mainActivity);
    }

    public static RequestManagerRetriever getRetriever(Context context) {
        return Glide.get(context).getRequestManagerRetriever();
    }

    /**
     * glide  是 new 出来的转变
     * @param context
     * @return
     */
    public static Glide get(Context context) {
        return new GlideBuild().build();
    }


    public RequestManagerRetriever getRequestManagerRetriever() {
        return requestManagerRetriever;
    }

    public void setRequestManagerRetriever(RequestManagerRetriever requestManagerRetriever) {
        this.requestManagerRetriever = requestManagerRetriever;
    }
}

六 Glide手写实现之加载图片

组拼接之前的所有内容(缓存) --> Glide
加载资源 --》 缓存 ---》网络/SD/ 加载资源 成功后 --》资源 保存到缓存中

总结:
第一次的时候,去网络下载图片,保存到磁盘缓存中(/sd/disk_lru_cache_dir/key)
第二次的时候,直接再活动缓存中,找到了资源
第三次的时候,直接再活动缓存中,找到了资源
第N次的时候,直接再活动缓存中,找到了资源

把Activity给返回回去的时候,进行释放,活动缓存的释放
又一次加载的时候,从内存缓存中获取了
下一次加载的时候,就是从活动缓存获取了

把App给杀掉
整个活动缓存,整个内存缓存,都没有了
所以从磁盘缓存中获取

6.1 图示

image.png

完整代码

https://github.com/cain07/glidecain

相关文章

  • glide-手写glide

    1.BitmapFactory 一.Glide手写实现之资源封装 资源封装Key -- 对Value的唯一性进...

  • Glide(图片加载)

    Glide Glide使用文档 Glide 系列教程 玩转Glide的Target对象 Glide-内存缓存与磁盘...

  • glide源码解析

    title: glide-源码解析-图床版date: 2020-03-17 20:41:59tags: [andr...

  • Glide-教程

    https://blog.csdn.net/yulyu/article/details/55053099

  • Glide框架手写实现(四)磁盘缓存

    Glide框架手写实现(一)资源封装Glide框架手写实现(二)活动缓存Glide框架手写实现(三)内存缓存Gli...

  • Glide-源码详解

    前言: 之前的文章中,笔者介绍了很多Glide的使用方法,但是由于Glide框架封装得太好了,很多人在使用的时候,...

  • Glide-源码分析(一)

    前言 前面几片文章主要介绍了下Picasso,相对来说Picasso源码看起来会比较轻松,所以如果想研究图片框架的...

  • Glide-源码分析(二)

    前言 前面一篇文章对Glide第一次加载网络图片的流程通过源码的方式一步步的带大家看了下,至少对Glide的整个框...

  • Glide-源码分析(三)

    正文 第一篇文章介绍了下第一次加载网络图片的流程。第二篇文章介绍了下加载过图片,然后重启app,从磁盘加载图片的流...

  • Glide->01Glide.into关于磁盘缓存

    Glide基本用法 关于磁盘缓存涉及到的类: DiskCacheStrategy DiskCacheStrateg...

网友评论

      本文标题:glide-手写glide

      本文链接:https://www.haomeiwen.com/subject/iyefnltx.html