【Android 內存優化】Bitmap 長圖加載 ( BitmapRegionDecoder 簡介 | BitmapRegionDecoder 使用流程 | 區域解碼加載示例 )







一、BitmapRegionDecoder 簡介



官方文檔 API : BitmapRegionDecoder



BitmapRegionDecoder 簡介 :


① 主要作用 : BitmapRegionDecoder 可以從圖像中 解碼一個矩形區域 ;

② 適用場景 : 當一張圖片非常大 , 在手機中只需要顯示其中一部分內容 , BitmapRegionDecoder 非常有用 ;

③ 基本使用流程 : 先創建 , 後解碼 ;

  • 流程 1 : 創建 BitmapRegionDecoder : 調用 newInstance 方法 , 創建 BitmapRegionDecoder 對象 ;
// 創建 BitmapRegionDecoder 對象方法
static BitmapRegionDecoder newInstance(InputStream is, boolean isShareable)
static BitmapRegionDecoder newInstance(FileDescriptor fd, boolean isShareable)
static BitmapRegionDecoder newInstance(String pathName, boolean isShareable)
static BitmapRegionDecoder newInstance(byte[] data, int offset, int length, boolean isShareable)
  • 流程 2 : 解碼圖像區域內容 : 調用 decodeRegion 方法 , 獲取指定 Rect 矩形區域的解碼後的 Bitmap 對象 ;
Bitmap decodeRegion(Rect rect, BitmapFactory.Options options)




二、圖片信息



將一張圖片存放在 assets 目錄下 , 圖片尺寸爲 938 x 7561 , 這是 BitmapRegionDecoder 的文檔截圖 ;

該圖片如果按照默認的 ARGB_8888 格式加載到內存中 , 會佔用 28,368,872 字節的內存 , 大約 27 MB ;


內存大小計算過程如下 :

938×7561×4=28,368,872938 \times 7561 \times 4 = 28,368,872

在這裏插入圖片描述





三、BitmapRegionDecoder 對象創建



1 . BitmapRegionDecoder 對象創建 : 調用 newInstance 方法創建該對象 ;


① 函數作用 : 根據輸入流創建 BitmapRegionDecoder 對象 ;

② 輸入流的數據位置 : 輸入流的當前讀取位置就是在之前讀取的的解碼數據的後面一個字節位置 ;

③ 支持的圖像格式 : 目前圖像區域解碼對象只支持 JPEG 和 PNG 兩種圖像格式 ;



2 . 函數原型 :


  • InputStream is 參數 : 圖片的輸入流 ;

  • boolean isShareable 參數 : 是否共享輸入流 ; 如果設置了共享爲 true , 如果將該輸入流關閉 , 假如 BitmapRegionDecoder 對象中也在使用該輸入流 , 那麼關閉以後 , BitmapRegionDecoder 對象也無法使用該輸入流了 ; 如果設置該參數爲 false , 那麼關閉該輸入流 , 不影響 BitmapRegionDecoder 對象使用 , 一般都是該區域解碼對象需要長時間使用 , 此處都要設置成 false ;

    public static BitmapRegionDecoder newInstance(InputStream is,
            boolean isShareable) throws IOException {
        // 當前的輸入流是 AssetInputStream 輸入流的情況
        if (is instanceof AssetManager.AssetInputStream) {
            return nativeNewInstance(
                    ((AssetManager.AssetInputStream) is).getNativeAsset(),
                    isShareable);
        } else {
        	// 當前的輸入流是文件輸入流
        	// 傳入臨時緩存到 Native 代碼中 ; 
        	// 創建一個足夠大的臨時緩存區 , 這樣可以減少 is.read 方法的回調次數 ; 
        	// 應該避免 is.read 回調次數太多 , 同時每次讀取很少數據的情況 ; 
            byte [] tempStorage = new byte[16 * 1024];
            return nativeNewInstance(is, tempStorage, isShareable);
        }
    }




四、解碼圖像



函數原型 : 解碼 JPEG 或 PNG 中指定的矩形區域 ;

  • Rect rect 參數 : 要解碼的矩形區域 ;
  • BitmapFactory.Options options 參數 : 解碼選項 ;
public Bitmap decodeRegion(Rect rect, BitmapFactory.Options options)




五、圖像區域解碼示例



1 . 主界面代碼 : 先創建 BitmapRegionDecoder 對象 , 然後調用該對象的 decodeRegion 方法 , 進行圖像剪切 ;

package kim.hsl.lgl;

import android.graphics.Bitmap;
import android.graphics.BitmapRegionDecoder;
import android.graphics.Rect;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

import java.io.IOException;
import java.io.InputStream;

public class MainActivity extends AppCompatActivity {

    static {
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextView tv = findViewById(R.id.sample_text);
        tv.setText(stringFromJNI());

        // 顯示剪切後的正方形圖像
        showImage();
    }

    private void showImage(){
        InputStream inputStream = null;
        try {
            // 獲取 Assets 文件的輸入流
            inputStream = getAssets().open("bitmap_region_decoder.png");
            
            /*
                函數原型 :
                public static BitmapRegionDecoder newInstance(InputStream is,
                    boolean isShareable) throws IOException {

                InputStream is 參數 : 圖片的輸入流
                boolean isShareable 參數 : 是否共享輸入流

                如果設置了共享爲 true , 如果將該輸入流關閉 ,
                假如 BitmapRegionDecoder 對象中也在使用該輸入流 ,
                那麼關閉以後 , BitmapRegionDecoder 對象也無法使用該輸入流了 ;
                如果設置該參數爲 false , 那麼關閉該輸入流 , 不影響 BitmapRegionDecoder 對象使用 ,
                一般都是該區域解碼對象需要長時間使用 , 此處都要設置成 false ;
             */
            BitmapRegionDecoder bitmapRegionDecoder = BitmapRegionDecoder
                    .newInstance(inputStream, false);

            /*
                解碼圖片
                這裏解析前面的一部分圖片
             */
            Bitmap bitmap = bitmapRegionDecoder.decodeRegion(
                    new Rect(0, 0, 938, 938),   //解碼區域
                    null);  //解碼選項 BitmapFactory.Options 類型

            ImageView imageView = findViewById(R.id.imageView);
            imageView.setImageBitmap(bitmap);


        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(inputStream != null){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public native String stringFromJNI();
}

2 . 佈局文件 : 在佈局中放置一個正方形的 ImageView , 顯示剪切後的 938 x 938 大小的 Bitmap 圖片 ;

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="match_parent"
        android:layout_height="0dip"
        android:scaleType="fitXY"
        app:layout_constraintDimensionRatio="1:1"
        app:layout_constraintVertical_bias="0"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/sample_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

3 . 執行效果 : 正方形的 ImageView , 顯示從 938 x 7561 大小的圖片上剪切下來的 938 x 938 大小的圖片 , 效果如下 ;

在這裏插入圖片描述





六、源碼及資源下載



源碼及資源下載地址 :

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章