Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android中大圖片加載及快速浏覽處理

Android中大圖片加載及快速浏覽處理

編輯:關於Android編程

一個bug引出了一系列需要修改和注意的問題,讓人去思考前期的設計與相關問題的思考,對後期功能增加和問題修復的重要性。

問題描述:軟件的圖片浏覽功能中查看圖片時,發現圖片模糊,放大查看時更明顯
查找原因:分析代碼發現,在浏覽及查看圖片時顯示的是縮略圖,沒有顯示原圖;而且設計如此,但不滿足當前需求,需要修改
初步修改:將獲取圖片縮略圖的地方,替換成獲取原圖
思路簡單清晰,但忽略了此前代碼的設計,隨後導致與圖片浏覽顯示相關的其它問題:
(1)縮放比例問題:顯示原圖了,但當原圖過大時,允許的縮放比例邊界值不夠,致使在首次顯示時,圖片不能完整顯示
(2)緩存問題:內存緩存,原設計采用雙緩存機制,固定緩存3張,動態緩存采用軟引用方式;在顯示縮略圖時內存占用並不高。換成顯示原圖後,浏覽和顯示一般圖片沒有任何問題,但當浏覽大圖時(數碼相機圖片),1張圖片的內存占用就在70M,快速浏覽幾次很容易OOM
(3)適配問題:低內存手機,直接顯示原圖後,有的無法支持緩存了,甚至顯示一張大的原圖都會OOM


當然還有其它相關的問題,如快速浏覽卡頓,圖片旋轉等,需要熟悉下原設計思路,重新考慮修改的思路。在整體考慮修改思路之前,先整理下相關知識。

1.圖片內存占用
通常的誤解是圖片文件的大小,直接影響其內存占用大小。圖片在內存中占用的大小是由圖片分辨率和單位像素占用的字節數決定的。什麼是單位像素字節數,即表示一個像素所需要的字節數。在Bitmap.Config中就定義了幾種類型,
如ARGB_8888 ,就是一個像素點用4字節表示,具體為透明度用1字節表示,顏色值用3字節RGB表示。那麼一張1080*720的圖片的內存占用為1080*720*4=3110400字節,約3M;當寬高都乘5時,內存約75M
但是如果為RGB_565,即單位像素只占用2字節,內存占用縮小一半,但大圖的話仍然是個不小的數字

2.大圖OOM處理
在了解圖片的內存占用後,也就能理解強引用幾張大圖後,必然會導致OOM(out of memory);而且在內存很小的手機上,直接顯示單張大圖,都會造成OOM。因此,得根據使用的場景不同對圖片進行處理。
(1)首先單純希望通過捕獲OutOfMemoryError的做法,在Android6.0上可能不行,因為調用BitmapFactory.decodeStream時,可能在native層就報OOM了,但仍然是種防御手段。
(2)減少表示一個像素所需要的字節數:前面提到加載圖片時,默認采用ARGB_8888,如果對透明度和色值范圍要求不高的情況下,可以采用RGB_565,或者Bitmap.Config提供的其它選項,但這種減少畢竟有限;
(3)降低圖片分辨率:BitmapFactory加載圖片時,BitmapFactory.Options設置inSampleSize可以顯著減少圖片占用的內存大小,當然也是降低了圖片的分辨率。inSampleSize為2時,其實內存占用是原來的四分之一
另外為了配合計算inSampleSize的取值,可以在不加載圖片的情況下,解析出圖片的寬高信息,給個示例代碼如下:
根據屏幕大小,降低圖片分辨率
    public static Bitmap getBitmapFromFile(String imgPath, int screenWidth, int screenHeight) {
        File imgfile = new File(imgPath);
        if (!imgfile.exists()) {
            return null;
        }
        
        Bitmap bmp = null;
        FileInputStream fis = null;
        try {
            Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(new FileInputStream(imgPath), null, options);
            int sample = 1;
            int imgWidth = options.outWidth;
            int imgHeight = options.outHeight;
            while ((imgWidth / sample > screenWidth * 2) || (imgHeight / sample > screenHeight * 2)) {
                sample *= 2;
            }
            
            fis = new FileInputStream(imgPath);
            if (sample > 1) {
                options = new BitmapFactory.Options();
                options.inPreferredConfig = Config.ARGB_8888;
                options.inSampleSize = sample;
                bmp = BitmapFactory.decodeStream(fis, null, options);
            } else {
                bmp = BitmapFactory.decodeStream(fis);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (OutOfMemoryError e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (fis != null)
                    fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return bmp;
    }

3.其它問題
(1)圖片縮放、平移的矩陣變換

對Bitmap的旋轉、縮放、平移,都可以通過Matrix來實現,詳細的解讀可參考
Pro Android學習筆記(一零九):2D動畫(4):view的Matrix(http://blog.csdn.net/flowingflying/article/details/38304057)
圖片的旋轉代碼如:
Matrix matrix = new Matrix();
matrix.setRotate(90);
Bitmap rotateBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix,
  false);

(2)圖片旋轉
上面示例代碼通過Matrix使圖片旋轉,但是具體旋轉多少角度,可以通過圖片的長寬比例來計算,也可以通過
ExifInterface.TAG_ORIENTATION的值來計算,示例代碼如:
ExifInterface exif = null;
try {
    exif = new ExifInterface(filePath);
} catch (IOException e) {
    e.printStackTrace();
}
String sOrientation = exif.getAttribute(ExifInterface.TAG_ORIENTATION);
當得到ORIENTATION_ROTATE_90,將圖片旋轉90度
(3)bitmap.recycle,現在主流的4.X以上手機這樣調用沒有用處,還會出現崩潰
java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@41cb98e8

整體修改思路
在整理完一些必要的知識後,再整體思考設計,目標是可快速浏覽,在查看圖片時能顯示原圖,因此,依然保持雙緩存,都保存縮略圖;當停留在某張圖片時,才加載該圖的原圖,也就是大家常見的先顯示模糊圖,之後圖再變清晰。具體流程上是,浏覽的緩存中存儲較多的縮略圖(占用內存較小),方便快速浏覽滑動;當停留在某個圖上超過一定時間後(通過handler.removeCallbacks和handler.postDelayed控制),在線程池中加載原圖,並替換當前顯示的縮略圖
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved