美文网首页
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

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