標籤:獨立 out print trace imageview cancel can ash sof
麥洛開通部落格以來,有一段時間沒有更新博文了.主要是麥洛這段時間因項目開發實在太忙了.今天周六還在公司加班,苦逼程式猿都是這樣生活的.
今天在做項目的時候,有一個實現非同步載入圖片的功能,雖然比較簡單但還是記錄一下吧.因為麥洛之前實現非同步載入圖片都是使用了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的引用傳入就可以了.
Android 實現非同步載入圖片