想了半天都没想出来写什么作为第一篇博客比较好,突然发现这次项目的版本控制是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相关操作的博客,挺详细
http://blog.csdn.net/small_rice_/article/details/45095323