Android实践:自实现Memory/DiskCache逻辑

本文深入探讨了Android中自定义Memory/DiskCache的实现过程,详细解析了journal文件的作用,以及LRU缓存策略在内存和磁盘缓存中的应用。通过理解Snapshot和Editor机制,提升应用的性能和用户体验。
摘要由CSDN通过智能技术生成
在Android开发过程中,我们接触的缓存技术主要分为内存缓存和磁盘缓存。它们一般会被用来缓存http返回result、下载后渲染的Bitmap和文案等等。虽然现在已经有了比较成熟的缓存框架和工具,为了能让大家更加了解底层的实现和使用,下面我们就分别从原理和实践方面,给大家介绍下这两种技术:

一、内存缓存
1.简介

  会占用内存成本,但是达到快速访问目的;  
  合适缓存大小没有准则,考虑屏幕密度和大小,图片数量和质量,访问频率等因素来权衡;
  缓存过小导致额外的开销,过大导致OOM;
  注意:过去常用SoftReference或者WeakReference来实现Bitmap的缓存,现在并不推荐。因为从Android2.3的垃圾回收器更积极的回收它们,使得它们基本无效;Android3.0之前,开发不需要对Btimap进行手动的释放,内存中保存的位图数据回收不可预测,可能导致程序超出内存限制崩溃;
2.原理
关于内存缓存的实现,核心是LinkedHashMap<K, V>数据结构。它以强引用的方式保存了对象写入顺序,当我们的操作缓存(写和读都是线程安全的)到达极限就删除最近最少被使用的缓存,直到达到目标大小。从而实现Lru(Least Recently Used)算法;
3.使用
使用LruCache,有如下步骤:
  初始化LruCache,指定缓存的大小;
  重写sizeOf()方法,实现对缓存的数据大小计算逻辑;
  根据需要,是否要重写entryRemoved()方法;
  根据业务,使用key调用get()和put()方法读写缓存;

二、硬盘缓存
1.简介:

  内存缓存解决了快速访问的问题,但是当加载大量图片会很容易达到内存缓存限制;
  应用程序被其它任务(如接入电话)打断进入后台,Activity被杀死后内存缓存丢失,恢复后还得重新获取;
  当位图在内存缓存中不再可用时,硬盘缓存来保存这些位图,来减少网络下载次数;
  当然硬盘读取位图的速度比内存慢,所以应该在后台线程中进行;
2.原理:
关于硬盘缓存的实现原理,我们要关系的核心有如下2方面的问题:
  硬盘缓存相关信息是以什么形式保存的?
  硬盘缓存是如何读写,并实现Lru算法的?
问题1:硬盘缓存相关信息是通过两种文件进行记录的;
  缓存日志文件:该文件记录了磁盘缓存当前状态,执行的所有写入、保存、删除、读取动作;
    libcore.io.DiskLruCache
    1
    100
    2
    CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054
    DIRTY 335c4c6028171cfddfbaae1a9c313c52
    CLEAN 335c4c6028171cfddfbaae1a9c313c52 3934 2342
    REMOVE 335c4c6028171cfddfbaae1a9c313c52
    DIRTY 1ab96a171faeeee38496d8b330771a7a
    CLEAN 1ab96a171faeeee38496d8b330771a7a 1600 234
    READ 335c4c6028171cfddfbaae1a9c313c52
    READ 3400330d1dfc7f3f7f4b8d4d803dfcf6
    第一部分:
      CLEAN:缓存写入完毕,成为正式的缓存;
      DIRTY:正在写入的缓存,成功后就会继续记录一条CLEAN日志
      REMOVE:删除缓存;
      READ:读取缓存;
    第二部分:缓存对应的key;
    第三部分:
      缓存文件的长度。一条缓存可能存在多个缓存文件,如果有多个缓存文件,则有多个文件的长度;
      DIRTY时,写入时缓存文件名称为"key."+i+".tmp"。CLEAN时,写入文件更名为"key."+i(i为第几个缓存文件);
    解释:CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054
      CLEAN:成功写入一条缓存;
      3400330d1dfc7f3f7f4b8d4d803dfcf6 :key为3400330d1dfc7f3f7f4b8d4d803dfcf6;
      832 21054:该条缓存有两个缓存文件,分别为3400330d1dfc7f3f7f4b8d4d803dfcf6.0.tmp和3400330d1dfc7f3f7f4b8d4d803dfcf6.1.tmp,长度分别为832和21054;
  缓存文件:该文件保存了我们具体缓存的内容。如图片,字符串等;
问题2:
  硬盘缓存初始化时,逐条读取缓存日志文件中每一条操作日志,“还原”缓存对象集合;
  每个缓存对象包含:key—唯一标识每天缓存;名为key.index的缓存文件流集合—用于读写缓存文件;
  每次执行完CLEAN,DIRTY,REMOVE等动作后,将检查缓存对象集合缓存的个数是否超过限制,如果超过则删除最早保存的缓存;
3.使用
使用DiskLruCache的步骤如下:
  异步任务中,调用open()方法初始化硬盘缓存,并指定缓存的目录;
  根据业务,使用key调用get(key)方法返回Snapshot对象,通过该对象调用getInpuStream(index)获取对应缓存文件的输入流,来读取缓存数据;
  根据业务,使用key调用edit(key)方法返回Editor对象,通过该对象调用newOutPutStream(index)获取要缓存文件的输出流,来写入缓存数据,别忘记commit()提交;

三、实践
相信大家对两种缓存技术已经有了一定的认识,下面我们就结合一个简单的图片列表的实例,跟大家展示一下如何使用内存缓存和硬盘缓存技术。
  首次从服务端下载图片,分别存储在内存缓存和磁盘缓存中,然后展示在列表中;
  后面先从内存缓存中查找图片,如有没有则从磁盘缓存查找,再没有从服务端下载(完成后存入内存和磁盘缓存),然后展示在列表中;
1.项目目录

ImageListAdapter.java:图片列表适配器,处理了列表的展示和缓存相关逻辑(这里仅仅用于演示,故并没有缓存等相关功能单独抽出来);
DiskLruCache.java:从Android演示项目DisplayingBitmaps复制该硬盘缓存实现文件;
CacheList.java:图片列表显示Activity;
2.代码实现
CacheActivity.java
public class CacheActivity extends AppCompatActivity{
    private GridView gridView;
    private ImageListAdapter imageListAdapter;


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


        gridView = (GridView) findViewById(R.id.gridview1);
        ArrayList<String> imageUrlList = new ArrayList<>();
        Random random = new Random();
        for (int i = 0; i < 40; i++) {
            imageUrlList.add("http://localhost:8080/qserver/img/" + random.nextInt(16) % 6 + ".jpg");


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值