Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android高級之圖片加載框架的選擇

Android高級之圖片加載框架的選擇

編輯:關於Android編程

從Android爆發以後,自定義的控件如EditTextWithDelete、ActionBar、PullToRresh逐步進入開發者的視野,想起11年的時候,基本項目中使用的UI還全都是Android提供的基礎控件,少一點的動畫+布局,下載和網絡請求都是用HttpClient,圖片加載當然也是下載後再使用,當時的程序資源可沒有現在這麼豐富,看看Github上開源的項目,現在的程序員應該感到幸福。

項目開發從程序上來講,萬古不變兩大事情,一是網絡通信框架,二是圖片加載框架,有關網絡框架上一篇已經介紹了async-http和okhttp,而其他如volly同時擁有網絡請求和圖片加載兩個框架,很多人圖省事就一次性使用了,當然facebook自己的開源框架也是寫的非常不錯,接下來再一一介紹;先貼一張11年我們自己寫的imageloader

 

public class ImageDownloader {

	private static ImageDownloader instance = null;
	private static File cacheDir;
	public static Map> bitMapCache = new HashMap>();

	public static ImageDownloader getInstance() {
		if (instance == null) {
			instance = new ImageDownloader();
		}
		return instance;
	}

	private ImageDownloader() {
		// Find the dir to save cached images
		if (android.os.Environment.getExternalStorageState().equals(
				android.os.Environment.MEDIA_MOUNTED))
			cacheDir = new File(
					android.os.Environment.getExternalStorageDirectory(),
					"WholeMag");
		else
			cacheDir = WholeMagApplication.getInstance().getCacheDir();
		if (!cacheDir.exists())
			cacheDir.mkdirs();
	}

	public void download(String actName, String url, ImageView imageView) {
		BitmapDownloaderTask task = new BitmapDownloaderTask(imageView, actName);
		task.execute(url);
		// return task.doInBackground(url);
	}

	class BitmapDownloaderTask extends AsyncTask {
		// private String url;
		// private boolean flag;
		private final WeakReference imageViewReference; // 使用WeakReference解決內存問題
		private String actName;

		public BitmapDownloaderTask(ImageView imageView, String actName) {
			imageViewReference = new WeakReference(imageView);
			this.actName = actName;
		}

		@Override
		protected Bitmap doInBackground(String... params) { // 實際的下載線程,內部其實是concurrent線程,所以不會阻塞
			Bitmap rebmp = getLocalBitmap(params[0], actName);
			if (rebmp == null)
				rebmp = downloadBitmap(params[0], actName);
			if (rebmp == null) {
				doInBackground(params[0]);
			}
			return rebmp;
		}

		@Override
		protected void onPostExecute(Bitmap bitmap) { // 下載完後執行的
			if (isCancelled()) {
				bitmap = null;
			}
			if (imageViewReference != null) {
				ImageView imageView = imageViewReference.get();
				if (imageView != null) {
					imageView.setDrawingCacheEnabled(true);
					Bitmap temp = imageView.getDrawingCache();
					imageView.setDrawingCacheEnabled(false);
					if (temp != null) {
						temp.recycle();
					}
					double widthX = (float) WholeMagDatas.getDeviceWidth()
							/ bitmap.getWidth(); // 圖片寬度拉伸比例
					int bitmapHight = bitmap.getHeight();// 圖片高度
					imageView.setImageBitmap(bitmap); // 下載完設置imageview為剛才下載的bitmap對象
					if(actName.equals(AppData.NEWS_DETAIL_ACT)){
						FrameLayout.LayoutParams ll = new FrameLayout.LayoutParams(
								android.view.ViewGroup.LayoutParams.FILL_PARENT,
								(int) (bitmapHight * widthX), Gravity.CENTER);
						imageView.setLayoutParams(ll);
					}
					AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);// 創建一個AlphaAnimation對象
					alphaAnimation.setDuration(500);// 設置動畫執行的時間(單位:毫秒)
					imageView.startAnimation(alphaAnimation);
				}
			}
		}
	}

	static Bitmap getLocalBitmap(String url, String actName) {
		if (bitMapCache.containsKey(url)) {
			return bitMapCache.get(url).get();
		}
		// String tmp = url;
		// String first = url.substring(url.lastIndexOf("/") + 1);
		// tmp = tmp.substring(0, tmp.lastIndexOf("/"));
		// String second = tmp.substring(tmp.lastIndexOf("/") + 1);
		// tmp = tmp.substring(0, tmp.lastIndexOf("/"));
		// String third = tmp.substring(tmp.lastIndexOf("/") + 1);
		// String filename = third + second + first;
		// File f = new File(cacheDir, filename);
		File f = Tools.getFile(actName, url);
		InputStream inputStream = null;
		try {
			// decode image size
			BitmapFactory.Options o = new BitmapFactory.Options();
			o.inPreferredConfig = Bitmap.Config.RGB_565;
			o.inDither = false;
			o.inPurgeable = true;
			// o.inTempStorage = new byte[12 * 1024];
			inputStream = new FileInputStream(f);
			// Bitmap bitmap = BitmapFactory.decodeFile(f.getAbsolutePath());
			Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, o);
			bitMapCache.put(url, new SoftReference(bitmap));
			return bitmap;
		} catch (Exception e) {
		} finally {
			if (null != inputStream) {
				try {
					inputStream.close();
				} catch (Exception ex) {
				}
			}
		}
		return null;
	}

	static Bitmap downloadBitmap(String url, String actName) {
		final AndroidHttpClient client = AndroidHttpClient.newInstance("linux");
		final HttpGet getRequest = new HttpGet(url);

		try {
			HttpResponse response = client.execute(getRequest);
			final int statusCode = response.getStatusLine().getStatusCode();
			if (statusCode != HttpStatus.SC_OK) {
				// Log.e("cwjDebug", "Error " + statusCode
				// + " while retrieving bitmap from " + url);
				return null;
			}

			final HttpEntity entity = response.getEntity();
			if (entity != null) {
				// String tmp = url;
				// String first = url.substring(url.lastIndexOf("/") + 1);
				// tmp = tmp.substring(0, tmp.lastIndexOf("/"));
				// String second = tmp.substring(tmp.lastIndexOf("/") + 1);
				// tmp = tmp.substring(0, tmp.lastIndexOf("/"));
				// String third = tmp.substring(tmp.lastIndexOf("/") + 1);
				// String filename = third + second + first;
				// File f = new File(cacheDir, filename);
				File f = Tools.getFile(actName, url);
				OutputStream os = new FileOutputStream(f);
				InputStream inputStream = null;
				try {
					inputStream = entity.getContent();
					BitmapFactory.Options o = new BitmapFactory.Options();
					o.inPreferredConfig = Bitmap.Config.RGB_565;
					o.inDither = false;
					o.inPurgeable = true;
					final Bitmap bitmap = BitmapFactory.decodeStream(
							inputStream, null, o);
					bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
					bitMapCache.put(url, new SoftReference(bitmap));
					return bitmap;
				} finally {
					if (inputStream != null) {
						inputStream.close();
					}
					if (null != os) {
						os.close();
					}
					entity.consumeContent();
				}
			}
		} catch (Exception e) {
			getRequest.abort();
		} finally {
			if (client != null) {
				client.close();
			}
		}
		return null;
	}
}
當年為了防止圖片爆掉出現OOM,使用了軟引用,覺得還不錯;圖片加載的基本原理就是,把url+imageview拋過來,然後開啟異步線程加載,根據獲取的byte流decode成bitmap,最後在UI線程將圖片加載到imageview上;也沒有說做本地緩存,僅做了應用緩存;沒有對圖片進行壓縮或者設置格式,使占用內存更小,展示也更合理;LruCache基本原理跟上面使用軟引用的過程差不多,只不過多限制了圖片占用內存的大小,計算圖片使用的頻率,對應用層、SD卡層均做了封裝。

 

上面介紹完,相信你也對圖片加載有個大概的輪廓,我們拿開源的imageloader為例,來講講圖片加載框架的一些細節

 

public final class ImageLoaderConfiguration {

    final Resources resources;//主要給圖片設計寬高時,獲得屏幕寬高使用
    final int maxImageWidthForMemoryCache;//內存中最大的圖片寬度
    final int maxImageHeightForMemoryCache;//內存中最大的圖片高度
    final int maxImageWidthForDiskCache;//SD卡中最大的圖片寬度
    final int maxImageHeightForDiskCache;//SD卡中最大的圖片高度
    final BitmapProcessor processorForDiskCache;//從SD卡獲得Bitmap的加載器

    final Executor taskExecutor;//加載圖片時的執行器
    final Executor taskExecutorForCachedImages;//加載緩存時的執行器
    final boolean customExecutor;//是否使用默認執行器
    final boolean customExecutorForCachedImages;//是否使用默認緩存執行器

    final int threadPoolSize;//線程數,可以用來控制展示當前界面的item圖片
    final int threadPriority;//線程的執行優先級
    final QueueProcessingType tasksProcessingType;//是LILO還是LIFO,默認是前者,但一般喜歡後者

    final MemoryCache memoryCache;//內存緩存對象,如不寫可用默認
    final DiskCache diskCache;//SD卡緩存對象,如不寫可用默認
    final ImageDownloader downloader;//圖片加載器,根據網絡(http/s)、file、content、drawable、asset來加載
    final ImageDecoder decoder;//圖片解析器,根據獲取的圖片參數拿到Bitmap
    final DisplayImageOptions defaultDisplayImageOptions;//設置圖片加載狀態和結果,見下面源碼

    final ImageDownloader networkDeniedDownloader;//不用網絡下載圖片的下載器,可理解為加載SD卡圖片的加載器
    final ImageDownloader slowNetworkDownloader;//僅網絡下載圖片的下載器,支持斷點續傳

拿這些變量來講,基本就可以說明事情
public final class DisplayImageOptions {

    private final int imageResOnLoading;//圖片是否加載中
    private final int imageResForEmptyUri;//圖片是否來自於空url
    private final int imageResOnFail;//圖片是否加載失敗
    private final Drawable imageOnLoading;//加載中的圖片
    private final Drawable imageForEmptyUri;//空數據的圖片
    private final Drawable imageOnFail;//加載失敗的圖片
    private final boolean resetViewBeforeLoading;//加載完是否重置(意味著放棄之前的加載)
    private final boolean cacheInMemory;//是否緩存在內存中
    private final boolean cacheOnDisk;//是否緩存在SD卡中
    private final ImageScaleType imageScaleType;//要多大的圖片,統一設置
    private final Options decodingOptions;//Bitmap的options對象
    private final int delayBeforeLoading;//是否延遲加載,可用於非當前頁面圖片
    private final boolean considerExifParams;//是否支持jpeg圖片的rotate和flip等方法
    private final Object extraForDownloader;//額外數據
    private final BitmapProcessor preProcessor;//加載不在內存中的圖片
    private final BitmapProcessor postProcessor;//加載在圖片中的圖片
    private final BitmapDisplayer displayer;//展示圖片
    private final Handler handler;//這個就不用講了吧,跟主線程交互必不可少的工具
    private final boolean isSyncLoading;//是否同步加載 

 

補充一下,上面框架還支持給圖片設置像素點占位大小;看到這麼多功能,對於現在的項目基本滿足要求,因此就不打算換了,再看看其他幾種圖片加載框架的異同

fresco,facebook出品,最大的優勢在於可展示加載過程,即加載進度、加載前圖片、加載中圖片、加載後圖片、加載失敗圖片等,還可以設置圖片的形狀

picasso,加載更快,因為默認設置圖片格式占內存小

Glide是升級版本的piccaso,支持跟fragment和activity生命周期綁定

volly,基於老的imageloader又做了次封裝,差別不是太大,功能弱化一些

而後面這幾種框架,都是在imageloader興起之後出現的,所以也基本支持它,在我看來也僅有fresco和glide是真正寫出了跟原框架不同的東西。

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved