麥洛原文連接配接 http://miloisbadboy.com/archives/51
麥洛開通部落格以來,有一段時間沒有更新博文了.主要是麥洛這段時間因項目開發實在太忙了.今天周六還在公司加班,苦逼程式猿都是這樣生活的.
今天在做項目的時候,有一個實作異步加載圖檔的功能,雖然比較簡單但還是記錄一下吧.因為麥洛之前實作異步加載圖檔都是使用了AsynTask這個API,繼續這個類,實作起來非常簡單也很友善.在doInBackground()方法裡實作下載下傳邏輯.具體實作如下
實作邏輯是:先從記憶體中讀取,如果記憶體中有這張圖檔,則直接使用;如果記憶體沒有再到sdcard上讀取,如果有則顯示;如果sdcard上還沒有則到網絡上讀取.記憶體中開啟緩存是參考了網上的實作.麥洛在這裡非常感謝喜歡分享的程式猿們.
public class ImageDownloader extends AsyncTask<String, Integer, Object> {
private static final String TAG = "ImageDownloader";
// 為了加快速度,在記憶體中開啟緩存(主要應用于重複圖檔較多時,或者同一個圖檔要多次被通路,比如在ListView時來復原動)
private Map<String, SoftReference<Drawable>> imageCache = new HashMap<String, SoftReference<Drawable>>();
/**
* 顯示圖檔的控件
*/
private ImageView mImageView;
public ImageDownloader(ImageView image) {
mImageView = image;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected Object doInBackground(String... params) {
// Log.i("ImageDownloader", "loading image...");
String url = params[0];
Drawable drawable = null;
try {
if (!"".equals(url) && url != null) {
String fileName = url.hashCode()+".jpg";
// 如果緩存過就從緩存中取出資料
if (imageCache.containsKey(fileName)) {
SoftReference<Drawable> softReference = imageCache.get(fileName);
drawable = softReference.get();
if (drawable != null) {
return drawable;
}
}
File dir = new File(FileConstant.IMAGE_FILE_PATH);
if (!dir.exists()) {
boolean m = dir.mkdirs();
}
File file = new File(dir, fileName);
if (file.exists() && file.length() > 0) {
Log.i(TAG, "load image from sd card");
// 如果檔案存在則直接讀取sdcard
drawable = readFromSdcard(file);
} else {
//file.createNewFile();
Log.i(TAG, "load image from network");
URL imageUrl = new URL(url);
// 寫入sdcard
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
saveImageFile(imageUrl, file);
drawable = Drawable.createFromStream(new FileInputStream(file), fileName);
}else{
//直接從流讀取
drawable = Drawable.createFromStream(imageUrl.openStream(), fileName);
}
}
if(drawable!=null){
//儲存在緩存中
imageCache.put(fileName, new SoftReference<Drawable>(drawable));
}
}
} catch (Exception e) {
e.printStackTrace();
}
return drawable;
}
/**
* save image
*/
private void saveImageFile(URL url, File file) {
FileOutputStream out = null;
InputStream in = null;
try {
file.deleteOnExit();
out = new FileOutputStream(file);
in = url.openStream();
byte[] buf = new byte[1024];
int len = -1;
while((len = in.read(buf))!=-1){
out.write(buf, 0, len);
out.flush();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(out!=null){
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(in!=null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 從sdcard中擷取圖檔
*/
private Drawable readFromSdcard(File file) throws Exception {
FileInputStream in = new FileInputStream(file);
return Drawable.createFromStream(in, file.getName());
}
@Override
protected void onPostExecute(Object result) {
super.onPostExecute(result);
Drawable drawable = (Drawable) result;
if (mImageView != null && drawable != null) {
mImageView.setBackgroundDrawable(drawable);
}
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
@Override
protected void onCancelled() {
super.onCancelled();
}
}
使用時:
ImageDownloader loader = new ImageDownloader(imageView);
loader.execute(url);
其實這樣的話,還有一些隐患的,就是說這個類實作還是有些問題的.比如每次都在imageView中設定網絡上的圖檔時,其實是沒有使用到這個類裡面的記憶體緩存的,就是imageCache
Map<String, SoftReference<Drawable>> imageCache = new HashMap<String, SoftReference<Drawable>>();
因為每次設定imageView的時候,都是new了一個ImageDownloader的對象.是以每個ImageDownloader對象裡面都是獨立的一個imageCache.
另外,AsynTask也是一個線程.而每次使用都開一個線程來load 圖檔,對線程個數沒有進行顯示,畢竟線程數目還是有限制的.
是以麥洛今天發現了這個問題,于是參考了别人的實作,使用了線程池,實作邏輯也上面的代碼一樣,先從記憶體讀取,如果沒有到sdcard讀取,如果還是沒有,則是網絡讀取;實作沒有使用AsynTask,具體代碼如下:
/**
* 異步加載圖檔,并将圖檔設定到ImageView控件中
*/
public class ImageDownloader extends AsyncTask<String, Integer, Object> {
private static final String TAG = "ImageDownloader";
// 為了加快速度,在記憶體中開啟緩存(主要應用于重複圖檔較多時,或者同一個圖檔要多次被通路,比如在ListView時來復原動)
private Map<String, SoftReference<Drawable>> imageCache = new HashMap<String, SoftReference<Drawable>>();
/**
* 顯示圖檔的控件
*/
private ImageView mImageView;
public ImageDownloader(ImageView image) {
mImageView = image;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected Object doInBackground(String... params) {
// Log.i("ImageDownloader", "loading image...");
String url = params[0];
Drawable drawable = null;
try {
if (!"".equals(url) && url != null) {
String fileName = url.hashCode()+".jpg";
// 如果緩存過就從緩存中取出資料
if (imageCache.containsKey(fileName)) {
SoftReference<Drawable> softReference = imageCache.get(fileName);
drawable = softReference.get();
if (drawable != null) {
return drawable;
}
}
File dir = new File(FileConstant.IMAGE_FILE_PATH);
if (!dir.exists()) {
boolean m = dir.mkdirs();
}
File file = new File(dir, fileName);
if (file.exists() && file.length() > 0) {
Log.i(TAG, "load image from sd card");
// 如果檔案存在則直接讀取sdcard
drawable = readFromSdcard(file);
} else {
//file.createNewFile();
Log.i(TAG, "load image from network");
URL imageUrl = new URL(url);
// 寫入sdcard
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
saveImageFile(imageUrl, file);
drawable = Drawable.createFromStream(new FileInputStream(file), fileName);
}else{
//直接從流讀取
drawable = Drawable.createFromStream(imageUrl.openStream(), fileName);
}
}
if(drawable!=null){
//儲存在緩存中
imageCache.put(fileName, new SoftReference<Drawable>(drawable));
}
}
} catch (Exception e) {
e.printStackTrace();
}
return drawable;
}
/**
* save image
*/
private void saveImageFile(URL url, File file) {
FileOutputStream out = null;
InputStream in = null;
try {
file.deleteOnExit();
out = new FileOutputStream(file);
in = url.openStream();
byte[] buf = new byte[1024];
int len = -1;
while((len = in.read(buf))!=-1){
out.write(buf, 0, len);
out.flush();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(out!=null){
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(in!=null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 從sdcard中擷取圖檔
*/
private Drawable readFromSdcard(File file) throws Exception {
FileInputStream in = new FileInputStream(file);
return Drawable.createFromStream(in, file.getName());
}
@Override
protected void onPostExecute(Object result) {
super.onPostExecute(result);
Drawable drawable = (Drawable) result;
if (mImageView != null && drawable != null) {
mImageView.setBackgroundDrawable(drawable);
}
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
@Override
protected void onCancelled() {
super.onCancelled();
}
}
這個ImageDownloader2的使用也很簡單
public class ImageUtil {
/**
* image loader
*/
static ImageDownloader2 loader = null;
/**
* load image
*/
public static void loadImage(String url,final ImageView imageView){
if(loader == null){
loader = new ImageDownloader2();
}
loader.loadDrawable(url, new ImageCallback() {
@Override
public void imageLoaded(Drawable imageDrawable) {
if(imageDrawable!=null){
imageView.setBackgroundDrawable(imageDrawable);
}
}
});
}
}
每次在使用是需要調用ImageUtil.loadImage(url,imageView)将圖檔url已經需要顯示圖檔的控件ImageView的引用傳入就可以了.