在開發Android用戶端應用時,經常要與Server端進行通訊,最常用的也是最麻煩的就是 通過網路載入各種圖片資源。由於與PC相比,手機的記憶體實在少的可憐。同時,為了提手機內同時啟動並執行任務數,Android在系統 內對虛擬機器是有最高記憶體限制的,預設在4.0上APP最高記憶體佔用為48MB,在3.0以前最高位32MB。以上就是在開發Android用戶端經常要面對的兩個最主要的問題,下面一一進行解決。
一、網路載入技術
想要快速的實現網路載入,可以從2各方面入手,一個是與Server端的通訊協議;第二個就是提高自身載入速度。
首先,與Server端的通訊協議,可以採用分段載入的方式,就是在載入過程中,分為兩個階段,一個是載入整體資料的階段,另一個是載入具體資源階段。我們在瀏覽網頁時經常就是通過這種方式提高訪問速度,提升訪問體驗的。具體 就是首先擷取整個頁面的文字內容,擷取各個圖片資源的大小資訊,進行頁面的布局排版。之後根據具體瀏覽的位置,再單獨發送請求,擷取具體的圖片資源,從而實現頁面的快速顯示,同時節省網路流量。為了實現這種訪問方式,在用戶端通過Bitmap載入網狀圖片時,可以通過2段訪問的方式,及先擷取Bitmap大小,需要時再去擷取真正資源,從而大大提升頁面載入速度。
private BitmapFactory.Options mOption; mOption = new BitmapFactory.Options();mOption.inSampleSize = 1; //只進行大小判斷 mOption.inJustDecodeBounds = true; BitmapFactory.decodeStream(new URL(imageUrl).openStream(),null,mOption); if(mOption.outHeight > 210){ mOption.inSampleSize = 4; } mOption.inJustDecodeBounds = false; Bitmap drawable = BitmapFactory.decodeStream(new URL(imageUrl) .openStream(),null,mOption);
其次,就是用戶端在訪問Server端時,採用多線程訪問的技術,同步發起資源請求,可以大大提高資源訪問及申請的速度。這裡比較常用的手段是採用線程池來管理資源的請求與管理。線程池擁有自己的執行隊列,自動將執行請求進行隊列緩衝,按順序執行請求。線程池可以有效避免手動建立線程的線程開銷,線程池自動維護執行線程,在有執行請求時,不會銷毀執行線程。
private ExecutorService executorService; executorService = new ThreadPoolExecutor(CORE_THREAD_SIZE, MAX_THREAD_SIZE, 120, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardOldestPolicy());executorService.submit(new Runnable() {public void run() {} }
二、網狀圖片資源載入及記憶體管理
在進行用戶端開發時,經常要面對的就是網路載入大量的圖片,而每一張圖片又是即時載入,需要手動釋放資源,否則在載入新圖片時經常出現OOM(Out of Memory)問題。同時為了介面顯示瀏覽的流暢性,又需要在一定程度上、記憶體允許範圍內的緩衝請求。如何在平衡這兩方面的問題,避免出現OOM,提高APP的穩定性那。這裡提兩個簡單有效、實用的方式。
首先,根據具體的資源大小分別進行管理,這是在緩衝、載入中需要處理的第一個問題,需要根據具體的需求,哪些資源對緩衝的需求更強烈,那些資源對釋放更迫切。使用不同的資源訪問、管理方式。
接下來就根據資源的簡單分類分別進行訪問、管理;對於釋放要求更迫切的資源來說,我們可以採用二級緩衝、同步釋放的方式。簡單來說就是在訪問網路資源擷取後,複製到內部固定緩衝空間,同步釋放網路載入資源。
private static Bitmap mBigBitmap[] = new Bitmap[HorizontalViewer.MAX_PREVIEW_PICS]; static{ for(int i=0;i<HorizontalViewer.MAX_PREVIEW_PICS;i++){// mDrawBitmap[i] = Bitmap.createBitmap(IMAGE_WIDTH, IMAGE_HEIGHT, Config.ARGB_8888); mBigBitmap[i] = Bitmap.createBitmap(BIG_IMAGE_WIDTH, BIG_IMAGE_HEIGHT, Config.ARGB_8888); } }Bitmap drawable = BitmapFactory.decodeStream(new URL(imageUrl) .openStream(),null,mOption); mCanvas = new Canvas(mBigBitmap[inx]); mCanvas.drawBitmapdrawable new Rect(0,0,imageDrawable.getWidth(),imageDrawable.getHeight()), new Rect(0,0,BIG_IMAGE_WIDTH,BIG_IMAGE_HEIGHT), null);if (!drawable.isRecycled()) { drawable.recycle(); }
通過Canvas實現對圖片資源的快速複製,由於在複製過程中,Bitmap底層直接採用記憶體拷貝的方式進行,在效率上非常高,通過固定使用static的Bitmap緩衝池,使資源佔用在一個定值內,網路非同步載入的資源,在拷貝完後,迅速釋放,避免記憶體的過高佔用。
而對於緩衝需求更強烈的資源來說,在訪問過程中,可以採用弱引用的方式,進行載入與管理,在系統資源滿足要求的情況下,系統不會釋放弱引用所指向的資源,在資源緊張下,自動回收若引用資源。這樣剛好滿足我們對資源緩衝的要求。
public Map<String, WeakReference<Bitmap>> imageCache = new HashMap<String, WeakReference<Bitmap>>(); public Bitmap loadSmallDrawable(final String imageUrl, final ImageCallback callback) { /* 方案 :小圖片的緩衝比較多 */ if (imageCache.containsKey(imageUrl)) { WeakReference<Bitmap> softReference = imageCache.get(imageUrl); if (softReference.get() != null) { if (callback != null) { callback.imageLoaded(softReference.get()); } return softReference.get(); } } if (callback == null) return null; // 緩衝中沒有映像,則從網路上取出資料,並將取出的資料緩衝到記憶體中 executorService.submit(new Runnable() { public void run() { try { Bitmap drawable = BitmapFactory.decodeStream(new URL(imageUrl) .openStream(),null,mOption); drawable = Bitmap.createScaledBitmap(drawable, 120, 200, true);// drawable.recycle(); imageCache.put(imageUrl, new WeakReference<Bitmap>(drawable)); Log.e(TAG, "get a drawable"); } catch (Exception e) { throw new RuntimeException(e); } } }); return null; }
通過以上技術基本可以滿足Android用戶端訪問網路載入資源的需求,最後一點需要注意的就是對資源的訪問,盡量做到提前預知資源大小,盡量避免對圖片的資源的二次加工。簡單來說就是對網路載入的資源,盡量減少在內部的縮放、Alpha等的重新改變,主要由於對Bitmap的各種改變,系統都是重新建立資源,這樣就導致記憶體空間的浪費,及比較高的時間代價。在訪問資源時,通過改變採樣率等方式,有效避免這種情況的發生。
private BitmapFactory.Options mOption; mOption = new BitmapFactory.Options(); mOption.inSampleSize = 1; //只進行大小判斷 mOption.inJustDecodeBounds = true; BitmapFactory.decodeStream(new URL(imageUrl) .openStream(),null,mOption); if(mOption.outHeight > 210){ mOption.inSampleSize = 4; } mOption.inJustDecodeBounds = false; Bitmap drawable = BitmapFactory.decodeStream(new URL(imageUrl) .openStream(),null,mOption);
——歡迎轉載,請註明出處http://blog.csdn.net/zyplus——