標籤:bitmap 非同步 多線程 圖片 android
Android非同步載入全解析之大圖處理非同步載入中非常重要的一部分就是對映像的處理,這也是我們前面用非同步載入映像做示範例子的原因。一方面是因為影像處理不好的話會非常占記憶體,而且容易OOM,另一方面,映像也比文字要大,載入比較慢。所以,在講解了如何進行多線程、AsyncTask進行多線程載入後,先暫停下後面的學習,來對映像的非同步處理進行一些最佳化工作。
為什麼要對影像處理為什麼要對映像進行處理,這是一個很直接的問題,一張映像,不管你拿手機、相機、單反還是什麼玩意拍出來,它就有一定的大小,但是在不同的終端上,終端也有不同的大小,比如一張超高請無碼大圖,10M大小,在網頁中看著挺爽,全高清,毛孔都看得清。同樣一張圖片,如果放在4.7寸的手機上,當然,同樣還是一張高清無碼大圖,但這張圖片10M,在電腦上可能不算什麼,但在手機上,已經是非常大了,而這張圖片在手機上,你拚命看,也就是那樣,即使解析度減少一半,你看上去也還是差不多。這就像所謂的視網膜屏、2k屏、4k屏,其實已經基本達到視覺分析的極限了,普通情況下,差別並不大。但是,雖然你看著區別不大,但對系統來說,差別就非常大了,手機的記憶體,要像使用你藏的私房錢一樣,每一分都要三思而用。所以,我們在下載高解析度的圖片的時候,可以對映像進行壓縮,顯示上雖然沒有太大區別,但是卻幫系統節省了大量的私房錢。
BitmapFactory之inSampleSizeBitmapFactory是Android中提供的對映像的解析方法,通過它的一些靜態方法,我們可以對映像進行解析,例如從檔案中解析——decodeFile;從資源中解析——decodeResource;從網路中解析——decodeStream等等。當我們從網路上進行映像下載的時候,看情況,是否需要對映像進行壓縮,那麼如何在系統不載入映像到記憶體之前,就擷取映像的大小等參數呢?看上去非常矛盾,但系統給我們提供了一種簡單的解決方案。BitmapFactory提供了BitmapFactory.Options參數,這個參數有一個inJustDecodeBounds屬性,當這個屬性為true的時候,我們就可以禁止系統載入映像到記憶體,但是!!!這時候,Options參數中的映像寬高、類型等屬性已經被賦值了,這樣,我們就實現了空手套白狼,哦,不對,是不使用記憶體就擷取映像的屬性。
BitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = true;BitmapFactory.decodeResource(getResources(), R.id.myimage, options);// 擷取屬性值int imageHeight = options.outHeight;int imageWidth = options.outWidth;String imageType = options.outMimeType;
在獲得了映像的參數之後,我們就可以對映像進行相應的處理了,例如我們顯示映像的ImageView只有200 X 200 像素,而我們的圖片有800 X 800 像素,那你把這麼大的一張圖放到這麼小的ImageView中,有啥用呢?白白浪費了記憶體。OK,那麼下面我們就來對映像進行壓縮,Options參數中給我們提供了這樣一個屬性——inSampleSize,這個屬性可以設定映像的縮放比例,例如一張1000 X 1000像素的映像,設定inSampleSize為5,意思就是把這個映像縮放到了五分之一,即200 X 200 。OK,下面我們就通過這樣一個方法來對映像進行最佳化,首先,我們需要建立一個方法來擷取到一個合適的inSampleSize:
/** * 擷取合適的inSampleSize * @param options * @param targetWidth 期望Width * @param targetHeight 期望Height * @return */public static int getInSampleSize(BitmapFactory.Options options, int targetWidth, int targetHeight) { // 原始圖片的高度和寬度 final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > targetHeight || width > targetWidth) { // 計算出實際寬高和目標寬高的比率 final int heightRate = Math.round((float) height / (float) targetHeight); final int widthRate = Math.round((float) width / (float) targetWidth); inSampleSize = heightRate < widthRate ? heightRate : widthRate; } return inSampleSize;}
方法非常簡單,就是通到期望長寬來擷取縮放的比例。下面我們就建立一個方法來擷取縮放後的映像,這裡為了示範,我們只建立從資源檔中擷取映像的方法:
/** * 使用targetWidth、targetHeight來擷取合適的inSampleSize * 並使用inSampleSize來縮放得到合適大小的映像 * @param res getResources() * @param resId id * @param targetWidth * @param targetHeight * @return */public static Bitmap decodeSuitableBitmap(Resources res, int resId, int targetWidth, int targetHeight) { // 空手套白狼 final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resId, options); // 計算合適的inSampleSize options.inSampleSize = getInSampleSize(options, targetWidth, targetHeight); // 載入到記憶體 options.inJustDecodeBounds = false; return BitmapFactory.decodeResource(res, resId, options);}
通過調用decodeSuitableBitmap這樣一個方法,我們就可以非常簡單的將映像進行壓縮。
我的Github
我的視訊 慕課網
Android非同步載入全解析之大圖處理