Android圖片的異步加載,主要原理: java
加載圖片時先查看緩存中時候存在該圖片,若是存在則返回該圖片,不然先加載載一個默認的佔位圖片,同時建立一個經過網絡獲取圖片的任務並添加,任務完成後放鬆消息給主線程更新界面。 android
廢話少說,先貼上代碼: 數組
package com.wangge.uumao.http;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.widget.ImageView;
import com.wangge.uumao.util.PicUtil;
public class AsynImageLoader {
private static final String TAG = "AsynImageLoader";
// 緩存下載過的圖片的Map
private Map<String, SoftReference<Bitmap>> caches;
// 任務隊列
private List<Task> taskQueue;
private boolean isRunning = false;
public AsynImageLoader(){
// 初始化變量
caches = new HashMap<String, SoftReference<Bitmap>>();
taskQueue = new ArrayList<AsynImageLoader.Task>();
// 啓動圖片下載線程
isRunning = true;
new Thread(runnable).start();
}
/**
*
* @param imageView 須要延遲加載圖片的對象
* @param url 圖片的URL地址
* @param resId 圖片加載過程當中顯示的圖片資源
*/
public void showImageAsyn(ImageView imageView, String url, int resId){
imageView.setTag(url);
Bitmap bitmap = loadImageAsyn(url, getImageCallback(imageView, resId));
if(bitmap == null){
imageView.setImageResource(resId);
}else{
imageView.setImageBitmap(bitmap);
}
}
public Bitmap loadImageAsyn(String path, ImageCallback callback){
// 判斷緩存中是否已經存在該圖片
if(caches.containsKey(path)){
// 取出軟引用
SoftReference<Bitmap> rf = caches.get(path);
// 經過軟引用,獲取圖片
Bitmap bitmap = rf.get();
// 若是該圖片已經被釋放,則將該path對應的鍵從Map中移除掉
if(bitmap == null){
caches.remove(path);
}else{
// 若是圖片未被釋放,直接返回該圖片
Log.i(TAG, "return image in cache" + path);
return bitmap;
}
}else{
// 若是緩存中不常在該圖片,則建立圖片下載任務
Task task = new Task();
task.path = path;
task.callback = callback;
Log.i(TAG, "new Task ," + path);
if(!taskQueue.contains(task)){
taskQueue.add(task);
// 喚醒任務下載隊列
synchronized (runnable) {
runnable.notify();
}
}
}
// 緩存中沒有圖片則返回null
return null;
}
/**
*
* @param imageView
* @param resId 圖片加載完成前顯示的圖片資源ID
* @return
*/
private ImageCallback getImageCallback(final ImageView imageView, final int resId){
return new ImageCallback() {
@Override
public void loadImage(String path, Bitmap bitmap) {
if(path.equals(imageView.getTag().toString())){
imageView.setImageBitmap(bitmap);
}else{
imageView.setImageResource(resId);
}
}
};
}
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
// 子線程中返回的下載完成的任務
Task task = (Task)msg.obj;
// 調用callback對象的loadImage方法,並將圖片路徑和圖片回傳給adapter
task.callback.loadImage(task.path, task.bitmap);
}
};
private Runnable runnable = new Runnable() {
@Override
public void run() {
while(isRunning){
// 當隊列中還有未處理的任務時,執行下載任務
while(taskQueue.size() > 0){
// 獲取第一個任務,並將之從任務隊列中刪除
Task task = taskQueue.remove(0);
// 將下載的圖片添加到緩存
task.bitmap = PicUtil.getbitmap(task.path);
caches.put(task.path, new SoftReference<Bitmap>(task.bitmap));
if(handler != null){
// 建立消息對象,並將完成的任務添加到消息對象中
Message msg = handler.obtainMessage();
msg.obj = task;
// 發送消息回主線程
handler.sendMessage(msg);
}
}
//若是隊列爲空,則令線程等待
synchronized (this) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
};
//回調接口
public interface ImageCallback{
void loadImage(String path, Bitmap bitmap);
}
class Task{
// 下載任務的下載路徑
String path;
// 下載的圖片
Bitmap bitmap;
// 回調對象
ImageCallback callback;
@Override
public boolean equals(Object o) {
Task task = (Task)o;
return task.path.equals(path);
}
}
} 緩存
源代碼的17-24行是一些變量,數組的聲明。 網絡
private static final String TAG = "AsynImageLoader";
// 緩存下載過的圖片的Map
private Map<String, SoftReference<Bitmap>> caches;
// 任務隊列
private List<Task> taskQueue;
private boolean isRunning = false; 異步
值得一提的是的這裏的下載過的圖片的mAp是軟應用了,爲何了。咱們查閱資料之後得知:
若是一個對象只具備軟引用,那就相似於可有可物的 生活用品。若是內存空間足夠,垃圾回收器就不會回收它,若是內存空間不足了,就會回收這些對象的內存。只 要垃圾回收器沒有回收它,該對象就能夠被程序使用。軟引用可用來實現內存敏感的高速緩存。 軟引用能夠和一個引用隊列(ReferenceQueue)聯 合使用,若是軟引用所引用的對象被垃圾回收,Java虛擬機就會把這個軟引用加入到與之關聯的引用隊列中。 ide
因爲圖片是大對象了,確保沒有足夠的緩存之後,java虛擬機可以將其回收。 this
代碼的25-33行作了一件事,來初始化變量。 url
public AsynImageLoader(){ 而且啓動圖片下載線程。
40行-50行主要是用來啓動異步線程來展現圖片。 .net
public void showImageAsyn(ImageView imageView, String url, int resId){
imageView.setTag(url);
Bitmap bitmap = loadImageAsyn(url, getImageCallback(imageView, resId));
if(bitmap == null){
imageView.setImageResource(resId);
}else{
imageView.setImageBitmap(bitmap);
}
}
這個方法,主要調用了本段源代碼,最核心的方法loadImageAsyn方法。
public Bitmap loadImageAsyn(String path, ImageCallback callback){
// 判斷緩存中是否已經存在該圖片
if(caches.containsKey(path)){
// 取出軟引用
SoftReference<Bitmap> rf = caches.get(path);
// 經過軟引用,獲取圖片
Bitmap bitmap = rf.get();
// 若是該圖片已經被釋放,則將該path對應的鍵從Map中移除掉
if(bitmap == null){
caches.remove(path);
}else{
// 若是圖片未被釋放,直接返回該圖片
Log.i(TAG, "return image in cache" + path);
return bitmap;
}
}else{
// 若是緩存中不常在該圖片,則建立圖片下載任務
Task task = new Task();
task.path = path;
task.callback = callback;
Log.i(TAG, "new Task ," + path);
if(!taskQueue.contains(task)){
taskQueue.add(task);
// 喚醒任務下載隊列
synchronized (runnable) {
runnable.notify();
}
}
}
// 緩存中沒有圖片則返回null
return null;
}
首先判斷緩存池中是否包含了這個圖片,若是有這個圖片,就從緩存中取得這個圖片,不然的話,就從網絡段來請求。這就是本方法的做用。
而runnable是從網絡直接下載圖片的方法。
private Runnable runnable = new Runnable() {
上述的代碼告訴咱們這樣一個意思,經過一個消息的隊列來下載圖片,若是沒有此線程的話,就令線程等待。
這就實現了android異步加載圖片的應用。