三级缓存 Volley /Lrucache/DiskLrucache + AndroidStudio导出jar包 + upload to github
2015-11-23 14:24
405 查看
想了半天都没想出来写什么作为第一篇博客比较好,突然发现这次项目的版本控制是github,三级缓存和屏幕适配又是一个跑不掉的话题,就以这些话题开始我的博客之旅吧,同时总结一下在AndroidStudio中如何导出jar包。
加载网络图片时,使用三级缓存可以有效的提高用户体验以及程序的流畅度。
三级缓存为以下内容:网络层/内存层/磁盘层
缓存的流程如下:
1. 网络层下载图片 Volley->imageLoader
2. 存入内存层 LruCache
3. 存入磁盘层 DiskLruCache
而当以上操作均完成以后,此次下载的图片就写入到了手机中,下次运行程序时,就会优先到目录中查找是否存在该图片,如果存在就直接使用,免除了多次加载耗费流量。
首先,在使用之前,包含volley的库以及DiskLrucache, 链接如下
volley
DiskLrucache
1. 定义MyApplication, 在其中获取到context,避免每次都需要单独获取一次上下文对象。
在MyApplication中设置有一个静态的TAG,这个TAG是为了获取基础类的名称,以此作为每一个加入RequestQueue的request的标志
然后在AndroidManifest文件中声明:
同样的,为了避免重复编写,将各种工具类进行单独的封装
在Volley中,存在一个RequestQueue就可以了,其他所有的请求都直接放在这个队列中
对于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的工具类:
之后,开始准备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认为当应用程序有版本更新的时候,所有数据都应从网上重新获取。
图片缓存管理类ImageCacheManager
向外部提供一个loadImage的重载方法,一个传入加载图片的宽高,一个默认加载原图,使外部不再需要关注任何关于缓存的操作
MainActivity.java
activty_main.xml中只包含一个TextView 和一个ImageView
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包了.
此次代码上传的位置: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
([参考郭霖的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
第一篇博客到这里就结束了,下一篇为三种屏幕适配
相关文章推荐
- Android中Canvas绘图基础详解(附源码下载)
- android增删改查sqlite数据库
- <学习笔记>FloatingActionsMenu悬浮按钮菜单+ArcGIS Android API地图类型切换
- Android 输入法汇总
- Android应用启动优化:一种DelayLoad的实现和原理
- Android使用fitsSystemWindows属性实现–状态栏【status_bar】各版本适配方案
- Activity的生命周期(转载)
- AndroidManifest.xml权限设置
- Android studio 使用问题汇总
- Android屏幕适配初探
- Android Studio 1.5 RC1搭建NDK开发环境
- Android AVD 出现No content provider found for permission revoke可能的一种解决方案
- Android,activity托管fragment的方式
- Android开发进阶系列(一) 序言
- Android Handler
- android进程和线程使用总结
- Android Resource Types之Style简介
- Android智能指针
- android获取屏幕宽高与获取控件宽高 方法总结
- Android性能优化之使用线程池处理异步任务