13

ビットマップのデコードに関するメモリ割り当ての問題に関する多くの関連記事を読みましたが、公式 Web サイトで提供されているコードを使用した後でも、次の問題の解決策を見つけることができません。

これが私のコードです:

public static Bitmap decodeSampledBitmapFromResource(InputStream inputStream, int reqWidth, int reqHeight) {

    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    byte[] buffer = new byte[1024];
    int len;
    try {
        while ((len = inputStream.read(buffer)) > -1) {
        baos.write(buffer, 0, len);
        }
        baos.flush();
        InputStream is1 = new ByteArrayInputStream(baos.toByteArray());
        InputStream is2 = new ByteArrayInputStream(baos.toByteArray());

        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(is1, null, options);

        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
        options.inPurgeable = true;
        options.inInputShareable = true;
        options.inJustDecodeBounds = false;
        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
        return BitmapFactory.decodeStream(is2, null, options);

    } catch (Exception e) {
        e.printStackTrace();

        return null;
    }
}

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) {

        // Calculate ratios of height and width to requested height and width
        final int heightRatio = Math.round((float) height / (float) reqHeight);
        final int widthRatio = Math.round((float) width / (float) reqWidth);

        // Choose the smallest ratio as inSampleSize value, this will guarantee
        // a final image with both dimensions larger than or equal to the
        // requested height and width.
        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
    }


    return inSampleSize;
}

bitmap = decodeSampledBitmapFromResource(inputStream, 600, 600); 

次の行に「3250016 のメモリ不足エラー - バイト割り当て」が表示されます。

return BitmapFactory.decodeStream(is2, null, options);

3.2 MB は割り当てられるのに十分小さいように思えます。どこが間違っていますか?どうすればこれを解決できますか?

編集

N-Joy のこちらのソリューションを調べた後、必要なサイズ 300 で問題なく動作しますが、必要なサイズは 800 であるため、まだエラーが発生しています。

4

10 に答える 10

36

このメソッドdecodeSampledBitmapFromResourceは、ByteArrayOutputStream baos、ByteArrayInputStream is1、および ByteArrayInputStream is2 の 3 つのストリームを使用するため、メモリ効率がよくありません。これらのそれぞれは、イメージの同じストリーム データ(それぞれに 1 バイト配列) を格納します。

デバイス (LG nexus 4) でテストして、SD カード上の 2560x1600 の画像をターゲット サイズ 800 にデコードすると、次のようになります。

03-13 15:47:52.557: E/DecodeBitmap(11177): dalvikPss (beginning) = 1780
03-13 15:47:53.157: E/DecodeBitmap(11177): dalvikPss (decoding) = 26393
03-13 15:47:53.548: E/DecodeBitmap(11177): dalvikPss (after all) = 30401 time = 999

4096000 ピクセルの画像をデコードするためだけに割り当てられたメモリ (28.5 MB) が多すぎることがわかります。

解決策: InputStream を読み取り、データを 1 つのバイト配列に直接格納し、このバイト配列を残りの作業に使用します。
サンプルコード:

public Bitmap decodeSampledBitmapFromResourceMemOpt(
            InputStream inputStream, int reqWidth, int reqHeight) {

        byte[] byteArr = new byte[0];
        byte[] buffer = new byte[1024];
        int len;
        int count = 0;

        try {
            while ((len = inputStream.read(buffer)) > -1) {
                if (len != 0) {
                    if (count + len > byteArr.length) {
                        byte[] newbuf = new byte[(count + len) * 2];
                        System.arraycopy(byteArr, 0, newbuf, 0, count);
                        byteArr = newbuf;
                    }

                    System.arraycopy(buffer, 0, byteArr, count, len);
                    count += len;
                }
            }

            final BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeByteArray(byteArr, 0, count, options);

            options.inSampleSize = calculateInSampleSize(options, reqWidth,
                    reqHeight);
            options.inPurgeable = true;
            options.inInputShareable = true;
            options.inJustDecodeBounds = false;
            options.inPreferredConfig = Bitmap.Config.ARGB_8888;

            int[] pids = { android.os.Process.myPid() };
            MemoryInfo myMemInfo = mAM.getProcessMemoryInfo(pids)[0];
            Log.e(TAG, "dalvikPss (decoding) = " + myMemInfo.dalvikPss);

            return BitmapFactory.decodeByteArray(byteArr, 0, count, options);

        } catch (Exception e) {
            e.printStackTrace();

            return null;
        }
    }

計算を行うメソッド:

public void onButtonClicked(View v) {
        int[] pids = { android.os.Process.myPid() };
        MemoryInfo myMemInfo = mAM.getProcessMemoryInfo(pids)[0];
        Log.e(TAG, "dalvikPss (beginning) = " + myMemInfo.dalvikPss);

        long startTime = System.currentTimeMillis();

        FileInputStream inputStream;
        String filePath = Environment.getExternalStorageDirectory()
                .getAbsolutePath() + "/test2.png";
        File file = new File(filePath);
        try {
            inputStream = new FileInputStream(file);
//          mBitmap = decodeSampledBitmapFromResource(inputStream, 800, 800);
            mBitmap = decodeSampledBitmapFromResourceMemOpt(inputStream, 800,
                    800);
            ImageView imageView = (ImageView) findViewById(R.id.image);
            imageView.setImageBitmap(mBitmap);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        myMemInfo = mAM.getProcessMemoryInfo(pids)[0];
        Log.e(TAG, "dalvikPss (after all) = " + myMemInfo.dalvikPss
                + " time = " + (System.currentTimeMillis() - startTime));
    }

そして結果:

03-13 16:02:20.373: E/DecodeBitmap(13663): dalvikPss (beginning) = 1823
03-13 16:02:20.923: E/DecodeBitmap(13663): dalvikPss (decoding) = 18414
03-13 16:02:21.294: E/DecodeBitmap(13663): dalvikPss (after all) = 18414 time = 917
于 2013-03-13T09:04:17.660 に答える
4

これは、ユーザーが大きなビットマップで遊んでいるときに通常直面する一般的な問題であり、ユーザーが正確な解決策を操作できない場合でも、サイト、ここここここここなどで多くの質問が議論されています。

ビットマップをスムーズに管理するライブラリと、以下にリストした他のリンクを偶然見つけました。お役に立てれば!

スムージーライブラリ

Android-BitmapCache

OutOfMemoryErrorの Android-Universal-Image-Loader ソリューション: ビットマップ サイズが VM の予算を超えています

于 2013-03-07T06:05:34.517 に答える
2

ARGB_8888アルファカラー値を使用するため、より多くのメモリを使用するため、ここRGB_565に記載されているように使用することをお勧めします

注: に比べて品質が少し低下しARGB_8888ます。

于 2013-03-14T14:30:27.107 に答える
1

ビットマップのメモリ使用量に多くの問題がありました。

結果:

  • ほとんどのデバイスでは、グラフィックス用のヒープ メモリが限られています。ほとんどの小型デバイスでは、アプリだけでなく、アプリ全体で 16 MB に制限されています
  • 該当する場合は、4 ビットまたは 8 ビットまたは 16 ビットのビットマップを使用します。
  • 可能であればビットマップを省略して、ゼロから図形を描画してみてください。
于 2013-03-07T06:09:15.603 に答える
1

WebViewを使用して、好きなだけ画像を動的にロードします。NDK (低レベル) を使用して構築されているため、GDI ヒープ メモリの制限はありません。それはスムーズかつ高速に動作します:)

于 2013-03-07T06:29:34.033 に答える
1

おそらく、以前のビットマップ参照を保持しているでしょう。このコードを数回実行していて、決して実行していないと思いますbitmap.recycle()。メモリは必然的に不足します。

于 2013-03-06T17:48:29.293 に答える
0

このビデオを見てください。http://www.youtube.com/watch?v=_CruQY55HOk . ビデオで提案されているように system.gc() を使用しないでください。MAT アナライザーを使用して、メモリ リークを見つけます。返されたビットマップが大きすぎるため、メモリ リークが発生していると思われます。

于 2013-03-06T17:47:09.290 に答える
0

表示する画像が大きいようです。

画像をダウンロードして sdcard に保存し ()、この コードを使用して sdcard から画像を表示できます。

于 2013-03-06T17:50:25.873 に答える
-1

私も以前に同じ問題を抱えていました..必要な幅と高さとしてスケールを取得できるこの関数を使用して管理しました。

private Bitmap decodeFile(FileInputStream f)
{
    try
    {
        //decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(f,null,o);

        //Find the correct scale value. It should be the power of 2.
        final int REQUIRED_SIZE=70;
        int width_tmp=o.outWidth, height_tmp=o.outHeight;
        int scale=1;
        while(true)
        {
            if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
                break;
            width_tmp/=2;
            height_tmp/=2;
            scale*=2;
        }

        //decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize=scale;
        return BitmapFactory.decodeStream(f, null, o2);
    } 
    catch (FileNotFoundException e) {}
    return null;
}

およびメモリリークエラー Androidおよびgridview android への画像の読み込み中のエラーを参照してください

于 2013-03-15T13:28:24.783 に答える