Android性能优化之缓存的代码实现

 Hi,众猿们,昨天大概谈了一下三级缓存的原理,今天就说其代码实现吧,主要有:
    1.随时获取全局Context(上下文对象)的方式
    2.如何代码实现json的三级缓存
    3.如何编写一个实现Lru算法的缓存类
    4.编写一个用LruCache类实现图片的内存缓存的工具类

第一个问题:第一个问题貌似和后三个没啥关系,其实json缓存中获取SharedPreference对象是需要通过Context对象的,因此我们要先解决第一个问题。我们需要创建一个自定义的Application对象:MyApplication,该对象基础自系统的Application对象,其代码如下:
public class MyApplication extends Application {
    //创建一个静态全局变量用于保存上下文对象
    private static Context mContext;

    @Override
    public void onCreate() {
        super.onCreate();
        //创建应用时获取context对象并保存在全局变量中
        mContext = getApplicationContext();
    }
    /**
     * 静态方法,可直接由类名调用,用于获取上下文对象
     * @return
     */
    public static Context getContext(){
        return mContext;
    }
}
这样,当我们需要用到全局变量时,直接通过类名MyApplication调用getContext()方法即可,很简单吧,还不到20行代码,其实自定义Application对象有很多妙用,如在应用初始化时创建一些全局变量等,这个我看以后有木有机会再说吧。还要注意一点,我们自定义的Application对象需要在AndroidManifest文件中注册,记载application标签下添加下列属性即可:   
android:name="lmz.threelevelcache.application.MyApplication"

第二个问题,不多说直接贴代码吧:

public class JsonCacheUtil {
    public static final int ERROR_NET_NUSED = 1;
    public static final int ERROR_NO_RESULT = 0;
    public static final String KEY_JSONCACHE = "JsonCache";

    public static void loadJson(
            String url,
            SuccessCallback successCallback,
            FailCallback failCallback){
        String result = null;
        //从本地缓存加载数据,
        result = loadFromLocalCache(url);
        if(TextUtils.isEmpty(result)){
            loadFromNet(url, successCallback, failCallback);
        }

    }


    /**
     * 从网络加载json数据,
     * @param url 请求数据的url,它会以get方式向服务器请求数据,且
     *      请求参数已经添加到的url尾部
     * @param successCallback
     * @param failCallback
     */
    private static void loadFromNet(
            final String url,
            final SuccessCallback successCallback, 
            final FailCallback failCallback) {

        if(!isNetCanAccessable()){
            if (failCallback != null) {
                failCallback.onFail(ERROR_NET_NUSED);
            }
            return;
        }

        new AsyncTask<Void, Void, String>(){
            @Override
            protected String doInBackground(Void... params) {
                URLConnection connection = null;
                try {
                    connection = new URL(url).openConnection();
                    connection.setConnectTimeout(3000);
                    connection.setReadTimeout(3000);
                    connection.connect();
                    BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                    StringBuffer buffer = new StringBuffer();
                    String line = null;
                    while((line = reader.readLine()) != null){
                        buffer.append(line);
                    }
                    return buffer.toString();
                } catch (Exception e) {}
                return null;
            }
            @Override
            protected void onPostExecute(String result) {
                if (result != null) {
                    //将json数据缓存在本地
                    cacheJsonToLocal(url, result);
                    if (successCallback != null) {
                        successCallback.onSuccess(result);
                    }
                }else {
                    if (failCallback != null) {
                        failCallback.onFail(ERROR_NO_RESULT);
                    }
                }
                super.onPostExecute(result);
            }
        }.execute();
    }

    /**
     * 缓存json数据至本地的方法
     * @param url
     * @param result
     */
    protected static void cacheJsonToLocal(String url, String result) { 
        MyApplication.getContext()
            .getSharedPreferences(KEY_JSONCACHE, Context.MODE_PRIVATE)
            .edit().putString(url, result).commit();
    }

    /**
     * 判断网络是否连接
     * @return 网络已经连接则返回true
     */
    private static boolean isNetCanAccessable() {
        boolean flag = false;
        ConnectivityManager manager = (ConnectivityManager) MyApplication.getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo info = manager.getActiveNetworkInfo();
        if (info != null) {
            flag = info.isAvailable();
        }
        return false;
    }

    /**
     * 根据url从本地缓存中加载json数据
     * @param url
     * @return 若本地未缓存url对应的json数据则直接返回空字符串
     */
    private static String loadFromLocalCache(String url) {
        return MyApplication.getContext()
                .getSharedPreferences(KEY_JSONCACHE, Context.MODE_PRIVATE)
                .getString(url, "");
    }

    public interface SuccessCallback{ public void onSuccess(String result);}
    public interface FailCallback{public void onFail(int error);}
}
代码上都有注释,本猿猿就不在多说了。

第三个问题,昨天的文章已经说过(戳这里看昨天文章),LRU算法有不同的实现方式,不同的方式在命中率、效率、时间空间复杂度等方面也不同,它们各有优缺点,这儿我就挑一个最简单的方式来实现吧(HashMap+链表,其实本猿猿也并不是什么老猿,算法功底也没那么好),其主要的思想为:建立一个链表,储存着HashMap中所有的键,当有值加入HashMap或从HashMap中取出时,将其对应的键移至表头,而每次移除对象时,总是优先移除链表尾的键所对应的值。其代码如下。其完整代码如下:
public abstract class MyLruCache<K, V> {
    private int mMaxSize = 0;
    private int currSize = 0;

    private Map<K, V> mValues = new LinkedHashMap<K, V>();
    private List<K> mPositions = new LinkedList<K>();

    public MyLruCache(int maxSize){
        //设置该所管理的空间的最大值
        this.mMaxSize = maxSize;
    }

    /**
     * 将指定的键和值放入集合中
     * @param k
     * @param v
     */
    public void put(K k, V v){
        if (mPositions.contains(k)) {
            return;
        }

        int size = sizeof(v);
        //若加入了值后该对象所占的空间大于最大空间,则不断移除对象直到不大于最大空间
        while(currSize + size > mMaxSize){
            removeFromTail();
        }

        mValues.put(k, v);
        mPositions.add(k);

    }

    /**
     * 根据指定的键得到值
     * @param k
     * @return
     */
    public V get(K k){
        if ( !mPositions.contains(k)) {
            return null;
        }

        if (mPositions.remove(k)) {
            mPositions.add(k);
        }else{
            return null;
        }
        return mValues.get(k);
    }

    /**
     * 根据指定的键移除值
     * @param k
     */
    public void remove(K k){
        if ( !mPositions.contains(k)) {
            return;
        }
        mPositions.remove(k);
        mValues.remove(k);
    }

    /**
     * 从链表尾部移除值
     */
    private void removeFromTail() {
        K k = mPositions.remove(0);
        V v = mValues.remove(k);
        currSize =- sizeof(v);
    }


    /**
     * 用于计算加入该空间的每一个值得大小,子类必须实现该方法
     * @param v
     * @return
     */
    abstract protected int sizeof(V v);
}
Android系统的LruCache对象也是采用这种方式(微信搜索公众号:brooklee123,关注本猿猿的微信公众号“猿聚于此”,回复关键字“LruCache”,即可得到LruCache的源码),LRU算法不仅仅局限于处理图片的缓存,只要我们需要维护一定大小的某一空间(如内存),都可以使用Android为我们提供的LruCache对象。


好了,最后一个问题,不多说了,直接上源码:
public class BitmapMemoryCacheUtils {

    private LruCache<String, Bitmap> mMemCache;

    public BitmapMemoryCacheUtils() {
        long maxMemory = Runtime.getRuntime().maxMemory();// 模拟器默认是16M内存
        mMemCache = new LruCache<String, Bitmap>((int) (maxMemory / 8)) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getRowBytes() * value.getHeight();// 返回图片大小
            }
        };
    }

    /**
     * 从内存中取图片
     * 
     * @param url
     * @return
     */
    public Bitmap getBitmapFromMem(String url) {
        return mMemCache.get(url);
    }

    /**
     * 向内存中存图片
     * 
     * @param url
     * @param bitmap
     */
    public void putBitmapToMem(String url, Bitmap bitmap) {
        mMemCache.put(url, bitmap);
    }
}
对了,要想理解这一部分,你还有了解URLConnection及其子类HttpURLConnection对象、AsyncTask对象,有机会的话本猿会捯饬捯饬这两个对象的。
本猿还开了一个个人微信公众号:猿聚于此,本猿基本每天都会在该公众号上更新文章,如果大家像获得文章更新的最新信息的话,欢迎关注该微信公众号,微信搜索brooklee123即可关注,最后出个小题,就当娱乐一下吧,想不出来答案的猿猿们可要好好努力呀:
食堂有两个程序猿,一个吃完小心翼翼地把餐具送完才走,一个没送餐具就遛了,请问那个是C++程序猿?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值