android三级缓存访问网络图片
什么是三级缓存?
第一级:内存缓存(优先从内存中加载图片,速度最快,不浪费流量)
第二级:本地缓存(其次从本地加载图片,速度快,不浪费流量)
第三级:网络缓存(最后从网络加载图片,速度慢,浪费流量)
简单原理图:
三级缓存分别的解决方案
内存缓存解决方案:
方案一:普通的hashmap<String ,Bitmap>。由于android默认给每个app只分配16M的内存。非常容易oom。强烈不建议!!!
方案二:软引用或者弱引用包装的Bitmap(SoftReference<Bitmap> softReference = mMemoryCache.get(url))
咋一看是不是就完美解决了呢?在api level 9之前确实是完美的。但是注意,android2.3以后,系统会优先将SoftReference,WeakReference回收,即使内存足够用。
- java中的引用
- 强引用 垃圾回收器不会回收,java默认引用都是强引用
- 软引用 SoftReference 在内存不够时,会考虑回收
- 弱引用 WeakReference 在内存不够时,会优先回收
- 虚引用 PhantomReference 在内存不够时,最优先回收
方案三:lruCache(Lru: least recentlly used 最近最少使用算法)可以将最近最少使用的对象回收掉, 从而保证内存不会超出范围。
本地缓存解决方案:
通过把图片写到sd卡解决,没什么可讲的。
网络缓存解决方案:
利用AsyncTask来解决这个问题,其实AsyncTask就是线程池加上handler,实现异步加载任务和主界面的ui任务更新(线程池和handler以后有机会再详细讲)。
AsyncTask只有两个核心方法doInBackground(Object...params)和onPostExecute(Bitmap result)
doInBackgound(Object...params)是运行在子线程,所以可以直接进行异步任务加载,就是访问网络加载图片,在这里面参数是ImageView对象和图片url
onPostExecute(Bitmap result)是运行在主线程(查看源码得知该方法运行在finish()方法,而finish()是运行在handler里面),所以可以直接更新UI,参数是返回的bitmap对象。
三级缓存代码:
内存缓存代码:
public class MemoryCacheUtil {
private LruCache<String, Bitmap> mMemoryCache;
public MemoryCacheUtil() {
// LruCache 可以将最近最少使用的对象回收掉, 从而保证内存不会超出范围
// Lru: least recentlly used 最近最少使用算法
long maxMemory = Runtime.getRuntime().maxMemory();// 获取分配给app的内存大小
System.out.println("maxMemory:" + maxMemory);
mMemoryCache = new LruCache<String, Bitmap>((int) (maxMemory / 8)) {
// 返回每个对象的大小
@Override
protected int sizeOf(String key, Bitmap value) {
// int byteCount = value.getByteCount();
int byteCount = value.getRowBytes() * value.getHeight();// 计算图片大小:每行字节数*高度
return byteCount;
}
};
}
/**
* 写缓存
*/
public void setMemoryCache(String url, Bitmap bitmap) {
// mMemoryCache.put(url, bitmap);
// SoftReference<Bitmap> soft = new SoftReference<Bitmap>(bitmap);//
// 使用软引用将bitmap包装起来
// mMemoryCache.put(url, soft);
mMemoryCache.put(url, bitmap);
}
/**
* 读缓存
*/
public Bitmap getMemoryCache(String url) {
// SoftReference<Bitmap> softReference = mMemoryCache.get(url);
//
// if (softReference != null) {
// Bitmap bitmap = softReference.get();
// return bitmap;
// }
return mMemoryCache.get(url);
}
}
本地缓存代码:
public class LocalCacheUtil {
private static final String LOCAL_CACHE_PATH=
Environment.getExternalStorageDirectory().getAbsolutePath()+"/lry_cache";
public void setLocalCache(String url, Bitmap bitmap){
File dir=new File(LOCAL_CACHE_PATH);
if(!dir.exists()||!dir.isDirectory()){
dir.mkdirs();
}
try {
//因为url里面可能包含特殊字符,所以可能创建不了文件
String fileName=MD5Encoder.encode(url);
//创建一个在文件夹dir下的文件
File cacheFile=new File(dir,fileName);
//写到本地 格式,质量(0-100),输出流
bitmap.compress(Bitmap.CompressFormat.JPEG,100,new FileOutputStream(cacheFile));
} catch (Exception e) {
e.printStackTrace();
}
}
public Bitmap getLocalCache(String url){
try {
File cacheFile=new File(LOCAL_CACHE_PATH,MD5Encoder.encode(url));
if(cacheFile.exists()){
return BitmapFactory.decodeStream(new FileInputStream(cacheFile));
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
网络缓存代码:
public class NetCacheUtil {
private LocalCacheUtil localCacheUtil;
private MemoryCacheUtil memoryCacheUtil;
public NetCacheUtil(LocalCacheUtil localCacheUtil,MemoryCacheUtil memoryCacheUtil) {
this.localCacheUtil=localCacheUtil;
this.memoryCacheUtil=memoryCacheUtil;
}
public void getBitmapFromNet(ImageView imageView, String url){
//AsyncTask 异步封装的工具 实现异步请求和主界面的更新(线程池+handler)
new BitmapTask().execute(imageView,url);
}
/**
* 三个泛型的意义
* 第一个泛型代表的是doInBackground(Object...params)里面的参数,是请求的参数
* 第二个泛型的意义是onProgressUpdate()里的参数类型,更新进度用的
* 第三个泛型的意义是onPostExecute()里的参数类型以及doInBackground的返回值类型,是请求的结果
*/
class BitmapTask extends AsyncTask<Object,Integer,Bitmap>{
private ImageView imageView;
private String url;
/**
* 预加载,在异步请求之前做一些事情,比如弹出dialog,运行在主线程
*/
@Override
protected void onPreExecute() {
super.onPreExecute();
}
/**
* 正在加载,运行在子线程,可以直接异步请求
* @param params
* @return
*/
@Override
protected Bitmap doInBackground(Object... params) {
imageView = (ImageView) params[0];
url = (String) params[1];
imageView.setTag(url);
//根据图片url异步下载网络图片设置在imageview上面
Bitmap bitmap = download(url);
// publishProgress(values);如果自己一个一个字节得下载图片,可以获取下载进度并且调用此方法更新进度(回调onProgressUpdate)
return bitmap;
}
/**
* 更新进度,运行在主线程,可以直接更新UI,该方法在handler里面调用的
* @param values
*/
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
/**
* 加载结束,运行在主线程,可以直接更新UI,看源码知该方法是在finish()里面调用,而finish()是在handler调用的
* 所以可以直接更新UI
* @param result
*/
@Override
protected void onPostExecute(Bitmap result) {
if(result!=null){
String tagUrl=(String)imageView.getTag();
if(url.equals(tagUrl)){
imageView.setImageBitmap(result);
//写本地缓存
localCacheUtil.setLocalCache(url,result);
}
}
super.onPostExecute(result);
}
}
/**
* 下载图片
* @param url 图片地址
* @return itmapB对象
*/
private Bitmap download(String url) {
HttpURLConnection conn=null;
try {
conn= (HttpURLConnection) new URL(url).openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);//连接超时 5s没有连接上服务器
conn.setReadTimeout(5000);//读取超时 5s未读取到数据
conn.connect();
int responseCode=conn.getResponseCode();
if(responseCode==200){
InputStream is=conn.getInputStream();
Bitmap bitmap= BitmapFactory.decodeStream(is);
return bitmap;
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(conn!=null){
conn.disconnect();
}
}
return null;
}
}
核心:
public class MyBitmapUtil {
private NetCacheUtil netCacheUtil;
private LocalCacheUtil localCacheUtil;
private MemoryCacheUtil memoryCacheUtil;
public MyBitmapUtil(){
localCacheUtil=new LocalCacheUtil();
memoryCacheUtil=new MemoryCacheUtil();
netCacheUtil=new NetCacheUtil(localCacheUtil,memoryCacheUtil);
}
/**
*
* @param imageView ImageView
* @param url 网络图片地址
*/
public void display(ImageView imageView,String url){
display(imageView,url,null);
}
/**
*
* @param imageView
* @param url
* @param bitmap 设置默认图片
*/
public void display(ImageView imageView, String url, Bitmap bitmap){
if(bitmap!=null){
imageView.setImageBitmap(bitmap);//设置默认图片
}
//三级缓存
//内存缓存 优先从内存中加载图片, 速度最快, 不浪费流量
Bitmap memoryBitmap=memoryCacheUtil.getMemoryCache(url);
if(memoryBitmap!=null){
imageView.setImageBitmap(memoryBitmap);
Log.i("TAG","从内存读取");
return;
}
//本地缓存 其次从本地(sdcard)加载图片, 速度快, 不浪费流量
Bitmap localBitmap=localCacheUtil.getLocalCache(url);
if(localBitmap!=null){
imageView.setImageBitmap(localBitmap);
//写内存缓存
memoryCacheUtil.setMemoryCache(url,localBitmap);
Log.i("TAG","从本地读取");
return;
}
//网络缓存 最后从网络下载图片, 速度慢, 浪费流量
netCacheUtil.getBitmapFromNet(imageView,url);
Log.i("TAG","从网络读取");
}
}
防止url带特殊符号建不了文件的md5工具类
public class MD5Encoder {
public static String encode(String string) throws Exception {
byte[] hash = MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8"));
StringBuilder hex = new StringBuilder(hash.length * 2);
for (byte b : hash) {
if ((b & 0xFF) < 0x10) {
hex.append("0");
}
hex.append(Integer.toHexString(b & 0xFF));
}
return hex.toString();
}
}
如何使用样例代码:
public class MyBitmapActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_bitmap);
Bitmap default_bitmap= BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher);
ImageView imageview= (ImageView) findViewById(R.id.imageview);
new MyBitmapUtil().display(imageview,"http://7mno4h.com2.z0.glb.qiniucdn.com/5608eb8cN9b9a0a39.jpg",default_bitmap);
}
}
以上就是本人对于三级网络缓存的全部理解,如有不当的地方,还请多多指教!qq:953506233