三级缓存 Volley /Lrucache/DiskLrucache + AndroidStudio导出jar包 + upload to github

  想了半天都没想出来写什么作为第一篇博客比较好,突然发现这次项目的版本控制是github,三级缓存和屏幕适配又是一个跑不掉的话题,就以这些话题开始我的博客之旅吧,同时总结一下在AndroidStudio中如何导出jar包。

([参考郭霖的blog](http://blog.csdn.net/guolin_blog/article/details/28863651))
([参考李晨玮的blog](http://www.cnblogs.com/lichenwei)/)

不管是提升用户体验还是提升APP的流畅度,三级缓存都是必不可少的,在这里就先总结一下三级缓存的使用。

  加载网络图片时,使用三级缓存可以有效的提高用户体验以及程序的流畅度。
  三级缓存为以下内容:网络层/内存层/磁盘层
  缓存的流程如下:
  1. 网络层下载图片 Volley->imageLoader
  2. 存入内存层 LruCache
  3. 存入磁盘层 DiskLruCache
  而当以上操作均完成以后,此次下载的图片就写入到了手机中,下次运行程序时,就会优先到目录中查找是否存在该图片,如果存在就直接使用,免除了多次加载耗费流量。
首先,在使用之前,包含volley的库以及DiskLrucache, 链接如下
volley
DiskLrucache

 1. 定义MyApplication, 在其中获取到context,避免每次都需要单独获取一次上下文对象。
 在MyApplication中设置有一个静态的TAG,这个TAG是为了获取基础类的名称,以此作为每一个加入RequestQueue的request的标志

package com.mylibs.volley_disklrucache_lrucache.customview;

import android.app.Application;

/**
 * Created by lizhongquan on 15-11-23.
 */
public class MyApplication extends Application {

    /**
     * Application
     */
    public static MyApplication myApplication;

    /**
     * Get Tag to setTag for everyAty
     */
    public static String TAG;

    public static MyApplication getMyApplication() {
        return myApplication;
    }

    @Override
    public void onCreate() {
        super.onCreate();

        myApplication = this;
        TAG = this.getClass().getSimpleName();
    }
}

然后在AndroidManifest文件中声明: 

<application
        android:name=".customview.MyApplication" >
</application>

同样的,为了避免重复编写,将各种工具类进行单独的封装

在Volley中,存在一个RequestQueue就可以了,其他所有的请求都直接放在这个队列中

package com.mylibs.volley_disklrucache_lrucache.utils;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.Volley;
import com.mylibs.volley_disklrucache_lrucache.customview.MyApplication;

/**
 * Created by lizhongquan on 15-11-23.
 */
public class VolleyRequestQueue {

    /**
     * get RequestQueue
     */
    public static RequestQueue mRequestQueue = Volley.newRequestQueue(MyApplication.getMyApplication());

    /**
     * add request
     */
    public static void addRequest(Request<?> request, Object tag){
        if (null != tag){
            request.setTag(tag);
        }
        mRequestQueue.add(request);
    }

    /**
     * cancel all request
     */
    public static void cancelRequest(Object tag){
        mRequestQueue.cancelAll(tag);
    }
}

       journal内容
对于DiskLruCache, 他是在手机的保存目录中创建一个journal文件,在此文件中有如下内容:
第一行是个固定的字符串“libcore.io.DiskLruCache”,标志着我们使用的是DiskLruCache技术。
第二行是DiskLruCache的版本号,这个值是恒为1。
第三行是应用程序的版本号,我们在open()方法里传入的版本号是什么这里就会显示什么。
第四行是valueCount,这个值也是在open()方法中传入的,通常情况下都为1。
第五行是一个空行。
前五行也被称为journal文件的头。
第六行是以一个DIRTY前缀开始的,后面紧跟着缓存图片的key。通常我们看到DIRTY这个字样都不代表着什么好事情,意味着这是一条脏数据。每当我们调用一次DiskLruCache的edit()方法时,都会向journal文件中写入一条DIRTY记录,表示我们正准备写入一条缓存数据,但不知结果如何。然后调用commit()方法表示写入缓存成功,这时会向journal中写入一条CLEAN记录,意味着这条“脏”数据被“洗干净了”,调用abort()方法表示写入缓存失败,这时会向journal中写入一条REMOVE记录。也就是说,每一行DIRTY的key,后面都应该有一行对应的CLEAN或者REMOVE的记录,否则这条数据就是“脏”的,会被自动删除掉。
第七行的那条记录,除了CLEAN前缀和key之外,后面还有一个152313,这是什么意思呢?其实,DiskLruCache会在每一行CLEAN记录的最后加上该条缓存数据的大小,以字节为单位。152313也就是缓存的图片的字节数了,换算出来大概是148.74K,和缓存图片刚刚好一样大。

以上的一段话包括图片均来自郭霖的博客。

同时,由上可以看出,我们必须对每一个文件名创建一个独立的文件名,比较推荐的一种方式是利用MD5转换。
这个工具类没有好介绍的,直接拿来用就可以了

MD5的工具类:

package com.mylibs.volley_disklrucache_lrucache.utils;

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * Created by lizhongquan on 15-11-23.
 */
public class MD5Util {

    /**
     * Encrypt the information by MD5
     */

    public static String md5(String plainText) {
        byte[] secretBytes = null;
        try {
            secretBytes = MessageDigest.getInstance("md5").digest(
                    plainText.getBytes());
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("no md5!");
        }

        /**
         * 16 Band
         */
        String md5code = new BigInteger(1, secretBytes).toString(16);

        /**
         * If number.lenth < 32, add 0 in its front
         */
        for (int i = 0; i < 32 - md5code.length(); i++) {
            md5code = "0" + md5code;
        }
        return md5code;
    }
}

之后,开始准备Volley的ImageLoader的图片缓存类ImageCacheUtil

图片缓存类包含的内容如下:
1.在它的构造方法中设置LruCache的最大缓存,首先获取到可用内存的最大值,使用内存如果超过这个值会引起OutOfMemory异常
2.使用最大可用内存值的1/8作为缓存的大小
3.从LruCache或者DiskLruCache中取出Bitmap, 对于DiskCache来说,首先会利用经MD5转换后的唯一名来进行查找,如果找到,就取出相关的资源
4.保存资源到LruCache或DiskLruCache中。对于DiskLruCache而言,同样是先检测后操作
5.获取磁盘的缓存地址。一般而言,缓存的地址会存放在 /sdcard/Android/data//cache 这个路径下,但是有的手机是没有SD卡的,因此需要进行一个判断。如果存在,使用getExternalCacheDir()方法来获取缓存路径,否则就调用getCacheDir()方法来获取缓存路径。前者获取到的是/sdcard/Android/data//cache ,后者获取到的是 /data/data//cache
6.获取应用程序的版本号。由于DiskLruCache的设置,所以只要我们APP的版本号改变了,缓存路径下存储的数据都会被清楚,因为DiskLruCache认为当应用程序有版本更新的时候,所有数据都应从网上重新获取。

package com.mylibs.volley_disklrucache_lrucache.utils;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.support.v4.util.LruCache;
import android.util.Log;

import com.android.volley.toolbox.ImageLoader;
import com.mylibs.volley_disklrucache_lrucache.DiskLruCache;
import com.mylibs.volley_disklrucache_lrucache.customview.MyApplication;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;

/**
 * Created by lizhongquan on 15-11-23.
 */
public class ImageCacheUtil implements ImageLoader.ImageCache {

    // get Tag
    private String TAG = ImageCacheUtil.this.getClass().getSimpleName();

    // LruCache / DiskLruCache
    private static LruCache<String, Bitmap> mLruCache;
    private static DiskLruCache mDiskLruCache;

    // DiskMaxSize
    private static final int DISKMAXSIZE = 10 * 1024 * 1024;

    public ImageCacheUtil() {
        // get 1/8 of memory to Cache
        int maxSize = (int) (Runtime.getRuntime().maxMemory() / 8);

        // Instance LruCaceh对象
        mLruCache = new LruCache<String, Bitmap>(maxSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getRowBytes() * bitmap.getHeight();
            }
        };

        // get DiskLruCahce
        try {
            mDiskLruCache = DiskLruCache.open(getDiskCacheDir(
                    MyApplication.getMyApplication(), "DiskLruCache"),
                    getAppVersion(MyApplication.getMyApplication()), 1, DISKMAXSIZE);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * getBitmap from LruCache or diskLruCache
     */
    @Override
    public Bitmap getBitmap(String url) {
        if (mLruCache.get(url) != null) {
            // get from LruCache
            Log.i(TAG, "get from LruCache");
            return mLruCache.get(url);
        } else {
            String key = MD5Util.md5(url);
            try {
                if (mDiskLruCache.get(key) != null) {
                    // get from DiskLruCache
                    DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
                    Bitmap bitmap = null;
                    if (snapshot != null) {
                        bitmap = BitmapFactory.decodeStream(snapshot.getInputStream(0));
                        // save into LruCache
                        mLruCache.put(url, bitmap);
                        Log.i(TAG, "get from diskLruCache");
                    }
                    return bitmap;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * save into LruCache or DiskLruCache
     */
    @Override
    public void putBitmap(String url, Bitmap bitmap) {
        // save into LruCache
        mLruCache.put(url, bitmap);

        // get the uniqueName
        String key = MD5Util.md5(url);
        try {
            // Judge if the DiskLruCache exists
            // If not, save into DiskLruCache
            if (mDiskLruCache.get(key) == null) {
                DiskLruCache.Editor editor = mDiskLruCache.edit(key);
                if (editor != null) {
                    OutputStream outputStream = editor.newOutputStream(0);
                    if (bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)) {
                        editor.commit();
                    } else {
                        editor.abort();
                    }
                }
                mDiskLruCache.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * Judge the SDCard is exits or not and then choose the DiskLruCacheDir
     *
     * @param context context
     * @param uniqueName the name of cache
     * @return The cachePath
     */
    public static File getDiskCacheDir(Context context, String uniqueName) {
        String cachePath;
        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
                || !Environment.isExternalStorageRemovable()) {
            cachePath = context.getExternalCacheDir().getPath();
        } else {
            cachePath = context.getCacheDir().getPath();
        }
        return new File(cachePath + File.separator + uniqueName);
    }

    /**
     * getAppVersion
     *
     * @param context
     * @return appVersion
     */
    public int getAppVersion(Context context) {
        try {
            PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
            return info.versionCode;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return 1;
    }
}

图片缓存管理类ImageCacheManager
向外部提供一个loadImage的重载方法,一个传入加载图片的宽高,一个默认加载原图,使外部不再需要关注任何关于缓存的操作

package com.mylibs.volley_disklrucache_lrucache.customview;

import android.graphics.Bitmap;
import android.widget.ImageView;

import com.android.volley.VolleyError;
import com.android.volley.toolbox.ImageLoader;
import com.mylibs.volley_disklrucache_lrucache.utils.ImageCacheUtil;
import com.mylibs.volley_disklrucache_lrucache.utils.VolleyRequestQueue;

/**
 * Created by lizhongquan on 15-11-23.
 * ImageCacheManager
 */
public class ImageCacheManager {

    private static String TAG = ImageCacheManager.class.getSimpleName();

    // get ImageCache
    private static ImageLoader.ImageCache mImageCache = new ImageCacheUtil();

    // get ImageLoader
    public static ImageLoader mImageLoader = new ImageLoader(
            VolleyRequestQueue.mRequestQueue, mImageCache);

    /**
     * ImageListener
     *
     * @param imageView    ImageView
     * @param defaultImage defaultImage
     * @param errorImage   errorImage
     * @return new ImageLoader.ImageListener
     */
    public static ImageLoader.ImageListener getImageListener(final ImageView imageView,
                                                             final Bitmap defaultImage,
                                                             final Bitmap errorImage) {

        return new ImageLoader.ImageListener() {

            @Override
            public void onErrorResponse(VolleyError error) {
                // Error
                if (errorImage != null) {
                    imageView.setImageBitmap(errorImage);
                }
            }

            @Override
            public void onResponse(ImageLoader.ImageContainer response, boolean isImmediate) {
                // Success
                if (response.getBitmap() != null) {
                    imageView.setImageBitmap(response.getBitmap());
                } else if (defaultImage != null) {
                    imageView.setImageBitmap(defaultImage);
                }
            }
        };
    }

    /**
     * Method to LoadImage
     *
     * @param url          URL
     * @param imageView    ImageView
     * @param defaultImage defaultImage
     * @param errorImage   errorImage
     */
    public static void loadImage(String url, ImageView imageView, Bitmap defaultImage,
                                 Bitmap errorImage) {
        mImageLoader.get(url, ImageCacheManager.getImageListener(imageView,
                defaultImage, errorImage), 0, 0);
    }

    /**
     * Method to LoadImage
     *
     * @param url          URL
     * @param imageView    ImageView
     * @param defaultImage defaultImage
     * @param errorImage   errorImage
     * @param maxWidth     maxWidth
     * @param maxHeight    maxHeight
     */
    public static void loadImage(String url, ImageView imageView, Bitmap defaultImage,
                                 Bitmap errorImage, int maxWidth, int maxHeight) {
        mImageLoader.get(url, ImageCacheManager.getImageListener(imageView, defaultImage,
                errorImage), maxWidth, maxHeight);
    }

    /**
     * Method to getBitmapFromRes
     * It's connected to the Method loadImage
     *
     * @param context
     * @param resId
     * @return 
     */
    public static Bitmap getBitmapFromRes(Context context, int resId) {
        Resources res = context.getResources();
        return BitmapFactory.decodeResource(res, resId);
    }
}

MainActivity.java
activty_main.xml中只包含一个TextView 和一个ImageView

package com.mylibs.volley_disklrucache_lrucache.testaty;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import com.mylibs.volley_disklrucache_lrucache.R;
import com.mylibs.volley_disklrucache_lrucache.customview.ImageCacheManager;
import com.mylibs.volley_disklrucache_lrucache.customview.MyApplication;

public class MainActivity extends AppCompatActivity {

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

        TextView click = (TextView) findViewById(R.id.click);

        final ImageView imageView = (ImageView) findViewById(R.id.imageView);

        click.setText("click");
        click.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String address = "https://www.baidu.com/img/baidu_jgylogo3.gif";
                ImageCacheManager.loadImage(address,
                        imageView,
                        ImageCacheManager.getBitmapFromRes(MyApplication.getMyApplication(), R.mipmap.ic_launcher),
                        ImageCacheManager.getBitmapFromRes(MyApplication.getMyApplication(), R.mipmap.ic_launcher));
            }
        });
    }
}

AndroidStudio导出jar包

相关的代码及测试已经完毕,现在来生成一个jar包,方便以后使用

AndroidStudio下打包生成jar包有两种方式,一种是在build.gradle中编写代码导出,一种是直接在终端中导出。
网上很多推荐在build.gradle中编写导出的,但是就我而言还是比较喜欢直接在终端导出,毕竟终端自己用得多一点

首先, ./gradlew clean build 这一步之前是需要确定已经安装了gradlew的,如果没有安装的话,最好翻墙安装,不然会装很久

之后打开终端,输入:jar cvf libs_volley_lrucache_disklrucache.jar -C /home/lizhongquan/AndroidStudioWorkplace/Volley_DiskLrucache_Lrucache/app/build/intermediates/classes/release .

最后一排的路径可以直接右键/app/build/intermediates/classes/release获得

最后回车,就可以在目录下看到生成的jar包了.

上传到github

由于此次项目的版本控制工具选择了github(git.oschina.net), 所以在这里也记录一下github的相关操作
此次代码上传的位置:https://github.com/LiZHongquan2013/Libs_VolleyDiskLruCacheLruCache.git

首先,创建一个仓库

git init .
git add .
git commit -m “libs_volley_lrucache_disklrucache”
git remote add origin https://github.com/LiZHongquan2013/Libs_VolleyDiskLruCacheLruCache.git
git push origin master

以前收藏的一个有关git相关操作的博客,挺详细
http://blog.csdn.net/small_rice_/article/details/45095323

第一篇博客到这里就结束了,下一篇为三种屏幕适配

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值