Android Bitmap画像処理、メモリオーバーフロー防止

11125 ワード

Androidに1枚の画像をロードすると、画像が大きすぎるとメモリオーバーフローが発生する可能性があります.特にデータのロードが多すぎる場合、ListViewやGridViewなどの重複リストにあるため、Android画像を処理してメモリオーバーフローを防ぐことが特に重要であり、面接でよく聞かれる質問も多いです.面接官は通常、メモリの最適化方法について話します.では、画像のロード最適化は非常に重要な内容です.まず、メモリにロードされた画像が占めるメモリサイズの計算方法を見てみましょう.
*画像のロードに必要なメモリはどのように計算されますか?
1枚のピクチャ占有メモリ=ピクチャ長ピクチャ幅単位ピクセル占有バイト数.
ここで単位画素が占有するバイト数はピクチャのパラメータによって決まる、androidではBitmapFactory.OptionsのinPreferredConfig変数によって決定されます.inPreferredConfigはBitmap.Configタイプ:次のようになります.
Bitmap.Config 1. ALPHA_8
Each pixel is stored as a single translucency (alpha) channel. This is very useful to efficiently store masks for instance. No color information is stored. With this configuration, each pixel requires 1 byte of memory. この場合、画像はalpha値のみで、RGB値はなく、1画素1バイトを占有します.
  • ARGB_4444

  • This field is deprecated. Because of the poor quality of this configuration, it is advised to use ARGB_8888instead. このフォーマットの画像は、品質が悪すぎて、もうお勧めしません.Each pixel is stored on 2 bytes. The three RGB color channels and the alpha channel (translucency) are stored with a 4 bits precision (16 possible values.) This configuration is mostly useful if the application needs to store translucency information but also needs to save memory. It is recommended to use ARGB_8888 instead of this configuration. 1画素あたり2バイト、alpha(A)値、Red(R)値、Green(G)値、Blue(B)値がそれぞれ4ビット、合計16ビット、つまり2バイト
  • ARGB_8888

  • Each pixel is stored on 4 bytes. Each channel (RGB and alpha for translucency) is stored with 8 bits of precision (256 possible values.) This configuration is very flexible and offers the best quality. It should be used whenever possibleは1画素あたり4バイト、alpha(A)値、Red(R)値、Green(G)値、Blue(B)値がそれぞれ8ビット、32ビット、つまり4バイトと高品質のピクチャフォーマットで、パソコンで一般的に採用されているフォーマットです.Androidの携帯電話のBitMapでもあります
  • RGB_565

  • Each pixel is stored on 2 bytes and only the RGB channels are encoded: red is stored with 5 bits of precision (32 possible values), green is stored with 6 bits of precision (64 possible values) and blue is stored with 5 bits of precision. This configuration can produce slight visual artifacts depending on the configuration of the source. For instance, without dithering, the result might show a greenish tint. To get better results dithering should be applied. This configuration may be useful when using opaque bitmaps that do not require high color fidelity. 1画素2バイトを占有する、alpha(A)の値がない、すなわち透明と半透明をサポートしない、Red(R)の値が5ビット、Green(G)の値が6ビット、Blue(B)の値が5ビット、合計16ビット、すなわち2バイトである.透明色と半透明色の画像がない場合、このフォーマットの画像はARGB_に対して比較的レンダリング効果を達成することができる.8888では、メモリのオーバーヘッドを半分に削減できます.だからそれはいい選択です.私たちはandroidを通じてcontent.res.Resourcesが画像を取得する場合も、BitMapのAndroid 4から構築されます.0から開始します.このオプションは無効です.この値に設定しても、システムはARGB_を採用します.8888で構築
    Bitmap原理:各画像が画面に表示される前にメモリ処理を加えて画面に出力する必要があります.では、この画像を表示する必要があります.メモリが安全であることを完全に確認することはできません.androidは、画像の情報を読み取るときにメモリを入れないようにする方法を提供しています.画像情報を読み取ってサイズの圧縮処理を行った後、メモリ表示を再び加えると安全です.(java.lang.OutOfMemoryの異常を避けるためには、画像を実際に解析する前にサイズを確認する必要があります(このデータソースが正確な画像を提供し、メモリを過剰に消費しないことを確認できない限り).
    メモリの漏洩を避けるために、画像を読み取るときに圧縮を行います.通常は、画像のサイズを取得してから、サイズと画面サイズの割合で圧縮します.以下のようにします.
    BitmapFactory.Options optins=new BitmapFactory.Options();  
    options.inJustDecodeBounds=true; //                          
    BitmapFactory.decodeResources(getResources(),R.id.image,options);  //         
    int imgHeight=options.outHeight;  //      
    int imgWidth=options.outWidth;    //      
    String imageType=options.outMimeType;  //    

    次に、サイズ縮小バージョンをメモリにロードします.
    デコーダに縮小バージョンの画像をメモリにロードするように伝えるには、BitmapFactory.optionsでinSampleSizeの値を設定します.例えば、解像度が2048*1536のピクチャは、inSampleSize=4を設定すると、約512*384サイズのbitmapが生成され、このピクチャをロードするのに必要なメモリは約0.75 M(512*384*4/1024/1024)であり、原図をロードすると約12 Mメモリ(2048*1536*4/1024/1024)が必要になる(Bitmapの構成がARGB_8888であることを前提とする).以下にコードを貼り付けます:inSampleSizeの数字は何の表示で何倍に圧縮します.
    public static int calculateInSampleSize(
                BitmapFactory.Options options, int reqWidth, int reqHeight) {   
        // Raw height and width of image
        final int height = options.outHeight;   
        final int width = options.outWidth;      
        int inSampleSize = 1;
    if (height > reqHeight || width > reqWidth) {
            final int halfHeight = height / 2;          
            final int halfWidth = width / 2;          
            while ((halfHeight / inSampleSize) > reqHeight
                    && (halfWidth / inSampleSize) > reqWidth) {
                inSampleSize *= 2;
            }
        }
        return inSampleSize;
    }
    

    注意:inSampleSizeを2のべき乗に設定するのは、デコーダが最終的に2以外のべき乗のデータを下に処理するためで、2に最も近いべき乗の数を取得するには、inSampleSizeのドキュメントを参照してください.
    次に実戦で、まずプロジェクトを作成します.LoadImage:Activityコードは次のとおりです.
    public  int calculateInSampleSize(
                BitmapFactory.Options options, int reqWidth, int reqHeight) {
            options.inJustDecodeBounds = true;
            options.inPreferredConfig= Bitmap.Config.ARGB_8888;
            BitmapFactory.decodeResource(getResources(),R.drawable.abc,options);
            final int height = options.outHeight;
            final int width = options.outWidth;
            int inSampleSize = 1;
            if (height > reqHeight || width > reqWidth) {
                final int halfHeight = height / 2;
                final int halfWidth = width / 2;
                while ((halfHeight / inSampleSize) > reqHeight
                        && (halfWidth / inSampleSize) > reqWidth) {
                    inSampleSize *= 2;
                }
            }
            return inSampleSize;
        }

    このコードは簡単で、上で基本的に述べたように、私たちのコントロールが表示する幅の高さに基づいて圧縮の割合を計算し、inSampleSizeを得ることです.
    次のコードは簡単です.
    private Bitmap loadImage() {
            BitmapFactory.Options options = new BitmapFactory.Options();
                int j = calculateInSampleSize(options, 200, 250);
                options.inSampleSize = j;
                options.inJustDecodeBounds = false;
                int outWidth = options.outWidth;
                int outHeight = options.outHeight;
                Log.d("MainActivity", "outH:" + outHeight + ":outW:" + outWidth);
            return BitmapFactory.decodeResource(getResources(), R.drawable.abc, options);
        }

    この最も重要な一歩はoptionsです.inJustDecodeBounds = false; 画像に本当にメモリが入っていることを示していますが、これは比例的に圧縮されているので、メモリのオーバーフローを心配する必要はありません.
    最後に表示を設定すればいいです
    private ImageView imageView;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.image_view);
            imageView = (ImageView) findViewById(R.id.image_view);
            Bitmap bitmap = loadImage();
        }

    次に、レイアウトファイルを貼り付けます.
    
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent">
        <ImageView
            android:id="@+id/image_view"
            android:layout_width="match_parent"
            android:scaleType="center"
            android:src="@drawable/myimage"
            android:layout_height="match_parent"
            android:layout_gravity="center_vertical"/>
    LinearLayout>

    簡単ではありませんが、android studioの学生がロードされたメモリサイズを直接見ることができる場合は、入力された表示コントロールの要求の幅を異なるように設定すると、メモリの需要も異なります.具体的なメモリは、厳密なピクチャロードメモリのサイズほど大きくない可能性があります.これは、空のアプリケーションでもメモリを消費しなければならないためですが、大体のメモリはピクチャに必要なメモリに近いからです.
    さて、Bitmapの最も簡単な使い方はここまでで、次の文章はもっと高級な使い方を減らします.