这次的内容又是很简单 异步加载图片大家可能做的也很多了 通过阅读osc的源码 也算是 学习人家的一种架构吧,好歹作者的经验也比我丰富。
其实osc的图片异步加载也是使用了两级结构,即 内存缓存和sd卡缓存。整体思路:显示图片时先从内存缓存中找,如果找到,就直接返回这个bitmap并显示,如果找不到从sd卡找,找到就显示,找不到就从网路获取并保存到内存缓存和sd卡中
时序图如下
1.在Adapter等地方设置这个显示这个图片,其实是调用了BitmapManager的loadBitmap函数
|
bmpManager.loadBitmap(imgSmall, listItemView.image, BitmapFactory.decodeResource(context.getResources(), R.drawable.image_loading));
|
2.loadBitmap函数代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public void loadBitmap(String url, ImageView imageView, Bitmap defaultBmp, int width, int height) {
imageViews.put(imageView, url);
Bitmap bitmap = getBitmapFromCache(url);
if (bitmap != null) {
//显示缓存图片
imageView.setImageBitmap(bitmap);
} else {
//加载SD卡中的图片缓存
String filename = FileUtils.getFileName(url);
String filepath = imageView.getContext().getFilesDir() + File.separator + filename;
File file = new File(filepath);
if(file.exists()){
//显示SD卡中的图片缓存
Bitmap bmp = ImageUtils.getBitmap(imageView.getContext(), filename);
imageView.setImageBitmap(bmp);
}else{
//线程加载网络图片
imageView.setImageBitmap(defaultBmp);
queueJob(url, imageView, width, height);
}
}
}
|
这里先分析从内存Cache中获取Bitmap的实现:
在分析获取缓存之前有个imageViews成员变量需要解释一下
|
imageViews.put(imageView, url);
|
这个 imageViews.put(imageView, url); 的定义是
|
Map<ImageView, String>
imageViews = Collections.synchronizedMap(new WeakHashMap<ImageView, String>());
|
这里的Collections.synchronizedMap 是为了让HashMap可以用于多线程的环境
而 WeakHashMap当某个键不再正常使用时,将自动移除其条目。这个imageViews是为了在线程池中把imageview和url(其实就是从网络取回来的bitmap)一一对应 稍后还会看到。
4.getBitmpaFromCache函数
|
public Bitmap getBitmapFromCache(String url) {
Bitmap bitmap = null;
if (cache.containsKey(url)) {
bitmap = cache.get(url).get();
}
return bitmap;
}
|
那么,这个Cache是个什么东西呢?
|
private static HashMap<String, SoftReference<Bitmap>> cache;
cache = new HashMap<String, SoftReference<Bitmap>>();
|
原来是一个HashMap 其中他的键值String是那个需要显示的Bitmap的url 我以前做的项目是把这个url md5了 市面上很多应用比如京东也是进行的md5再放入缓存
至于SofrReference请自行google。
继续看代码,。如果从缓存中取到了bitmap,就直接显示,若没有就执行
|
Bitmap bmp = ImageUtils.getBitmap(imageView.getContext(), filename);
|
从sd卡取,如果sd卡没有这个文件,那直接从网路下载了 在此之前 还让imageview显示了一个默认图片
7.queueJob()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
public void queueJob(final String url, final ImageView imageView, final int width, final int height) {
/* Create handler in UI thread. */
final Handler handler = new Handler() {
public void handleMessage(Message msg) {
String tag = imageViews.get(imageView);
if (tag != null && tag.equals(url)) {
if (msg.obj != null) {
imageView.setImageBitmap((Bitmap) msg.obj);
try {
//向SD卡中写入图片缓存
ImageUtils.saveImage(imageView.getContext(), FileUtils.getFileName(url), (Bitmap) msg.obj);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
};
pool.execute(new Runnable() {
public void run() {
Message message = Message.obtain();
message.obj = downloadBitmap(url, width, height);
handler.sendMessage(message);
}
});
}
|
这里又是一个handler+thread了
有同学会说,这里木有thread,其实那个post就是一个线程池了
看定义
|
private static ExecutorService pool;
pool = Executors.newFixedThreadPool(5); //固定线程池
|
就是把downloadBitmap的任务放到线程池中执行了,等执行完了就发消息给handler
handler会使用从imageviews中保存的url进行匹配,如果消息写到的url是imageview需要显示的那个,就显示到imageview上
注意在从网路download取回bitmap之后会把bitmap放到cache'中 这句话在downloadBitmap()函数中
cache.put(url, new SoftReference(bitmap));
13.最后异步就是把获取到的bitmap保存到sd卡了
|
ImageUtils.saveImage(imageView.getContext(), FileUtils.getFileName(url), (Bitmap) msg.obj);
//向SD卡中写入图片缓存
|