1. 程式人生 > >非同步下載圖片+圖片快取

非同步下載圖片+圖片快取

程式碼參考自:jamendo,有一定修改。

功能如下:


流程如下:



 

   RemoteImageViewActivity:

public class RemoteImageViewActivity extends Activity {
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		RemoteImageView img = (RemoteImageView) findViewById(R.id.remote_img);
		img.setDefaultImage(R.drawable.ic_launcher);
		img.setImageUrl("http://img2.kwcdn.kuwo.cn:81/star/albumcover/120/7/8/83787_1323997225.jpg");
	}

	@Override
	protected void onPause() {
		// TODO Auto-generated method stub
		super.onPause();
	}
}

  ImageCache:

public class ImageCache extends WeakHashMap<String, Bitmap>{

	/**
	 * 判斷該url是否存在
	 * @param url
	 * @return
	 */
	public boolean isCached(String url){
		return containsKey(url) && get(url) != null;
	}
}

   RemoteImageApplication:

public class RemoteImageApplication extends Application {

	public static final String TAG = "RemoteImageApplication";

	private static RemoteImageApplication application;

	private ImageCache mImageCache;

	public SharedPreferences prefs = null;

	
	public static RemoteImageApplication getInstance() {
		return application;
	}

	@Override
	public void onCreate() {
		// TODO Auto-generated method stub
		super.onCreate();

		application = this;

		mImageCache = new ImageCache();

		prefs = PreferenceManager.getDefaultSharedPreferences(this);
	}

	public ImageCache getImageCache() {
		return mImageCache;
	}
}

    RemoteSettings:

public class RemoteSettings {

	public static final String CACHE_SIZE = "cache_size";  //圖片快取保留大小,如果超過該大小,即進行自動清除快取.

}

   RemoteImageView:

public class RemoteImageView extends ImageView {

	private Context mContext;

	private static int mCacheSize = 150; // 設定的快取大小。

	private static final int MAX_FAILURES = 3; // 下載的嘗試請求次數

	private int mFailure; // 下載失敗次數

	private String mUrl; // 當前下載的url

	private String mCurrentlyGrabbedUrl; // 當前下載成功的url

	private final static String JAMENDO_DIR = "Android/data/com.teleca.jamendo"; // 檔案快取存放的路徑.

	private final static long MB = 1073741824;

	public RemoteImageView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		mContext = context;
	}

	public RemoteImageView(Context context, AttributeSet attrs) {
		super(context, attrs);
		mContext = context;
	}

	public RemoteImageView(Context context) {
		super(context);
		mContext = context;
	}

	/**
	 * 設定預設圖片
	 */
	public void setDefaultImage(Integer resid) {
		setImageResource(resid);
	}

	/**
	 * 設定需要非同步載入的圖片
	 */
	public void setImageUrl(String url) {

		// 下載失敗進行重試,如果重試次數超過規定的限制,則直接返回.
		if (mUrl != null
				&& mUrl.equals(url)
				&& (mCurrentlyGrabbedUrl == null || (mCurrentlyGrabbedUrl != null && !mCurrentlyGrabbedUrl
						.equals(url)))) {
			mFailure++;
			if (mFailure > MAX_FAILURES) {
				Log.e(RemoteImageApplication.TAG, "下載該圖片地址失敗:" + url);
				return;
			}
		} else {

			mUrl = url;
			mFailure = 0;
		}

		ImageCache imageCache = RemoteImageApplication.getInstance()
				.getImageCache();

		if (imageCache.isCached(url)) {
			setImageBitmap(imageCache.get(url));
		} else {
			// 如果記憶體中沒有該快取,則從檔案中進行查詢.
			String fileName = convertUrlToFileName(url); // 進行檔名處理

			String filepath = getDirectory(fileName); // 取得快取資料夾目錄

			String pathFileName = filepath + "/" + fileName; // 組拼檔案

			File pathFile = new File(pathFileName);
			if (!pathFile.exists()) {
				try {
					pathFile.createNewFile();
				} catch (IOException e) {
					Log.d(RemoteImageApplication.TAG, "建立圖片檔案失敗:"
							+ pathFileName);
				}
			}

			Bitmap tbmp = BitmapFactory.decodeFile(pathFileName);

			if (tbmp == null) {
				Log.d(RemoteImageApplication.TAG, "圖片檔案不存在,開始進行下載");
				try {
					new DownloadTask().execute(url);
				} catch (RejectedExecutionException e) {
					Log.d(RemoteImageApplication.TAG, "下載失敗");
				}
			} else {
				Log.i(RemoteImageApplication.TAG, "從檔案中載入圖片");
				RemoteImageApplication.getInstance().getImageCache()
						.put(url, tbmp);
				this.setImageBitmap(tbmp);
			}

			updateCacheSize(pathFileName); // 進行檢測檔案大小,以便於清除快取.

		}

	}

	private void updateCacheSize(String pathFileName) {
		// TODO Auto-generated method stub
		updateSizeCache(pathFileName);

	}

	/**
	 * 檢查檔案目錄是否超過規定的快取大小
	 * 
	 * @param fileName
	 */
	private void updateSizeCache(String pathFileName) {
		// TODO Auto-generated method stub
		mCacheSize = PreferenceManager.getDefaultSharedPreferences(mContext)
				.getInt(RemoteSettings.CACHE_SIZE, 100); // 讀取設定的快取大小,前臺可以動態設定此值

		if (isSDCardEnable()) {
			String extStorageDirectory = Environment
					.getExternalStorageDirectory().toString(); // 取得SD根路徑

			String dirPath = extStorageDirectory + "/" + JAMENDO_DIR
					+ "/imagecache";

			File dirFile = new File(dirPath);

			File[] files = dirFile.listFiles();

			long dirSize = 0;

			for (File file : files) {

				dirSize += file.length();
			}

			if (dirSize > mCacheSize * MB) {
				clearCache();
			}
		}

	}

	/**
	 * 非同步下載圖片
	 * 
	 * @ClassName: DownloadTask
	 * @author 姜濤
	 * @version 1.0 2012-1-15 下午5:06:21
	 */
	class DownloadTask extends AsyncTask<String, Void, String> {

		private String mTaskUrl;
		private Bitmap mBmp = null;

		@Override
		public void onPreExecute() {
			// loadDefaultImage();
			super.onPreExecute();
		}

		@Override
		public String doInBackground(String... params) {

			mTaskUrl = params[0];
			InputStream stream = null;
			URL imageUrl;
			Bitmap bmp = null;

			try {
				imageUrl = new URL(mTaskUrl);
				try {
					stream = imageUrl.openStream();
					bmp = BitmapFactory.decodeStream(stream);
					try {
						if (bmp != null) {
							mBmp = bmp;
							RemoteImageApplication.getInstance()
									.getImageCache().put(mTaskUrl, bmp);
							Log.d(RemoteImageApplication.TAG,
									"圖片快取到application中: " + mTaskUrl);

						}
					} catch (NullPointerException e) {
						Log.w(RemoteImageApplication.TAG, "下載失敗,圖片為空:"
								+ mTaskUrl);
					}
				} catch (IOException e) {
					Log.w(RemoteImageApplication.TAG, "無法載入該url:" + mTaskUrl);
				} finally {
					try {
						if (stream != null) {
							stream.close();
						}
					} catch (IOException e) {
					}
				}

			} catch (MalformedURLException e) {
				e.printStackTrace();
			}
			return mTaskUrl;
		}

		@Override
		public void onPostExecute(String url) {
			super.onPostExecute(url);

			Bitmap bmp = RemoteImageApplication.getInstance().getImageCache()
					.get(url);
			if (bmp == null) {
				Log.w(RemoteImageApplication.TAG, "嘗試重新下載:" + url);
				RemoteImageView.this.setImageUrl(url);
			} else {

				RemoteImageView.this.setImageBitmap(bmp);
				mCurrentlyGrabbedUrl = url;
				saveBmpToSd(mBmp, url);

			}
		}

	};

	/**
	 * 把圖片儲存到本地
	 * 
	 * @param bm
	 * @param url
	 */
	private void saveBmpToSd(Bitmap bm, String url) {

		if (bm == null) {
			return;
		}

		if (mCacheSize == 0) {
			return;
		}

		String filename = convertUrlToFileName(url);
		String dir = getDirectory(filename);
		File file = new File(dir + "/" + filename);

		try {
			file.createNewFile();
			OutputStream outStream = new FileOutputStream(file);
			bm.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
			outStream.flush();
			outStream.close();

			Log.i(RemoteImageApplication.TAG, "圖片已儲存到sd卡");

		} catch (FileNotFoundException e) {
			Log.w(RemoteImageApplication.TAG, "無法找到檔案目錄");

		} catch (IOException e) {
			Log.w(RemoteImageApplication.TAG, "操作檔案出錯");
		}

	}

	/**
	 * 組拼檔名,字尾名用dat代替,避免別人使用圖片管理器搜尋出這種對於她們無用的圖片.
	 * 
	 * @param url
	 * @return
	 */
	private String convertUrlToFileName(String url) {
		String filename = url;
		filename = filename.replace("http://", "");
		filename = filename.replace("/", ".");
		filename = filename.replace(":", ".");
		filename = filename.replace("jpg", "dat");
		filename = filename.replace("png", "dat");
		return filename;
	}

	/**
	 * 返回快取圖片所存放的資料夾
	 * 
	 * @param filename
	 * @return
	 */
	private String getDirectory(String filename) {

		String extStorageDirectory = Environment.getExternalStorageDirectory()
				.toString(); // 取得SD根路徑

		String dirPath = extStorageDirectory + "/" + JAMENDO_DIR
				+ "/imagecache";

		File dirFile = new File(dirPath);

		if (!dirFile.exists()) {
			dirFile.mkdirs();
		}

		return dirPath;

	}

	/**
	 * 清除快取
	 */
	private void clearCache() {

		if (isSDCardEnable()) {
			String extStorageDirectory = Environment
					.getExternalStorageDirectory().toString(); // 取得SD根路徑

			String dirPath = extStorageDirectory + "/" + JAMENDO_DIR
					+ "/imagecache";

			File dir = new File(dirPath);

			File[] files = dir.listFiles(); // 取得該目錄下的所有檔案

			if (files == null || files.length == 0) {
				return;
			}

			for (File file : files) {
				file.delete();
			}

			Log.d(RemoteImageApplication.TAG, "已清除快取:" + dirPath);
		}
	}

	/**
	 * 判斷SD卡是否可用
	 */
	public static boolean isSDCardEnable() {

		return Environment.getExternalStorageState().equals(
				Environment.MEDIA_MOUNTED);
	}

}


 程式碼參見附件.