新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > Android高效加載大圖、多圖解決方案,有效避免程序OOM

        Android高效加載大圖、多圖解決方案,有效避免程序OOM

        作者: 時間:2018-09-05 來源:網絡 收藏

        高效加載大圖片我們在編寫Android程序的時候經常要用到許多圖片,不同圖片總是會有不同的形狀、不同的大小,但在大多數情況下,這些圖片都會大于我們程序所需要的大小。比如說系統圖片庫里展示的圖片大都是用手機攝像頭拍出來的,這些圖片的分辨率會比我們手機屏幕的分辨率高得多。大家應該知道,我們編寫的應用程序都是有一定內存限制的,程序占用了過高的內存就容易出現OOM(OutOfMemory)異常。我們可以通過下面的代碼看出每個應用程序最高可用內存是多少。

        本文引用地址:http://www.104case.com/article/201809/388514.htm

        int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);

        Log.d(TAG, Max memory is + maxMemory + KB);

        復制代碼

        因此在展示高分辨率圖片的時候,最好先將圖片進行壓縮。壓縮后的圖片大小應該和用來展示它的控件大小相近,在一個很小的ImageView上顯示一張超大的圖片不會帶來任何視覺上的好處,但卻會占用我們相當多寶貴的內存,而且在性能上還可能會帶來負面影響。下面我們就來看一看,如何對一張大圖片進行適當的壓縮,讓它能夠以最佳大小顯示的同時,還能防止OOM的出現。

        BitmapFactory這個類提供了多個解析方法(decodeByteArray, decodeFile, decodeResource等)用于創建Bitmap對象,我們應該根據圖片的來源選擇合適的方法。比如SD卡中的圖片可以使用decodeFile方法,網絡上的圖片可以使用decodeStream方法,資源文件中的圖片可以使用decodeResource方法。這些方法會嘗試為已經構建的bitmap分配內存,這時就會很容易導致OOM出現。為此每一種解析方法都提供了一個可選的BitmapFactory.Options參數,將這個參數的inJustDecodeBounds屬性設置為true就可以讓解析方法禁止為bitmap分配內存,返回值也不再是一個Bitmap對象,而是null。雖然Bitmap是null了,但是BitmapFactory.Options的outWidth、outHeight和outMimeType屬性都會被賦值。這個技巧讓我們可以在加載圖片之前就獲取到圖片的長寬值和MIME類型,從而根據情況對圖片進行壓縮。如下代碼所示:

        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;

        復制代碼

        為了避免OOM異常,最好在解析每張圖片的時候都先檢查一下圖片的大小,除非你非常信任圖片的來源,保證這些圖片都不會超出你程序的可用內存。現在圖片的大小已經知道了,我們就可以決定是把整張圖片加載到內存中還是加載一個壓縮版的圖片到內存中。以下幾個因素是我們需要考慮的:

        預估一下加載整張圖片所需占用的內存。

        為了加載這一張圖片你所愿意提供多少內存。

        用于展示這張圖片的控件的實際大小。

        當前設備的屏幕尺寸和分辨率。

        比如,你的ImageView只有128*96像素的大小,只是為了顯示一張縮略圖,這時候把一張1024*768像素的圖片完全加載到內存中顯然是不值得的。那我們怎樣才能對圖片進行壓縮呢?通過設置BitmapFactory.Options中inSampleSize的值就可以實現。比如我們有一張2048*1536像素的圖片,將inSampleSize的值設置為4,就可以把這張圖片壓縮成512*384像素。原本加載這張圖片需要占用13M的內存,壓縮后就只需要占用0.75M了(假設圖片是ARGB_8888類型,即每個像素點占用4個字節)。下面的方法可以根據傳入的寬和高,計算出合適的inSampleSize值:

        public static int calculateInSampleSize(BitmapFactory.Options options,

        int reqWidth, int reqHeight) {

        // 源圖片的高度和寬度

        final int height = options.outHeight;

        final int width = options.outWidth;

        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {

        // 計算出實際寬高和目標寬高的比率

        final int heightRatio = Math.round((float) height / (float) reqHeight);

        final int widthRatio = Math.round((float) width / (float) reqWidth);

        // 選擇寬和高中最小的比率作為inSampleSize的值,這樣可以保證最終圖片的寬和高

        // 一定都會大于等于目標的寬和高。

        inSampleSize = heightRatio widthRatio ? heightRatio : widthRatio;

        }

        return inSampleSize;

        }

        復制代碼

        使用這個方法,首先你要將BitmapFactory.Options的inJustDecodeBounds屬性設置為true,解析一次圖片。然后將BitmapFactory.Options連同期望的寬度和高度一起傳遞到到calculateInSampleSize方法中,就可以得到合適的inSampleSize值了。之后再解析一次圖片,使用新獲取到的inSampleSize值,并把inJustDecodeBounds設置為false,就可以得到壓縮后的圖片了。

        public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,

        int reqWidth, int reqHeight) {

        // 第一次解析將inJustDecodeBounds設置為true,來獲取圖片大小

        final BitmapFactory.Options options = new BitmapFactory.Options();

        options.inJustDecodeBounds = true;

        BitmapFactory.decodeResource(res, resId, options);

        // 調用上面定義的方法計算inSampleSize值

        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

        // 使用獲取到的inSampleSize值再次解析圖片

        options.inJustDecodeBounds = false;

        return BitmapFactory.decodeResource(res, resId, options);

        }

        復制代碼

        下面的代碼非常簡單地將任意一張圖片壓縮成100*100的縮略圖,并在ImageView上展示。

        mImageView.setImageBitmap(

        decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));


        上一頁 1 2 下一頁

        關鍵詞:

        評論


        相關推薦

        技術專區

        關閉
        主站蜘蛛池模板: 竹山县| 宜春市| 霍林郭勒市| 刚察县| 当阳市| 岳普湖县| 汉寿县| 平利县| 武清区| 邯郸市| 育儿| 池州市| 宁晋县| 察哈| 西乌| 大新县| 云安县| 渑池县| 泰州市| 高台县| 山丹县| 永胜县| 临沂市| 满城县| 南雄市| 昆山市| 镇康县| 突泉县| 定安县| 华亭县| 奈曼旗| 昭平县| 高台县| 萝北县| 永兴县| 犍为县| 甘洛县| 长阳| 石泉县| 措勤县| 昌都县|