图片缓存是App开发中最常见的,本篇博文给大家带来自己手写的图片缓存框,大致的思路很简单,首先从内存中获取图片,如果内存中没有,就从手机本地进行获取,如果还没有,就从网络访问进行获取。
所以,我们在ImageLoader中只需要暴露一个方法loadImage(),外部只需要调用这个方法就可以完成图片缓存的所以逻辑
//加载图片到对应的控件
public void loadImage(String key, ImageView view) {
synchronized (view) {
this.imageView = view;
//检查缓存里是否有
Bitmap bitmap = getFromCache(key);
if (bitmap != null) {
//缓存存在,直接显示
view.setImageBitmap(bitmap);
} else {
//网络进行下载
/*view.setBackgroundDrawable(drawable);*/
view.setBackgroundDrawable(new ColorDrawable(Color.GRAY));
ImageAsycTask task = new ImageAsycTask(view);
task.execute(key);
}
}
}
这里,我将从内存中和本地获取图片的逻辑都统一放在getFromCache()方法中,这里值得一提的是,当内存中没有,本地有该图片的时候,还会将这个图片放入LinkedHashMap中,让这个图片在LinkedHashMap中处于最新的位置,不至于被回收。
private Bitmap getFromCache(String key) {
//检查内存软引用
synchronized (firstHashMap) {
if (firstHashMap.get(key) != null) {
Bitmap bitmap = firstHashMap.get(key).get();
if (bitmap != null) {
//更新一下,因为Lru算法会默认清除最老的选项
firstHashMap.put(key, new SoftReference(bitmap));
return bitmap;
}
}
}
//检查磁盘
Bitmap bitmap = getFromLocal(key);
if (bitmap != null) {
//更新一下,因为Lru算法会默认清除最老的选项
firstHashMap.put(key, new SoftReference(bitmap));
return bitmap;
}
return null;
}
在内存中,我使用了一个LinkedHashMap
private static LinkedHashMap> firstHashMap = new LinkedHashMap>(MAX_LENGTH) {
@Override
protected boolean removeEldestEntry(Entry> eldest) {
if (this.size() > MAX_LENGTH) {
//返回true,表示移除最老的
return true;
}
//往磁盘进行添加
diskCache(eldest.getKey(), eldest.getValue());
return false;
}
};
这里内部的removeEldestEntry()方法内部如果返回true,会默认移除掉最旧的一个成员,返回false表示不移除,同时还会把图片放入到手机本地中,这个逻辑通过diskCache()方法实现的,这里图片在本地中名字使用md5加密后的名字
// 把图片缓存到本地磁盘
private static void diskCache(String key, SoftReference value) {
//消息摘要算法
Bitmap bitmap;
FileOutputStream os = null;
try {
String fileName = MD5Utils.md5(key, "utf-8");
String path = mContext.getCacheDir().getAbsolutePath() + File.separator + fileName;
os = new FileOutputStream(new File(path));
if (value.get() != null) {
value.get().compress(Bitmap.CompressFormat.JPEG, 80, os);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
如果本地缓存中没有,会通过getFromLocal(key)方法,从手机本地中进行获取
//检查sd卡里是否有
private Bitmap getFromLocal(String key) {
InputStream is = null;
try {
String filname = MD5Utils.md5(key, "utf-8");
if (filname == null) {
return null;
} else {
String path = mContext.getCacheDir().getAbsolutePath() + File.separator + filname;
is = new FileInputStream(new File(path));
Bitmap bitmap = BitmapFactory.decodeStream(is);
return bitmap;
}
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
如果本地和内存都没有的话,那么就从网络进行获取,这里使用了AsyncTask
class ImageAsycTask extends AsyncTask<String, Void, Bitmap> {
private ImageView imagView;
private String key;
public ImageAsycTask(ImageView imageView) {
this.imagView = imageView;
}
@Override
protected Bitmap doInBackground(String... strings) {
this.key = strings[0];
Log.i(TAG,key);
Bitmap bitmap = downLoad(key);
return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
if (bitmap != null) {
addCache(key, bitmap);
/*Log.i("11",bitmap.toString());*/
imagView.setImageBitmap(bitmap);
}
}
}
其中downLoad()方法就是访问网络获取图片的方法
private Bitmap downLoad(String key) {
final Bitmap[] bitmap = new Bitmap[1];
mHttpClient = new OkHttpClient();
Request request = new Request.Builder().url(key).build();
mHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.i("TAG", "网络访问失败了");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
InputStream is = response.body().byteStream();
bitmap[0] = BitmapFactory.decodeStream(is);
Log.d("okHttp", bitmap[0].toString());
}
});
return bitmap[0];
}
图片下载完成之后,我们会将其读写到内存中,并显示在view上,这个view是通过AsyncTask的构造函数传进来的
private void addCache(String key, Bitmap bitmap) {
if (bitmap != null) {
synchronized (firstHashMap) {
firstHashMap.put(key, new SoftReference(bitmap));
}
}
}
这样这个图片缓存框架就写好了,我们就单纯的在MainActivity中访问网络进行显示来验证我们的框架,布局太简单就不贴了
public class MainActivity extends AppCompatActivity {
String url = "http://7mno4h.com2.z0.glb.qiniucdn.com/560bd9b6Nc4b5cbfe.jpg";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView imageView = (ImageView) findViewById(R.id.image);
ImageLoader imageLoader = ImageLoader.getmInstance(this);
imageLoader.loadImage(url,imageView);
}
}