android轻量级开源缓存结构——ASimpleCache(ACache)源码剖析-Android-优质IT资源分享社区

admin
管理员
管理员
  • UID1
  • 粉丝29
  • 关注4
  • 发帖数581
  • 社区居民
  • 忠实会员
  • 原创写手
阅读:261回复:0

  android轻量级开源缓存结构——ASimpleCache(ACache)源码剖析

楼主#
更多 发布于:2016-05-31 16:31

ASimpleCache结构源码衔接

https://github.com/yangfuhai/ASimpleCache

杨神作品,大家最了解他的应该是afinal结构吧

官方介绍

ASimpleCache 是一个为android制定的 轻量级的

开源缓存结构。轻量到只要一个java文件(由十几个类精简而来)。

1、它能够缓存啥东西?

一般的字符串、JsonObject、JsonArray、Bitmap、Drawable、序列化的java目标,和

byte数据。

2、它有啥特征?

特征首要是:

1:轻,轻到只要一个JAVA文件。

2:可装备,能够装备缓存途径,缓存巨细,缓存数量等。

3:能够设置缓存超时时刻,缓存超时主动失效,并被删除。

4:支撑多进程。

3、它在android中能够用在哪些场景?

1、更换SharePreference作为装备文件

2、能够缓存网络恳求数据,比方oschina的android客户端能够缓存http恳求的新闻内容,缓存时刻假设为1个小时,超时后主动失效,让客户端从头恳求新的数据,削减客户端流量,一起削减服务器并发量

3、您来说…

4、怎么运用 ASimpleCache?

以下有个小的demo,期望您能喜爱:

ACache mCache =

ACache.get(this);mCache.put("test_key1", "test value");mCache.put("test_key2",

"test value", 10);//保留10秒,假如超越10秒去获取这个key,将为nullmCache.put("test_key3", "test

value", 2 * ACache.TIME_DAY);//保留两天,假如超越两天去获取这个key,将为null

获取数据

ACache mCache = ACache.get(this);String value =

mCache.getAsString("test_key1");

源码剖析

一、ACache类结构图

ASimpleCache里只要一个JAVA文件——ACache.java

首要我用思维导图制作了ACache类的具体结构图:

二、官方demo剖析

经过剖析官方给的demo来驱动源码剖析吧

以字符串存储为例(官方给的demo里给出了很多种数据读取的比方,本来办法类似),翻开SaveStringActivity.java:

package com.yangfuhai.asimplecachedemo;import

org.afinal.simplecache.ACache;import android.app.Activity;import

android.os.Bundle;import android.view.View;import android.widget.EditText;import

android.widget.TextView;import android.widget.Toast;/** * * @ClassName:

SaveStringActivity * @Description: 缓存string * @Author Yoson Hao * @WebSite

www.haoyuexing.cn * @Email haoyuexing@gmail.com * @Date 2013-8-7 下午9:59:43 *

*/public class SaveStringActivity extends Activity {    private EditText

mEt_string_input;    private TextView mTv_string_res;    private ACache mCache;

   @Override    protected void onCreate(Bundle savedInstanceState) {      

 super.onCreate(savedInstanceState);      

 setContentView(R.layout.activity_save_string);        // 初始化控件      

 initView();        mCache = ACache.get(this);    }    /**     * 初始化控件     */  

 private void initView() {        mEt_string_input = (EditText)

findViewById(R.id.et_string_input);        mTv_string_res = (TextView)

findViewById(R.id.tv_string_res);    }    /**     * 点击save事情     *     * @param

v     */    public void save(View v) {        if

(mEt_string_input.getText().toString().trim().length() == 0) {          

 Toast.makeText(                    this,                    "Cuz u input is a

nullcharacter ... So , when u press \"read\" , if do not show any result , plz

don't be surprise",                    Toast.LENGTH_SHORT).show();        }//  

   mCache.put("testString", mEt_string_input.getText().toString());      

 mCache.put("testString", mEt_string_input.getText().toString(),300);    }  

 /**     * 点击read事情     *     * @param v     */    public void read(View v) {  

     String testString = mCache.getAsString("testString");        if (testString

== null) {            Toast.makeText(this, "String cache is null ...",

Toast.LENGTH_SHORT)                    .show();          

 mTv_string_res.setText(null);            return;        }      

 mTv_string_res.setText(testString);    }    /**     * 点击clear事情     *     *

@param v     */    public void clear(View v) {      

 mCache.remove("testString");    }}

能够看到缓存字符串的读取办法很简略!!!

在onCreate里经过get办法获取缓存实例

mCache = ACache.get(this);

在save按钮的点击事情里,经过put办法往缓存实例里保留字符串

mCache.put(“testString”,

mEt_string_input.getText().toString(),300);

在read按钮的点击事情里,经过getAsString办法从缓存实例里读取字符串

mCache.getAsString(“testString”);

别的数据读取,办法类似,也是这三个进程。300为保留时刻300秒。

三、ACache源码剖析

1、获取缓存实例

那咱们就从ACache.get()开端吧,本来检查上面的思维导图,ACache类的结构办法为private的,所以新建缓存实例只能经过ACache.get办法获取。

//实例化运用程序场景缓存    public static ACache get(Context

ctx) {        return get(ctx, "ACache");    }    //新建缓存目录    public static

ACache get(Context ctx, String cacheName) {      

 //新建文件夹,文件途径为运用场景缓存途径目录,文件夹名为ACache(new File()也可新建文件,带上后缀即可)        File f =

new File(ctx.getCacheDir(), cacheName);        return get(f, MAX_SIZE,

MAX_COUNT);    }    //新建缓存实例,存入实例map,key为缓存目录+每次运用敞开的进程id    public static

ACache get(File cacheDir, long max_zise, int max_count) {      

 //回来key为缓存目录+每次运用敞开的进程id的map的value值,赋给缓存实例manager        ACache manager =

mInstanceMap.get(cacheDir.getAbsoluteFile() + myPid());        if (manager ==

null) { //缓存实例为空时,            manager = new ACache(cacheDir, max_zise,

max_count);            mInstanceMap.put(cacheDir.getAbsolutePath() + myPid(),

manager);//刺进map        }        return manager;    }

在调用ACache.get(Context)办法进程中,本来履行了三个get办法

(1)get(Context ctx)->(2)get(Context

ctx, String cacheName)->(3)get(File cacheDir, long max_zise, int

max_count)

在(2)中新建了缓存目录,途径为:

/data/data/app-package-name/cache/ACache

缓存巨细MAX_SIZE和数量MAX_COUNT均由final变量操控。

本来终究调用(3)获取实例:

mInstanceMap的key为缓存目录+每次运用敞开的进程id,value为ACache.

初度运转,mInstanceMap没有任何键值对,所以manager ==

null。故经过ACache结构办法结构新实例,最终将该实例引证存入mInstanceMap。

接下来咱们来看看ACache结构办法:

//ACache结构函数 为private私有(所以在别的类里获得实例只能经过get()办法)  

 private ACache(File cacheDir, long max_size, int max_count) {        if

(!cacheDir.exists() && !cacheDir.mkdirs()) {     //缓存目录不存在而且无法创立时,抛出反常  

         throw new RuntimeException("can't make dirs in " +

cacheDir.getAbsolutePath());        }        mCache = new

ACacheManager(cacheDir, max_size, max_count);//实例化ACacheManager内部类实例    }

缓存目录不存在而且无法创立时,抛出反常,不然实例化ACacheManager内部类实例(缓存管理器)。ACacheManager内部类的结构函数如下:

//内部类ACacheManager的结构函数        private

ACacheManager(File cacheDir, long sizeLimit, int countLimit) {          

 this.cacheDir = cacheDir;            this.sizeLimit = sizeLimit;          

 this.countLimit = countLimit;            cacheSize = new AtomicLong();      

//原子类实例cacheSize,不必加锁保证线程安全            cacheCount = new AtomicInteger();  

//原子类实例cacheCount,不必加锁保证线程安全            calculateCacheSizeAndCacheCount();      

 }

结构函数得到原子类实例cacheSize和cacheCount,经过calculateCacheSizeAndCacheCount();办法核算cacheSize和cacheCount如下:

/**         * 核算 cacheSize和cacheCount         */  

     private void calculateCacheSizeAndCacheCount() {            new Thread(new

Runnable() {                @Override                public void run() {        

           //int size = 0;                    long size = 0;

 //这儿long类型才对——by牧之丶                    int count = 0;                    File[]

cachedFiles = cacheDir.listFiles();  //回来缓存目录cacheDir下的文件数组                  

 if (cachedFiles != null) {                        for (File cachedFile :

cachedFiles) {   //对文件数组遍历                            size +=

calculateSize(cachedFile);                            count += 1;              

             lastUsageDates.put(cachedFile, cachedFile.lastModified());

 //将缓存文件和最终修正时刻刺进map                        }                      

 cacheSize.set(size);        //设置为给定值                      

 cacheCount.set(count);      //设置为给定值                    }                }    

       }).start();        }

calculateCacheSizeAndCacheCount办法中敞开线程进行巨细和数量的核算。核算完后存入cacheSize和cacheCount,cacheSize和cacheCount在内部类中界说为AtomicLong和AtomicInteger量子类,也即是线程安全的。其根本的特性即是在多线程环境下,当有多个线程一起履行这些类的实例包含的办法时,具有排他性,即当某个线程进入办法,履行其间的指令时,不会被别的线程打断,而别的线程就像自旋锁相同,一直比及该办法履行完结,才由JVM从等候行列中挑选一个另一个线程进入。

到这儿获取缓存实例作业完结,首要完结了如下作业:

新建了缓存目录

经过ACache结构办法结构新实例,而且将该实例引证刺进mInstanceMap

实例化ACacheManager,核算cacheSize和cacheCount

接下来即是数据存取操作。

2、往缓存实例存入数据

从上面的思维导图public

method(各种数据的读写办法)中,有各种public的put和get等办法来缓存各种数据类型的数据。由上面的demo的put办法

mCache.put(“testString”,

mEt_string_input.getText().toString(),300);

咱们找到原形:

/**     * 保留 String数据 到 缓存中     *     * @param key

    *            保留的key     * @param value     *            保留的String数据     *

@param saveTime     *            保留的时刻,单位:秒     */    public void put(String

key, String value, int saveTime) {        put(key,

Utils.newStringWithDateInfo(saveTime, value));    }

这儿的put办法能够指定缓存时刻。调用他本身的另一个put办法:

/**     * 保留 String数据 到 缓存中     *     * @param key

    *            保留的key     * @param value     *            保留的String数据     */  

 public void put(String key, String value) {        File file =

mCache.newFile(key);    //新建文件        BufferedWriter out = null;        

 //缓冲字符输出流,作用是为别的字符输出流添加一些缓冲功用        try {            out = new

BufferedWriter(new FileWriter(file), 1024);   //获取file字符流          

 out.write(value);       //  把value写入        } catch (IOException e) {          

 e.printStackTrace();        } finally {            if (out != null) {          

     try {                    out.flush();                    out.close();      

         } catch (IOException e) {                    e.printStackTrace();      

         }            }            mCache.put(file);   //更新cacheCount和cacheSize

 lastUsageDates刺进新建文件和时刻的键值对        }    }

在put(String key, String

value)办法中首要调用mCache.newFile(key)新建一个文件:

//新建文件        private File newFile(String key) {  

         return new File(cacheDir, key.hashCode() + "");    

//新建文件,文件名为key的整型哈希码        }

新建的文件名为key的整型哈希码。回到put(String

key, String

value)中,然后经过out.write(value);将数据存入文件。最终调用mCache.put(file);进行ACacheManager实例的更新操作

//更新cacheCount和cacheSize

 lastUsageDates刺进新建文件和时刻的键值对  

 //文件放入程序缓存后,核算缓存总量,总数,文件存放到文件map中(value值为文件最终修正时刻,便于依据设置的毁掉时刻进行毁掉)  

 //缓存没有超越约束,则添加缓存总量,总数的数值    //缓存超越约束,则削减缓存总量,总数的数值    //经过removeNext办法找到最老文件的巨细

       private void put(File file) {            int curCacheCount =

cacheCount.get();   //获取数量            while (curCacheCount + 1 > countLimit)

{    //大于上限                long freedSize = removeNext();        

 //移除旧的文件,回来文件巨细                cacheSize.addAndGet(-freedSize);      

 //更新cacheSize                curCacheCount =

cacheCount.addAndGet(-1);//更新cacheCount            }          

 cacheCount.addAndGet(1);//更新cacheCount            long valueSize =

calculateSize(file);       //核算文件巨细            long curCacheSize =

cacheSize.get();        //获取当时缓存巨细            while (curCacheSize + valueSize

> sizeLimit) {  //大于上限                long freedSize = removeNext();        

     //移除旧的文件,回来文件巨细                curCacheSize =

cacheSize.addAndGet(-freedSize);     //更新curCacheSize            }          

 cacheSize.addAndGet(valueSize);                         //更新cacheSize          

 Long currentTime = System.currentTimeMillis();          

 file.setLastModified(currentTime);          //设置文件最终修正时刻          

 lastUsageDates.put(file, currentTime);      //刺进map        }

剖析完ACacheManager的put()后,咱们回到put(key,

Utils.newStringWithDateInfo(saveTime, value))

其间第二个参数value传入的是Utils.newStringWithDateInfo(saveTime,

value),而newStringWithDateInfo是ACache的内部工具类的一个办法,在value内容前面加上了时刻信息

//回来时刻信息+value        private static String

newStringWithDateInfo(int second, String strInfo) {            return

createDateInfo(second) + strInfo;        }

回来拼接createDateInfo(second)和value的字符串。createDateInfo()如下:

//时刻信息        private static String

createDateInfo(int second) {            String currentTime =

System.currentTimeMillis() + "";            while (currentTime.length() < 13)

{     //小于13,前面补0                currentTime = "0" + currentTime;            }  

         return currentTime + "-" + second + mSeparator; //当时时刻-保留时刻      

 }

回来字符串格局为 当时时刻-保留时刻“ ”

3、从缓存实例读取数据

由上面的demo的get办法mCache.getAsString(“testString”);咱们找到原形:

/**     * 读取 String数据     *     * @param key     *

@return String 数据     */    public String getAsString(String key) {        File

file = mCache.get(key);    //获取文件        if (!file.exists())            return

null;        boolean removeFile = false;        BufferedReader in = null;      

 try {            in = new BufferedReader(new FileReader(file));          

 String readString = "";            String currentLine;            while

((currentLine = in.readLine()) != null) { //逐行遍历                readString +=

currentLine;  //每行字符串衔接            }            if (!Utils.isDue(readString)) {

//String数据未到期                    return

Utils.clearDateInfo(readString);//去掉时刻信息的字符串内容            } else {              

 removeFile = true;      //移除文件象征位为真                return null;            }  

     } catch (IOException e) {            e.printStackTrace();            return

null;        } finally {            if (in != null) {                try {      

             in.close();                } catch (IOException e) {              

     e.printStackTrace();                }            }            if

(removeFile)                remove(key);    //移除缓存        }    }

getAsString(String

key)办法里首要经过缓存管理器的mCache.get(key)办法获取文件,然后用Utils.isDue(readString)**判别是不是字符串数据到期,未到期回来去掉时刻信息的字符串内容;到期则移除缓存,回来空。**Utils.isDue(readString)调用了isDue(byte[]

data)判别:

/**         * 判别缓存的byte数据是不是到期         *         *

@param data         * @return true:到期了 false:还没有到期         */        private

static boolean isDue(byte[] data) {            String[] strs =

getDateInfoFromDate(data);            if (strs != null && strs.length ==

2) {                String saveTimeStr = strs[0];                while

(saveTimeStr.startsWith("0")) {                    saveTimeStr =

saveTimeStr.substring(1, saveTimeStr.length());                }              

 long saveTime = Long.valueOf(saveTimeStr);                long deleteAfter =

Long.valueOf(strs[1]);                if (System.currentTimeMillis() >

saveTime + deleteAfter * 1000) {                    return true;              

 }            }            return false;        }

至此全部缓存字符串读取进程在ACache的源码剖析完结,别的缓存数据类型读取办法剖析进程相同。

JsonObject、JsonArray、Bitmap、Drawable、序列化的存入缓存都是转化为字符串/byte格局,再调用函数put(String

key, String value)即可。

比方BitMap的保留:

/**     * 保留 bitmap 到 缓存中,有保留时刻     *     * @param

key     *            保留的key     * @param value     *            保留的 bitmap 数据  

  * @param saveTime     *            保留的时刻,单位:秒     */    public void put(String

key, Bitmap value, int saveTime) {        put(key, Utils.Bitmap2Bytes(value),

saveTime);    }

Bitmap的读取:

/**     * 读取 bitmap 数据     *     * @param key    

* @return bitmap 数据     */    public Bitmap getAsBitmap(String key) {        if

(getAsBinary(key) == null) {            return null;        }        return

Utils.Bytes2Bimap(getAsBinary(key));    }

思维即是把bitmap转化为byte[], 再调用缓存byte的函数

。经过Utils.Bitmap2Bytes(value)完结Bitmap → byte[]

的变换。经过Utils.Bytes2Bimap(getAsBinary(key))完结byte[] → Bitmap的变换。

/*         * Bitmap → byte[]         */      

 private static byte[] Bitmap2Bytes(Bitmap bm) {            if (bm == null) {  

             return null;            }                ByteArrayOutputStream baos

= new ByteArrayOutputStream();//byte[]输出流          

 bm.compress(Bitmap.CompressFormat.PNG, 100,

baos);//按指定的图片格局以及画质,将图片变换为输出流。紧缩图片,不紧缩是100,表明紧缩率为0            return

baos.toByteArray();        }        /*         * byte[] → Bitmap         */    

   private static Bitmap Bytes2Bimap(byte[] b) {            if (b.length == 0) {

               return null;            }                return

BitmapFactory.decodeByteArray(b, 0, b.length);//解析        }

很简略吧。Drawable的缓存即是先转化为Bitmap,以后即是上面的进程变换成byte。

总结

该开源库类简略,简单了解。

能够运用ACache把那些不需要实时更新的数据缓存起来,一来削减网络恳求,二来本地加载速度也快。

能够设置缓存时刻。

能够更换SharePreference作为装备文件,保留多种数据类型,比方能够保留头像信息。

优质IT资源分享社区为你提供此文。

站有大量优质android教程视频,资料等资源,包含android基础教程,高级进阶教程等等,教程视频资源涵盖传智播客,极客学院,达内,北大青鸟,猎豹网校等等IT职业培训机构的培训教学视频,价值巨大。欢迎点击下方链接查看。

android教程视频

优质IT资源分享社区(www.itziyuan.top)
一个免费,自由,开放,共享,平等,互助的优质IT资源分享网站。
专注免费分享各大IT培训机构最新培训教学视频,为你的IT学习助力!

!!!回帖受限制请看点击这里!!!
!!!资源失效请在此版块发帖说明!!!

[PS:按 CTRL+D收藏本站网址~]

——“优质IT资源分享社区”管理员专用签名~

本版相似帖子

游客