您的位置:首页 > 移动开发 > Android开发

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

2015-11-23 14:24 405 查看
  想了半天都没想出来写什么作为第一篇博客比较好,突然发现这次项目的版本控制是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);
}
}


       


对于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相关操作的博客,挺详细

/article/8054586.html

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

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: