存储框架封装:LruCacheUtils+DiskLruCacheUtils+责任链设计模式+DeepLink

一.存储框架实现思路?

思路来源与图片的三级缓存,即:内存->磁盘->网络
对于部分数据(如:静态数据或者配置信息)我们可能同样需要进行缓存来提升效率,
具体实现思路如下:
1、使用多级缓存完成资源的复用 如:内存 -> 磁盘 ->…
2、使用责任链设计模式,可以通过自定义添加链节点完成多级缓存
3、使用LruCache完成内存部分存储
4、使用DiskLruCache完成磁盘部分存储

1.缓存策略

缓存策略主要包含缓存的添加、获取和删除三类操作。删除缓存是因为不管是内存缓存还是硬盘缓存,它们的缓存大小都是有限的。当缓存满了之后,再想添加缓存就需要删除一些旧的缓存。

2.LRU算法

Android的三级缓存主要的就是内存缓存和硬盘缓存。这两种缓存机制的实现都应用到了LRU算法。
LRU(Least Recently Used)缓存算法是近期最少使用算法,它的核心思想是当缓存满时,会优先淘汰那些近期最少使用的缓存对象。

3.LruCache内存缓存原理

LruCache是Android 3.1提供的一个缓存类,在Android中可以直接使用LruCache实现内存缓存。而硬盘缓存DisLruCache目前还不是Android SDK的一部分,但Android官方文档推荐使用该算法来实现硬盘缓存。

LruCache的核心思想就是维护一个缓存对象列表,这个队列就是由LinkedHashMap维护的。列表的排列方式按照访问顺序实现,即一直没访问的对象,将放在队尾,即将被淘汰,而最近访问的对象将放在队头,最后被淘汰。
在这里插入图片描述
LinkedHashMap由数组+双向链表实现。其中双向链表的结构可以实现访问顺序和插入顺序,使得LinkedHashMap中的<key,value>对按照一定顺序排列起来。

LinkedHashMap通过构造函数来指定双向链表的结构是访问顺序还是插入顺序。

public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) {
    super(initialCapacity, loadFactor);
    this.accessOrder = accessOrder;
}

accessOrder为true是访问顺序,为false是插入顺序。

比如,当设置accessOrder为true时:

public static final void main(String[] args) {
    LinkedHashMap<Integer, Integer> map = new LinkedHashMap<>(0, 0.75f, true);
    map.put(0, 0);
    map.put(1, 1);
    map.put(2, 2);
    map.put(3, 3);
    map.put(4, 4);
    map.put(5, 5);
    map.put(6, 6);
    map.get(1);
    map.get(2);
    for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
        System.out.println(entry.getKey() + ":" + entry.getValue());

    }
}

输出结果:

0:0

3:3

4:4

5:5

6:6

1:1

2:2

即最近访问的最后输出,这正好满足LRU缓存算法的思想。LruCache的实现就是利用了LinkedHashMap的这种数据结构

4.DiskLruCache磁盘缓存原理

磁盘读写也是用的LRU算法。但是这个和内存的LRU算法有一点小区别。为什么呢?因为内存缓存是我们运行的时候,程序加载内存里面的资源,可以直接通过一个LinkedHashMap去实现。但是磁盘不同,我总不可能吧所有磁盘的资源读出来然后加载在内存里面吧,这样的话,肯定会引发oom了。那么Glide是怎么做磁盘的LRU的呢?

Glide 是使用一个日志清单文件来保存这种顺序,DiskLruCache 在 APP 第一次安装时会在缓存文件夹下创建一个 journal 日志文件来记录图片的添加、删除、读取等等操作,后面每次打开 APP 都会读取这个文件,把其中记录下来的缓存文件名读取到 LinkedHashMap 中,后面每次对图片的操作不仅是操作这个 LinkedHashMap 还要记录在 journal 文件中. journal 文件内容如下图:

data/data/应用包名/cache/。。。。。
在这里插入图片描述
日志文件:
在这里插入图片描述

5.使用单例模式实现LRUCacheUtils

**********kotlin单例模式(掌握)

java版本

public class LRUCacheUtils<V> {
    int maxSize= (int) (Runtime.getRuntime().totalMemory()/8);
    private LRUCacheUtils(){
        lruCache=new LruCache<String, V>(maxSize);
    }
    private static volatile LRUCacheUtils instance=null;
    public static LRUCacheUtils getInstance(){
        if (null==instance){
            synchronized (LRUCacheUtils.class){
                if (null==instance){
                    instance=new LRUCacheUtils();
                }
            }
        }

        return instance;
    }

    LruCache<String,V> lruCache=null;

    /**
     * 按Key存储值
     * @param key
     * @param value
     */
    public void putValue(String key,V value){
        lruCache.put(key,value);
    }

    /**
     * 按Key获取值
     * @param key
     * @return
     */
    public V getValue(String key){
        return lruCache.get(key);
    }

    /**
     * 按Key删除指定值
     * @param key
     */
    public void removeValue(String key){
        lruCache.remove(key);
    }

    /**
     * 清空
     */
    public void clear(){
        lruCache.evictAll();
    }
}

kotlin版本

/**
 * @Author : yaotianxue
 * @Time : On 2023/5/23 07:59
 * @Description : LRUCacheUtils
 */
class LRUCacheUtils<V> {
    var maxSize = (Runtime.getRuntime().totalMemory() / 8).toInt()//内存的1/8
    var lruCache:LruCache<String,V> = LruCache<String,V>(maxSize)//lruCache
    //双重锁单例模式
    companion object{
        val instance by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
            LRUCacheUtils<Any>()
        }
    }
    /**
     * 按Key存储值
     */
    fun putValue(key:String,value:V){
        lruCache.put(key,value)
    }
    /**
     * 按Key获取值
     */
    fun getValue(key:String): V? {
        return lruCache.get(key)
    }
    /**
     * 按key删除
     */
    fun removeValue(key:String){
        lruCache.remove(key)
    }
    /**
     * 清空
     */
    fun clear(){
        lruCache.evictAll()
    }
}

5.使用单例模式实现DiskLRUCacheUtils

java版本

public final class DiskLRUCacheUtils<V> {
    private DiskLruCache diskLruCache;
    private static DiskLRUCacheUtils instance=new DiskLRUCacheUtils();
    /**
     * 容量上限200M
     */
    private static final int MAX_SIZE=200*1024*1024;
    private DiskLRUCacheUtils(){
        /**
         * 如下 初始化DiskLruCache
         */
        String diskCachePath = Environment.getExternalStorageDirectory().getAbsolutePath()+ File.separator+"bawei6diskcache";
        File file=new File(diskCachePath);
        if (!file.exists()){
            file.mkdirs();
        }
        try {
            diskLruCache = DiskLruCache.open(file, 1, 1, MAX_SIZE);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static DiskLRUCacheUtils getInstance(){
        return instance;
    }

    public void putValue(String key, V data) {

        String mKey= MD5.encrypt(key);
        OutputStream outputStream = null;
        DiskLruCache.Editor edit=null;
        try {
            edit = diskLruCache.edit(mKey);
            if (edit!=null){

                //对象转byte数组
                byte[] bytes= ObjUtils.obj2ByteArray(data);


                outputStream = edit.newOutputStream(0);
                outputStream.write(bytes);
                edit.commit();
                diskLruCache.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
            if (edit!=null){
                try {
                    edit.abort();
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }

        }finally {
            if (outputStream!=null){
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public V getValue(String key) {
        InputStream is=null;
        try {
            List<Byte> data = new ArrayList<>();
            String mKey = MD5.encrypt(key);
            DiskLruCache.Snapshot snapShot = diskLruCache.get(mKey);
            if (snapShot != null) {
                is = snapShot.getInputStream(0);
                byte[] bytes = new byte[2048];
                int len;
                while ((len = is.read(bytes)) != -1) {
                    for (int i = 0; i < len; i++) {
                        data.add(bytes[i]);
                    }
                }
                bytes = new byte[data.size()];
                for (int i = 0; i < bytes.length; i++) {
                    bytes[i] = data.get(i);
                }
                return ObjUtils.byteArray2Object(bytes);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (is!=null){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    public void removeValue(String key) {
        String mKey=MD5.encrypt(key);
        try {
            diskLruCache.remove(mKey);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void clear() {
        try {
            diskLruCache.delete();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

kotlin版本

/**
 * @Author : yaotianxue
 * @Time : On 2023/5/23 08:09
 * @Description : DiskLRUCacheUtils
 */
class DiskLRUCacheUtils<V> {
    //磁盘缓存路径
    private var diskCachePath:String = Environment.getExternalStorageDirectory().absolutePath + File.separator +"2010baweidiskcache"
    //磁盘缓存对象
    var  diskCache: DiskLruCache
    //初始化
    init {
        var file = File(diskCachePath)
        if(!file.exists()){//文件夹不存在
            file.mkdirs()//创建文件夹
        }
        diskCache = DiskLruCache.open(file,1,1, MAX_SIZE.toLong())
    }
    companion object{
        const val MAX_SIZE = 200*1024*1024
        val instance by lazy(LazyThreadSafetyMode.SYNCHRONIZED){
            DiskLRUCacheUtils<Any>()
        }
    }

    /**
     * 按Key存储值
     */
    fun putValue(key:String,data:V){
        var mKey = MD5.encrypt(key)//使用工具类将keyMD5加密
        var editor = diskCache.edit(mKey)
        editor.let {
            var arrays = ObjUtils.obj2ByteArray(data)//对象转数组
            var outputStream = editor.newOutputStream(0)
            outputStream.write(arrays)
            editor.commit()
            diskCache.flush()

            outputStream.close()
        }
    }

    /**
     * 按Key获取值
     */
    fun getValue(key:String): V? {
        var mKey = MD5.encrypt(key)//使用工具类将keyMD5加密
        var snapshot =  diskCache.get(mKey)//根据键获得snapshot对象
        var inputStream = snapshot.getInputStream(0)//获得输入流
        var bytes = ByteArray(2048)
        var outputStream = ByteArrayOutputStream()//字节数组输出流
        var len = 0
        //inputStream读取数据到outputStream
        while ((inputStream.read(bytes).also { len = it })!=-1){
            outputStream.write(bytes)
        }
        return ObjUtils.byteArray2Object(outputStream.toByteArray())
    }
    /**
     * 按key删除
     */
    fun removeValue(key:String){
        var mKey = MD5.encrypt(key)//使用工具类将keyMD5加密
        diskCache.remove(mKey)
    }

    /**
     * 清空
     */
    fun clear(){
       diskCache.delete()
    }
}

测试使用

        //测试
        var diskLRUCacheUtils = DiskLRUCacheUtils.instance.putValue("111","我是测试数据")
        var str = DiskLRUCacheUtils.instance.getValue("111")
        Toast.makeText(this,"$str",Toast.LENGTH_LONG).show()

二.什么是责任链设计模式?

顾名思义,责任链模式(Chain of Responsibility
Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求
的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。
在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不
能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

1.ResultCallback接口回调

public interface ResultCallback<T> {
    /**
     * 成功
     * @param 
     * @return 
     * @author zhangyue
     * @time 2021/9/25 8:28
     */ 
    void Success(T t);
    /**
     * 失败
     * @param 
     * @return 
     * @author zhangyue
     * @time 2021/9/25 8:28
     */ 
    void Failed(Throwable throwable);
}

2.StorageChain抽象类以及实现子类


import com.zy.storage.callback.ResultCallback;

/**
 * @ProjectName: FrameworkApp
 * @Package: com.zy.storage
 * @ClassName: StorageChain
 * @Description:
 * 存储链节点的父类提供一些通用的默认实现
 * @Author: 张跃 企鹅:444511958
 * @CreateDate: 2021/8/18 11:18
 * @UpdateUser: 张跃
 * @UpdateDate: 2021/8/18 11:18
 * @UpdateRemark:
 * @Version: 1.0
 */
public abstract class StorageChain<T> {

    /**
     * 前一个节点
     */
    protected StorageChain previousChain;

    /**
     * 后一个节点
     */
    protected StorageChain nextChain;

    /**
     * 存储值
     * @param
     * @return
     * @author zhangyue
     * @time 2021/8/18 11:21
     */
    protected abstract void saveData(String key,T data);

    /**
     * 获取值
     * @param key
     * @param callback
     */
    protected abstract void getData(String key, ResultCallback<T> callback);

    /**
     * 按key删除具体的值
     * @param key
     */
    protected abstract void removeAtKey(String key);

    /**
     * 清空所有的数据
     */
    protected abstract void clear();

    /**
     * 设置下一个节点及设置下个节点的上一个节点是自己
     * @param nextChain
     */
    public void setNextChain(StorageChain nextChain){
        this.nextChain=nextChain;
        nextChain.previousChain=this;
    }

    public StorageChain getNextChain() {
        return nextChain;
    }
    
    /**
     * 判断是否有下级节点
     * @param
     * @return 
     * @author zhangyue
     * @time 2021/8/19 9:16
     */ 
    public boolean hasNext(){
        if (nextChain!=null){
            return true;
        }
        return false;
    }

    /**
     * 存入值暴露给别人使用 已经适配了对应关系
     * @param
     * @return
     * @author zhangyue
     * @time 2021/8/18 15:25
     */
    public void putValue(String key,T value){
        //当前节点已经存储
        saveData(key,value);
        if (this.nextChain!=null){
            this.nextChain.putValue(key,value);
        }
    }

    /**
     * 取值暴露给别人使用 已经适配取的对应关系
     * @param
     * @return
     * @author zhangyue
     * @time 2021/8/18 15:30
     */
    public void getValue(final String key, final ResultCallback<T> callback){
        getData(key, new ResultCallback<T>() {
            @Override
            public void Success(T t) {
                //当前链节点没有取到对应数据则向下级节点获取
                if (null==t){
                    nextChain.getValue(key,callback);
                }
                //如果已经取到同步前面的节点
                else{
                    if (null!=previousChain){
                        previousChain.putValue(key,t);
                    }
                }

                callback.Success(t);
            }

            @Override
            public void Failed(Throwable throwable) {

            }
        });
    }

    /**
     * 根据key删除数据暴露给别人使用 已经适配取的对应关系
     * @param
     * @return 
     * @author zhangyue
     * @time 2021/8/18 15:37
     */ 
    public void removeValue(String key){
        removeAtKey(key);
        if (this.nextChain!=null){
            this.nextChain.removeValue(key);
        }
    }

    /**
     * 清空数据
     */
    public void removeAll(){
        clear();
        if (this.nextChain!=null){
            this.nextChain.removeAll();
        }
    }

    /**
     * 获取首节点
     * @return
     */
    public StorageChain getFirstChain(){
        StorageChain own=this;
        while (own.previousChain!=null){
            own=previousChain;
        }
        return own;
    }

}

DiskChain:磁盘链

package com.zy.storage.impl;

import com.zy.storage.StorageChain;
import com.zy.storage.callback.ResultCallback;
import com.zy.storage.utils.DiskLRUCacheUtils;

/**
 * @ProjectName: FrameworkApp
 * @Package: com.zy.storage.impl
 * @ClassName: DiskChain
 * @Description:
 * @Author: 张跃 企鹅:444511958
 * @CreateDate: 2021/8/18 15:42
 * @UpdateUser: 张跃
 * @UpdateDate: 2021/8/18 15:42
 * @UpdateRemark:
 * @Version: 1.0
 */
public class DiskChain<T> extends StorageChain<T> {
    @Override
    protected void saveData(String key, T data) {
        DiskLRUCacheUtils.getInstance().putValue(key,data);
    }

    @Override
    protected void getData(String key, ResultCallback<T> callback) {
        T value = (T) DiskLRUCacheUtils.getInstance().getValue(key);
        callback.Success(value);
    }

    @Override
    protected void removeAtKey(String key) {
        DiskLRUCacheUtils.getInstance().removeValue(key);
    }

    @Override
    protected void clear() {
        DiskLRUCacheUtils.getInstance().clear();
    }
}

DiskChain:内存链

package com.zy.storage.impl;

import com.zy.storage.StorageChain;
import com.zy.storage.callback.ResultCallback;
import com.zy.storage.utils.LRUCacheUtils;

/**
 * @ProjectName: FrameworkApp
 * @Package: com.zy.storage.impl
 * @ClassName: MemoryChain
 * @Description:内存上的存储链节点逻辑
 * @Author: 张跃 企鹅:444511958
 * @CreateDate: 2021/8/18 15:39
 * @UpdateUser: 张跃
 * @UpdateDate: 2021/8/18 15:39
 * @UpdateRemark:
 * @Version: 1.0
 */
public class MemoryChain<T> extends StorageChain<T> {
    @Override
    protected void saveData(String key, T data) {
        LRUCacheUtils.getInstance().putValue(key,data);
    }

    @Override
    protected void getData(String key, ResultCallback<T> callback) {
        T value = (T) LRUCacheUtils.getInstance().getValue(key);
        if (null!=callback){
            callback.Success(value);
        }

    }

    @Override
    protected void removeAtKey(String key) {
        LRUCacheUtils.getInstance().removeValue(key);
    }

    @Override
    protected void clear() {
        LRUCacheUtils.getInstance().clear();
    }
}

3.StorageChainManager管理类

package com.zy.storage;

import android.util.ArrayMap;

import com.zy.storage.callback.ResultCallback;
import com.zy.storage.impl.DiskChain;
import com.zy.storage.impl.MemoryChain;

import java.sql.SQLTransactionRollbackException;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @ProjectName: FrameworkApp
 * @Package: com.zy.storage
 * @ClassName: StorageChainManager
 * @Description:
 * @Author: 张跃 企鹅:444511958
 * @CreateDate: 2021/8/18 16:31
 * @UpdateUser: 张跃
 * @UpdateDate: 2021/8/18 16:31
 * @UpdateRemark:
 * @Version: 1.0
 */
public class StorageChainManager {
    private static ConcurrentHashMap<String,StorageChain> chainMap;
    private static StorageChainManager instance=null;
    private StorageChainManager(){
        chainMap=new ConcurrentHashMap<>();
    }
    private static class Handler{
        private static StorageChainManager INSTANCE=new StorageChainManager();
    }

    public static StorageChainManager getInstance(){
        return Handler.INSTANCE;
    }

    /**
     * 第一个节点 内存
     */
    private MemoryChain memoryChain=null;
    /**
     * 第二个节点 磁盘
     */
    private DiskChain diskChain=null;

    /**
     * 初始化链
     * @param 
     * @return 
     * @author zhangyue
     * @time 2021/8/19 9:00
     */ 
    private StorageChain initChain(String key){
        memoryChain=new MemoryChain();
        diskChain=new DiskChain();
        memoryChain.setNextChain(diskChain);

        chainMap.put(key,memoryChain);
        return diskChain;
    }

    /**
     * 追加链节点
     * @param key-链的key标识
     * @return
     * @author zhangyue
     * @time 2021/8/19 9:03
     */
    public StorageChainManager addChain(String key,StorageChain storageChain){
        if (chainMap.containsKey(key)){
            StorageChain chain = chainMap.get(key);
            while (chain.hasNext()){
                chain=chain.getNextChain();
            }

            chain.setNextChain(storageChain);
        }else{
            StorageChain lastestChain = initChain(key);
            lastestChain.setNextChain(storageChain);
            chainMap.put(key,lastestChain.getFirstChain());
        }
        return this;
    }
    
    /**
     * 获取指定链上的值
     * @param
     * @return 
     * @author zhangyue
     * @time 2021/8/19 9:15
     */ 
    public void getValue(String chainKey,String key, ResultCallback callback){
        if (chainMap.containsKey(chainKey)){
            chainMap.get(chainKey).getValue(key,callback);
        }
    }

    /**
     * 获取到对应链然后存储值
     * @param
     * @return 
     * @author zhangyue
     * @time 2021/8/19 9:16
     */ 
    public <T> void putValue(String chainKey,String key,T data){
        if (chainMap.containsKey(chainKey)){
            chainMap.get(chainKey).putValue(key,data);
        }
    }
}

三.DeepLink深链接

1.什么是 Deeplink?

目前移动端 Deeplink 的说法有狭义和广义之分,分为 Deeplink 和 Deferred Deeplink。

2.Deeplink:

深度链接,指已安装相应 App 的情况下,把特定的参数通过 url 的形式传递给 App,从而直接打开指定的内部页面,实现
从链接直达 App 内部页面的跳转。

3.Deferred Deeplink:

延迟深度链接,主要增加了一个是否已安装相应 App 的判断,用户点击链接时,如果未安装 App,则引导用户前往应用市场,下载完对应 App 后,首次打开该 App 时自动跳转进入指定的内部页面。一般业内常说的“Deeplink”其实就是这两层含义相结合的简称。事实上只要结合好这两者,用户不管是否下载了对应 App,只要点击链接,都能一键拉起 App,一步直达想去的任何页面

4.Deferred Deeplink:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值