質問

いリストビュープンしたブックショップの画像ボタンいですか。をクリックすると、リスト列で新。していた自分のタブでのカメラをレイアウト。の活動が始まったのです。場合はをクリックしても自分のボタンの画像をプレビュース(ロード画像はSDカードの申請を返しますから、活動を listview 活動の結果、ハンドラを再起動は私の新しい活動であるにも、ありとあら画像ウィジェット.

画像のプレビュー、リストビューの手によるものであり、カーソル ListAdapter.このイラストはピアプロかかることができるサイズ変更画像のサイトカイン小さなビットのサイズをピクセルとしての src 画像のボタンを押します。だリサイズのイメージしていたのですが、電話のカメラです。

の問題として取得し、out of memoryエラーがろうとし、再発第2回です。

  • はしてもらえると助かりますごめんなさい...皆知ってたんで、リストアダプタを容易に行列ができるサイズ変更をしたインタフェースwiseビット)?

このことが好ましいとしても必要な変更は他のウィジェット/要素の各行ができないを選択し、連続してタッチ画面での課題です。(使用できますローラーボール。)

  • かんのバンドのサイズの変更や保存の画像がないんでいきたいと思い、サンプルコードがいいですね。

っと障害者の画像のリストビューした。

参考:こうしてやっていたので

String[] from = new String[] { DBHelper.KEY_BUSINESSNAME,DBHelper.KEY_ADDRESS,DBHelper.KEY_CITY,DBHelper.KEY_GPSLONG,DBHelper.KEY_GPSLAT,DBHelper.KEY_IMAGEFILENAME  + ""};
int[] to = new int[] {R.id.businessname,R.id.address,R.id.city,R.id.gpslong,R.id.gpslat,R.id.imagefilename };
notes = new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to);
setListAdapter(notes);

場所 R.id.imagefilenameButtonImage.

こちらは自LogCat:

01-25 05:05:49.877: ERROR/dalvikvm-heap(3896): 6291456-byte external allocation too large for this process.
01-25 05:05:49.877: ERROR/(3896): VM wont let us allocate 6291456 bytes
01-25 05:05:49.877: ERROR/AndroidRuntime(3896): Uncaught handler: thread main exiting due to uncaught exception
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:304)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:149)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:174)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.drawable.Drawable.createFromPath(Drawable.java:729)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ImageView.resolveUri(ImageView.java:484)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ImageView.setImageURI(ImageView.java:281)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.SimpleCursorAdapter.setViewImage(SimpleCursorAdapter.java:183)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.SimpleCursorAdapter.bindView(SimpleCursorAdapter.java:129)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.CursorAdapter.getView(CursorAdapter.java:150)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.AbsListView.obtainView(AbsListView.java:1057)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ListView.makeAndAddView(ListView.java:1616)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ListView.fillSpecific(ListView.java:1177)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ListView.layoutChildren(ListView.java:1454)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.AbsListView.onLayout(AbsListView.java:937)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.layoutHorizontal(LinearLayout.java:1108)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.onLayout(LinearLayout.java:922)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.FrameLayout.onLayout(FrameLayout.java:294)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.layoutVertical(LinearLayout.java:999)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.onLayout(LinearLayout.java:920)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.FrameLayout.onLayout(FrameLayout.java:294)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.ViewRoot.performTraversals(ViewRoot.java:771)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.ViewRoot.handleMessage(ViewRoot.java:1103)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.os.Handler.dispatchMessage(Handler.java:88)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.os.Looper.loop(Looper.java:123)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.app.ActivityThread.main(ActivityThread.java:3742)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at java.lang.reflect.Method.invokeNative(Native Method)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at java.lang.reflect.Method.invoke(Method.java:515)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:739)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:497)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at dalvik.system.NativeStart.main(Native Method)
01-25 05:10:01.127: ERROR/AndroidRuntime(3943): ERROR: thread attach failed 

また新たなエラー表示時にイメージ:

01-25 22:13:18.594: DEBUG/skia(4204): xxxxxxxxxxx jpeg error 20 Improper call to JPEG library in state %d
01-25 22:13:18.604: INFO/System.out(4204): resolveUri failed on bad bitmap uri: 
01-25 22:13:18.694: ERROR/dalvikvm-heap(4204): 6291456-byte external allocation too large for this process.
01-25 22:13:18.694: ERROR/(4204): VM won't let us allocate 6291456 bytes
01-25 22:13:18.694: DEBUG/skia(4204): xxxxxxxxxxxxxxxxxxxx allocPixelRef failed
役に立ちましたか?

解決

Androidトレーニングクラス、<!> quot; ビットマップを効率的に表示する <!> quot ;、ビットマップをロードする際の例外java.lang.OutOfMemoryError: bitmap size exceeds VM budgetを理解し対処するための優れた情報を提供します。


ビットマップの寸法とタイプを読む

BitmapFactoryクラスは、さまざまなソースからdecodeByteArray()を作成するためのいくつかのデコードメソッド(decodeFile()decodeResource()Bitmapなど)を提供します。画像データソースに基づいて最も適切なデコード方法を選択します。これらのメソッドは、構築されたビットマップにメモリを割り当てようとするため、OutOfMemory例外が簡単に発生する可能性があります。デコードメソッドの各タイプには、BitmapFactory.Optionsクラスを介してデコードオプションを指定できる追加のシグネチャがあります。デコード中にinJustDecodeBoundsプロパティをtrueに設定すると、メモリ割り当てが回避され、ビットマップオブジェクトに対してnullが返されますが、outWidthoutHeight、およびoutMimeTypeは設定されます。この手法により、ビットマップの構築(およびメモリ割り当て)の前に、画像データのサイズとタイプを読み取ることができます。

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;

java.lang.OutOfMemory例外を回避するには、使用可能なメモリに快適に収まる予測可能なサイズの画像データを提供するソースを絶対に信頼しない限り、ビットマップをデコードする前にビットマップのサイズを確認します。


縮小版をメモリに読み込む

画像の寸法がわかったので、それらを使用して、完全な画像をメモリにロードするか、サブサンプリングバージョンを代わりにロードするかを決定できます。考慮すべきいくつかの要素を次に示します。

  • メモリにフルイメージをロードするための推定メモリ使用量。
  • アプリケーションのその他のメモリ要件を考慮して、このイメージのロードにコミットできるメモリの量。
  • イメージがロードされるターゲットImageViewまたはUIコンポーネントの寸法。
  • 現在のデバイスの画面サイズと密度。

たとえば、ImageViewの128x96ピクセルのサムネイルに最終的に表示される場合、それはメモリに1024x768ピクセルのイメージをロードする価値はありません<!>#8217;

画像をサブサンプリングし、メモリに小さいバージョンをロードするようデコーダーに指示するには、inSampleSizeオブジェクトでARGB_8888falseに設定します。たとえば、4のBitmapFactory.decode*でデコードされた解像度2048x1536の画像は、約512x384のビットマップを生成します。これをメモリに読み込むには、フルイメージに12MBではなく0.75MBを使用します(ビットマップ構成が<=>の場合)。 Here <!>#8217; sは、ターゲットの幅と高さに基づいて2のべき乗であるサンプルサイズ値を計算する方法です。

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;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) > reqHeight
                && (halfWidth / inSampleSize) > reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}
  

:デコーダーは   次のように、最も近い2のべき乗に切り捨てて最終値   <=>ドキュメント。

この方法を使用するには、まず<=>を<=>に設定してデコードし、オプションをパススルーしてから、新しい<=>値と<=>を<=>に設定して再度デコードします:

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
    int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

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

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

この方法により、次のコード例に示すように、任意の大きなサイズのビットマップを<=>にロードして、100x100ピクセルのサムネイルを簡単に表示できます。

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

同様のプロセスに従って、必要に応じて適切な<=>メソッドを置き換えることにより、他のソースからビットマップをデコードできます。

他のヒント

OutOfMemoryエラーを修正するには、次のようにする必要があります。

BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
Bitmap preview_bitmap = BitmapFactory.decodeStream(is, null, options);

このinSampleSizeオプションはメモリ消費を削減します。

完全なメソッドは次のとおりです。まず、コンテンツ自体をデコードせずに画像サイズを読み取ります。次に、最良の<=>値を見つけます。2のべき乗である必要があり、最終的に画像がデコードされます。

// Decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f) {
    try {
        // Decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(new FileInputStream(f), null, o);

        // The new size we want to scale to
        final int REQUIRED_SIZE=70;

        // Find the correct scale value. It should be the power of 2.
        int scale = 1;
        while(o.outWidth / scale / 2 >= REQUIRED_SIZE && 
              o.outHeight / scale / 2 >= REQUIRED_SIZE) {
            scale *= 2;
        }

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

私はFedorのコードを少し改善しました。基本的には同じですが、(私の意見では)いwhileループがなく、常に2のべき乗になります。元のソリューションを作成してくれたFedorに称賛を送ります。彼を見つけるまで行き詰まりました。

 private Bitmap decodeFile(File f){
    Bitmap b = null;

        //Decode image size
    BitmapFactory.Options o = new BitmapFactory.Options();
    o.inJustDecodeBounds = true;

    FileInputStream fis = new FileInputStream(f);
    BitmapFactory.decodeStream(fis, null, o);
    fis.close();

    int scale = 1;
    if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) {
        scale = (int)Math.pow(2, (int) Math.ceil(Math.log(IMAGE_MAX_SIZE / 
           (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5)));
    }

    //Decode with inSampleSize
    BitmapFactory.Options o2 = new BitmapFactory.Options();
    o2.inSampleSize = scale;
    fis = new FileInputStream(f);
    b = BitmapFactory.decodeStream(fis, null, o2);
    fis.close();

    return b;
}

私はiOSの経験から来ましたが、画像の読み込みと表示のような基本的な問題を発見することにイライラしていました。結局のところ、この問題を抱えているすべての人が適度なサイズの画像を表示しようとしています。とにかく、ここに私の問題を修正した2つの変更点があります(そして私のアプリはとても反応しました)。

1)BitmapFactory.decodeXYZ()を実行するたびに、BitmapFactory.OptionsinPurgeableに設定して(そしてできればtrueinInputShareableに設定して)Bitmap.createBitmap(width, height, Config.ARGB_8888)を渡すようにしてください。

2)recycle()を使用しないでください。私は決して意味しない!数回パスしてもメモリエラーが発生しないことはありませんでした。 System.gc()Bitmap.createScaledBitmap(srcBitmap, width, height, false)など、何も役に立たなかった。常に例外が発生しました。実際に動作するもう1つの方法は、ドロウアブル(または上記の手順1を使用してデコードした別のビットマップ)にダミーイメージを用意し、それを必要に応じてスケーリングし、結果のビットマップを操作します(キャンバスに渡すなど)より楽しいために)。したがって、代わりに使用する必要があるのはConfig.ARGB_4444です。何らかの理由でブルートフォース作成メソッドを使用する必要がある場合は、少なくとも<=>を渡します。

これにより、数日ではないにしても、何時間も節約できます。画像のスケーリングなどについて説明していることはすべて、実際には機能しません(間違ったサイズや劣化した画像を取得することを検討しない限り)。

これは既知のバグであり、大規模なためではありませんファイル。 AndroidはDrawableをキャッシュするため、いくつかの画像を使用するとメモリが不足します。しかし、Androidのデフォルトキャッシュシステムをスキップすることで、別の方法を見つけました。

ソリューション: 画像を<!> quot; assets <!> quot;に移動します。フォルダーに移動し、次の関数を使用してBitmapDrawableを取得します。

public static Drawable getAssetImage(Context context, String filename) throws IOException {
    AssetManager assets = context.getResources().getAssets();
    InputStream buffer = new BufferedInputStream((assets.open("drawable/" + filename + ".png")));
    Bitmap bitmap = BitmapFactory.decodeStream(buffer);
    return new BitmapDrawable(context.getResources(), bitmap);
}

この同じ問題があり、BitmapFactory.decodeStreamまたはdecodeFile関数を回避することで解決し、代わりにBitmapFactory.decodeFileDescriptor

を使用しました

decodeFileDescriptorはdecodeStream / decodeFileとは異なるネイティブメソッドを呼び出すように見えます。

とにかく、これがうまくいったのです(上記のようにいくつかのオプションを追加しましたが、違いはありませんでした。重要なのは、 BitmapFactory.decodeFileDescriptor > decodeStream または decodeFile ):

private void showImage(String path)   {

    Log.i("showImage","loading:"+path);
    BitmapFactory.Options bfOptions=new BitmapFactory.Options();
    bfOptions.inDither=false;                     //Disable Dithering mode
    bfOptions.inPurgeable=true;                   //Tell to gc that whether it needs free memory, the Bitmap can be cleared
    bfOptions.inInputShareable=true;              //Which kind of reference will be used to recover the Bitmap data after being clear, when it will be used in the future
    bfOptions.inTempStorage=new byte[32 * 1024]; 

    File file=new File(path);
    FileInputStream fs=null;
    try {
        fs = new FileInputStream(file);
    } catch (FileNotFoundException e) {
        //TODO do something intelligent
        e.printStackTrace();
    }

    try {
        if(fs!=null) bm=BitmapFactory.decodeFileDescriptor(fs.getFD(), null, bfOptions);
    } catch (IOException e) {
        //TODO do something intelligent
        e.printStackTrace();
    } finally{ 
        if(fs!=null) {
            try {
                fs.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    //bm=BitmapFactory.decodeFile(path, bfOptions); This one causes error: java.lang.OutOfMemoryError: bitmap size exceeds VM budget

    im.setImageBitmap(bm);
    //bm.recycle();
    bm=null;                        
}

decodeStream / decodeFileで使用されるネイティブ関数に問題があると思います。 decodeFileDescriptorを使用すると、別のネイティブメソッドが呼び出されることを確認しました。また、私が読んだのは、画像(ビットマップ)が標準のJava方式ではなく、ネイティブ呼び出しを介して割り当てられる<!> quot;です。割り当ては仮想ヒープの外部で行われますが、 それに対してカウント! <!> quot;

OutOfMemoryErrorを回避する最善の方法は、それに直面して理解することだと思います。

意図的に<=>を引き起こし、監視するためにアプリを作成しましたメモリ使用量。

このアプリで多くの実験を行った後、次の結論に達しました:

最初にHoney Combの前にSDKバージョンについて話します。

  1. ビットマップはネイティブヒープに格納されますが、ガベージコレクションが自動的に行われるため、recycle()を呼び出す必要はありません。

  2. {VMヒープサイズ} + {割り当てられたネイティブヒープメモリ} <!> gt; = {デバイスのVMヒープサイズ制限}で、ビットマップを作成しようとすると、OOMがスローされます。

    注意:VM ALLOCATED MEMORYではなくVM HEAP SIZEがカウントされます。

  3. VMヒープサイズは、割り当てられたVMメモリが縮小された場合でも、成長後に縮小することはありません。

  4. したがって、VMヒープサイズが大きくなりすぎてビットマップに使用可能なメモリを節約できないように、ピークVMメモリをできるだけ低く保つ必要があります。

  5. System.gc()を手動で呼び出すことは無意味です。システムは、ヒープサイズを増やす前に最初に呼び出します。

  6. ネイティブヒープサイズも決して縮小しませんが、OOMにはカウントされないため、心配する必要はありません。

次に、Sony Starts from Honey Combについて説明しましょう。

  1. ビットマップはVMヒープに格納され、ネイティブメモリはOOMにカウントされません。

  2. OOMの条件ははるかに単純です:{VMヒープサイズ} <!> gt; = {デバイスのVMヒープサイズ制限}。

  3. 同じヒープサイズ制限でビットマップを作成するために使用可能なメモリが多いため、OOMがスローされる可能性は低くなります。

ガベージコレクションとメモリリークに関する私の観察の一部です。

アプリで自分で確認できます。アクティビティが破棄された後もまだ実行中のAsyncTaskをアクティビティが実行した場合、アクティビティはAsyncTaskが終了するまでガベージコレクションされません。

これは、AsyncTaskが匿名内部クラスのインスタンスであり、アクティビティの参照を保持しているためです。

バックグラウンドスレッドのIO操作でタスクがブロックされている場合、AsyncTask.cancel(true)を呼び出しても実行は停止しません。

コールバックも匿名の内部クラスであるため、プロジェクトの静的インスタンスがコールバックを保持し、リリースしない場合、メモリがリークします。

タイマーなどの繰り返しタスクまたは遅延タスクをスケジュールした場合、onPause()でcancel()およびpurge()を呼び出さないと、メモリがリークします。

最近、OOMの例外とキャッシュについて多くの質問を見てきました。開発者ガイドには、これに関する本当に良い記事がありますが、適切な方法で実装することに失敗する傾向があります。

このため、Android環境でのキャッシングを示すサンプルアプリケーションを作成しました。この実装はまだOOMを取得していません。

ソースコードへのリンクについては、この回答の最後をご覧ください。

要件:

  • Android API 2.1以降(API 1.6でアプリケーションの利用可能なメモリを取得することができませんでした-これはAPI 1.6で動作しない唯一のコードです)
  • Androidサポートパッケージ

スクリーンショット

機能:

  • 向きが変更された場合にキャッシュを保持します、シングルトンを使用して
  • キャッシュに割り当てられたアプリケーションメモリの 1/8 を使用します(必要に応じて変更します)
  • 大きなビットマップはスケーリングされます(許可する最大ピクセルを定義できます)
  • ビットマップをダウンロードする前に、インターネット接続が利用可能であることを制御します
  • インスタンス化するのが行ごとに 1つのタスクのみであることを確認します
  • あなたがあなたを投げ飛ばしている場合ListView離れている場合、単にビットマップをダウンロードしません

これには以下は含まれません:

  • ディスクキャッシュ。とにかくこれは簡単に実装できるはずです-ディスクからビットマップを取得する別のタスクをポイントするだけです

サンプルコード:

ダウンロード中の画像は、Flickrからの画像(75x75)です。ただし、処理したい画像のURLを入力すると、アプリケーションは最大値を超えた場合に縮小します。このアプリケーションでは、URLは単にString配列にあります。

LruCache はビットマップを処理するための良い方法です。ただし、このアプリケーションでは、アプリケーションをより実行可能にするために、作成した別のキャッシュクラス内にloadBitmap()のインスタンスを配置しました。

Cache.javaの重要なもの(getView()メソッドが最も重要です):

public Cache(int size, int maxWidth, int maxHeight) {
    // Into the constructor you add the maximum pixels
    // that you want to allow in order to not scale images.
    mMaxWidth = maxWidth;
    mMaxHeight = maxHeight;

    mBitmapCache = new LruCache<String, Bitmap>(size) {
        protected int sizeOf(String key, Bitmap b) {
            // Assuming that one pixel contains four bytes.
            return b.getHeight() * b.getWidth() * 4;
        }
    };

    mCurrentTasks = new ArrayList<String>();    
}

/**
 * Gets a bitmap from cache. 
 * If it is not in cache, this method will:
 * 
 * 1: check if the bitmap url is currently being processed in the
 * BitmapLoaderTask and cancel if it is already in a task (a control to see
 * if it's inside the currentTasks list).
 * 
 * 2: check if an internet connection is available and continue if so.
 * 
 * 3: download the bitmap, scale the bitmap if necessary and put it into
 * the memory cache.
 * 
 * 4: Remove the bitmap url from the currentTasks list.
 * 
 * 5: Notify the ListAdapter.
 * 
 * @param mainActivity - Reference to activity object, in order to
 * call notifyDataSetChanged() on the ListAdapter.
 * @param imageKey - The bitmap url (will be the key).
 * @param imageView - The ImageView that should get an
 * available bitmap or a placeholder image.
 * @param isScrolling - If set to true, we skip executing more tasks since
 * the user probably has flinged away the view.
 */
public void loadBitmap(MainActivity mainActivity, 
        String imageKey, ImageView imageView,
        boolean isScrolling) {
    final Bitmap bitmap = getBitmapFromCache(imageKey); 

    if (bitmap != null) {
        imageView.setImageBitmap(bitmap);
    } else {
        imageView.setImageResource(R.drawable.ic_launcher);
        if (!isScrolling && !mCurrentTasks.contains(imageKey) && 
                mainActivity.internetIsAvailable()) {
            BitmapLoaderTask task = new BitmapLoaderTask(imageKey,
                    mainActivity.getAdapter());
            task.execute();
        }
    } 
}

ディスクキャッシュを実装する場合を除き、Cache.javaファイルを編集する必要はありません。

MainActivity.javaの重要なもの:

public void onScrollStateChanged(AbsListView view, int scrollState) {
    if (view.getId() == android.R.id.list) {
        // Set scrolling to true only if the user has flinged the       
        // ListView away, hence we skip downloading a series
        // of unnecessary bitmaps that the user probably
        // just want to skip anyways. If we scroll slowly it
        // will still download bitmaps - that means
        // that the application won't wait for the user
        // to lift its finger off the screen in order to
        // download.
        if (scrollState == SCROLL_STATE_FLING) {
            mIsScrolling = true;
        } else {
            mIsScrolling = false;
            mListAdapter.notifyDataSetChanged();
        }
    } 
}

// Inside ListAdapter...
@Override
public View getView(final int position, View convertView, ViewGroup parent) {           
    View row = convertView;
    final ViewHolder holder;

    if (row == null) {
        LayoutInflater inflater = getLayoutInflater();
        row = inflater.inflate(R.layout.main_listview_row, parent, false);  
        holder = new ViewHolder(row);
        row.setTag(holder);
    } else {
        holder = (ViewHolder) row.getTag();
    }   

    final Row rowObject = getItem(position);

    // Look at the loadBitmap() method description...
    holder.mTextView.setText(rowObject.mText);      
    mCache.loadBitmap(MainActivity.this,
            rowObject.mBitmapUrl, holder.mImageView,
            mIsScrolling);  

    return row;
}

rowObject.mBitmapUrlは非常に頻繁に呼び出されます。通常、行ごとに無限のスレッドを開始しないことを確認するチェックを実装していない場合は、イメージをダウンロードすることはお勧めできません。 Cache.javaは、AsyncTaskがすでにタスクにあるかどうかをチェックし、タスクにある場合は別のタスクを開始しません。したがって、<=>プールのワークキュー制限を超えていない可能性があります。

ダウンロード:

https://www.dropbox.com/sからソースコードをダウンロードできます。 /pvr9zyl811tfeem/ListViewImageCache.zip


最後の言葉:

これを数週間テストしましたが、1つのOOM例外はまだ発生していません。これをエミュレータ、Nexus One、Nexus Sでテストしました。HD品質の画像を含む画像URLをテストしました。唯一のボトルネックは、ダウンロードに時間がかかることです。

OOMが表示されると想像できるシナリオは1つだけです。つまり、非常に大きな画像を多数ダウンロードすると、それらがスケーリングされてキャッシュに入れられる前に、同時により多くのメモリを消費し、 OOM。しかし、それはとにかく理想的な状況でさえなく、より可能性の高い方法で解決することはおそらく不可能でしょう。

コメントのエラーを報告してください! :-)

画像を取得し、その場でサイズ変更するために次のことを行いました。これがお役に立てば幸いです

Bitmap bm;
bm = Bitmap.createScaledBitmap(BitmapFactory.decodeFile(filepath), 100, 100, true);
mPicture = new ImageView(context);
mPicture.setImageBitmap(bm);    

これは非常に長期にわたる問題であり、多くの異なる説明があるようです。ここで提示された最も一般的な2つの回答のアドバイスを受けましたが、どちらもプロセスの decoding 部分を実行するためのバイトを確保できないというVMの問題を解決しませんでした。少し掘り下げた後、ここでの本当の問題は、ネイティブヒープからのデコード処理であることがわかりました。

こちらをご覧ください: BitmapFactory OOMドライブナッツ

それが、この問題に対するいくつかの解決策を見つけた別のディスカッションスレッドに私を導きました。 1つは、画像が表示された後に手動でSystem.gc();を呼び出すことです。ただし、実際には、ネイティブヒープを削減するために、アプリでより多くのメモリを使用します。 2.0(ドーナツ)のリリース時点でのより良い解決策は、BitmapFactoryオプション<!> quot; inPurgeable <!> quot;を使用することです。そこで、単にo2.inPurgeable=true;の直後にo2.inSampleSize=scale;を追加しました。

このトピックの詳細:メモリヒープの制限わずか6M?

今、これらすべてを言ったので、私はJavaとAndroidにも完全な劣等生です。したがって、これがこの問題を解決するひどい方法だと思うなら、おそらく正しいでしょう。 ;-)しかし、これは私にとって驚異的な働きをしており、現在、ヒープキャッシュからVMを実行することは不可能であることがわかりました。私が見つけることができる唯一の欠点は、キャッシュされた描画イメージを破棄していることです。つまり、その画像に右に戻ると、毎回それを再描画していることになります。私のアプリケーションがどのように機能するかについては、それは実際には問題ではありません。走行距離は異なる場合があります。

残念ながら上記のどれも機能しない場合は、これをマニフェストファイルに追加します。 application タグ内

 <application
         android:largeHeap="true"

これを使用bitmap.recycle();これにより、画質の問題がなくなります。

あらゆる種類のスケーリングを必要としない、はるかに効果的なソリューションがあります。ビットマップを1回だけデコードし、その名前に対してマップにキャッシュします。次に、名前に対してビットマップを取得し、ImageViewで設定します。行う必要のあるものはこれ以上ありません。

これは、デコードされたビットマップの実際のバイナリデータがdalvik VMヒープ内に格納されていないため機能します。外部に保存されます。したがって、ビットマップをデコードするたびに、GCによって回収されないVMヒープの外部にメモリが割り当てられます

これをよりよく理解するために、あなたはウルの画像を描画可能なフォルダに保存したと想像してください。 getResources()。getDrwable(R.drawable。)を実行するだけで画像を取得できます。これは毎回画像をデコードするのではなく、呼び出すたびに既にデコードされたインスタンスを再利用します。したがって、本質的にはキャッシュされます。

画像はどこかのファイルにあるため(または外部サーバーから来る場合もあるため)、デコードされたビットマップインスタンスをキャッシュして、必要な場所で再利用する必要があります。

これがお役に立てば幸いです。

次の方法で同じ問題を解決しました。

Bitmap b = null;
Drawable d;
ImageView i = new ImageView(mContext);
try {
    b = Bitmap.createBitmap(320,424,Bitmap.Config.RGB_565);
    b.eraseColor(0xFFFFFFFF);
    Rect r = new Rect(0, 0,320 , 424);
    Canvas c = new Canvas(b);
    Paint p = new Paint();
    p.setColor(0xFFC0C0C0);
    c.drawRect(r, p);
    d = mContext.getResources().getDrawable(mImageIds[position]);
    d.setBounds(r);
    d.draw(c);

    /*   
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inTempStorage = new byte[128*1024];
        b = BitmapFactory.decodeStream(mContext.getResources().openRawResource(mImageIds[position]), null, o2);
        o2.inSampleSize=16;
        o2.inPurgeable = true;
    */
} catch (Exception e) {

}
i.setImageBitmap(b);

が問題です。

  • ビットマップメモリなのVMのヒープではなく、ネイティブデータヒープ]の順序見 BitmapFactory OOM走行うナット
  • ごみ収集のためのネイティブデータヒープ]の順序はlazierのVMのヒープ-ばなければなりませんでしも積極的に行う上で最も難しいのビットマップ.リサイクルおよびビットマップ=nullべての活動のonPauseはonDestroy

これは私のために働いた!

public Bitmap readAssetsBitmap(String filename) throws IOException {
    try {
        BitmapFactory.Options options = new BitmapFactory.Options(); 
        options.inPurgeable = true;
        Bitmap bitmap = BitmapFactory.decodeStream(assets.open(filename), null, options);
        if(bitmap == null) {
            throw new IOException("File cannot be opened: It's value is null");
        } else {
            return bitmap;
        }
    } catch (IOException e) {
        throw new IOException("File cannot be opened: " + e.getMessage());
    }
}

上記の回答のどれも私にとってはうまくいきませんでしたが、問題を解決する恐ろしくい回避策を思い付きました。私は非常に小さな1x1ピクセルの画像をリソースとしてプロジェクトに追加し、ガベージコレクションを呼び出す前にImageViewにロードしました。 ImageViewがビットマップを解放していなかったため、GCがそれを取得しなかった可能性があると思います。いですが、今のところ機能しているようです。

if (bitmap != null)
{
  bitmap.recycle();
  bitmap = null;
}
if (imageView != null)
{
  imageView.setImageResource(R.drawable.tiny); // This is my 1x1 png.
}
System.gc();

imageView.setImageBitmap(...); // Do whatever you need to do to load the image you want.

ここで素晴らしい答えが、この問題に対処するために完全に使用可能なクラスが欲しかったので、私はそれをしました。

これは、OutOfMemoryError証拠である私の BitmapHelperクラスです:-)

import java.io.File;
import java.io.FileInputStream;

import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;

public class BitmapHelper
{

    //decodes image and scales it to reduce memory consumption
    public static Bitmap decodeFile(File bitmapFile, int requiredWidth, int requiredHeight, boolean quickAndDirty)
    {
        try
        {
            //Decode image size
            BitmapFactory.Options bitmapSizeOptions = new BitmapFactory.Options();
            bitmapSizeOptions.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(new FileInputStream(bitmapFile), null, bitmapSizeOptions);

            // load image using inSampleSize adapted to required image size
            BitmapFactory.Options bitmapDecodeOptions = new BitmapFactory.Options();
            bitmapDecodeOptions.inTempStorage = new byte[16 * 1024];
            bitmapDecodeOptions.inSampleSize = computeInSampleSize(bitmapSizeOptions, requiredWidth, requiredHeight, false);
            bitmapDecodeOptions.inPurgeable = true;
            bitmapDecodeOptions.inDither = !quickAndDirty;
            bitmapDecodeOptions.inPreferredConfig = quickAndDirty ? Bitmap.Config.RGB_565 : Bitmap.Config.ARGB_8888;

            Bitmap decodedBitmap = BitmapFactory.decodeStream(new FileInputStream(bitmapFile), null, bitmapDecodeOptions);

            // scale bitmap to mathc required size (and keep aspect ratio)

            float srcWidth = (float) bitmapDecodeOptions.outWidth;
            float srcHeight = (float) bitmapDecodeOptions.outHeight;

            float dstWidth = (float) requiredWidth;
            float dstHeight = (float) requiredHeight;

            float srcAspectRatio = srcWidth / srcHeight;
            float dstAspectRatio = dstWidth / dstHeight;

            // recycleDecodedBitmap is used to know if we must recycle intermediary 'decodedBitmap'
            // (DO NOT recycle it right away: wait for end of bitmap manipulation process to avoid
            // java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@416ee7d8
            // I do not excatly understand why, but this way it's OK

            boolean recycleDecodedBitmap = false;

            Bitmap scaledBitmap = decodedBitmap;
            if (srcAspectRatio < dstAspectRatio)
            {
                scaledBitmap = getScaledBitmap(decodedBitmap, (int) dstWidth, (int) (srcHeight * (dstWidth / srcWidth)));
                // will recycle recycleDecodedBitmap
                recycleDecodedBitmap = true;
            }
            else if (srcAspectRatio > dstAspectRatio)
            {
                scaledBitmap = getScaledBitmap(decodedBitmap, (int) (srcWidth * (dstHeight / srcHeight)), (int) dstHeight);
                recycleDecodedBitmap = true;
            }

            // crop image to match required image size

            int scaledBitmapWidth = scaledBitmap.getWidth();
            int scaledBitmapHeight = scaledBitmap.getHeight();

            Bitmap croppedBitmap = scaledBitmap;

            if (scaledBitmapWidth > requiredWidth)
            {
                int xOffset = (scaledBitmapWidth - requiredWidth) / 2;
                croppedBitmap = Bitmap.createBitmap(scaledBitmap, xOffset, 0, requiredWidth, requiredHeight);
                scaledBitmap.recycle();
            }
            else if (scaledBitmapHeight > requiredHeight)
            {
                int yOffset = (scaledBitmapHeight - requiredHeight) / 2;
                croppedBitmap = Bitmap.createBitmap(scaledBitmap, 0, yOffset, requiredWidth, requiredHeight);
                scaledBitmap.recycle();
            }

            if (recycleDecodedBitmap)
            {
                decodedBitmap.recycle();
            }
            decodedBitmap = null;

            scaledBitmap = null;
            return croppedBitmap;
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }
        return null;
    }

    /**
     * compute powerOf2 or exact scale to be used as {@link BitmapFactory.Options#inSampleSize} value (for subSampling)
     * 
     * @param requiredWidth
     * @param requiredHeight
     * @param powerOf2
     *            weither we want a power of 2 sclae or not
     * @return
     */
    public static int computeInSampleSize(BitmapFactory.Options options, int dstWidth, int dstHeight, boolean powerOf2)
    {
        int inSampleSize = 1;

        // Raw height and width of image
        final int srcHeight = options.outHeight;
        final int srcWidth = options.outWidth;

        if (powerOf2)
        {
            //Find the correct scale value. It should be the power of 2.

            int tmpWidth = srcWidth, tmpHeight = srcHeight;
            while (true)
            {
                if (tmpWidth / 2 < dstWidth || tmpHeight / 2 < dstHeight)
                    break;
                tmpWidth /= 2;
                tmpHeight /= 2;
                inSampleSize *= 2;
            }
        }
        else
        {
            // Calculate ratios of height and width to requested height and width
            final int heightRatio = Math.round((float) srcHeight / (float) dstHeight);
            final int widthRatio = Math.round((float) srcWidth / (float) dstWidth);

            // 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;
    }

    public static Bitmap drawableToBitmap(Drawable drawable)
    {
        if (drawable instanceof BitmapDrawable)
        {
            return ((BitmapDrawable) drawable).getBitmap();
        }

        Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);

        return bitmap;
    }

    public static Bitmap getScaledBitmap(Bitmap bitmap, int newWidth, int newHeight)
    {
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        float scaleWidth = ((float) newWidth) / width;
        float scaleHeight = ((float) newHeight) / height;

        // CREATE A MATRIX FOR THE MANIPULATION
        Matrix matrix = new Matrix();
        // RESIZE THE BIT MAP
        matrix.postScale(scaleWidth, scaleHeight);

        // RECREATE THE NEW BITMAP
        Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, false);
        return resizedBitmap;
    }

}

これは私には有効です。

Bitmap myBitmap;

BitmapFactory.Options options = new BitmapFactory.Options(); 
options.InPurgeable = true;
options.OutHeight = 50;
options.OutWidth = 50;
options.InSampleSize = 4;

File imgFile = new File(filepath);
myBitmap = BitmapFactory.DecodeFile(imgFile.AbsolutePath, options);

これはC#monodroidにあります。 画像のパスを簡単に変更できます。ここで重要なのは、設定するオプションです。

これは、画像をロードおよび処理するためのユーティリティクラスをコミュニティと共有する適切な場所のようです。自由に使用して変更できます。

package com.emil;

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

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

/**
 * A class to load and process images of various sizes from input streams and file paths.
 * 
 * @author Emil http://stackoverflow.com/users/220710/emil
 *
 */
public class ImageProcessing {

    public static Bitmap getBitmap(InputStream stream, int sampleSize, Bitmap.Config bitmapConfig) throws IOException{
        BitmapFactory.Options options=ImageProcessing.getOptionsForSampling(sampleSize, bitmapConfig);
        Bitmap bm = BitmapFactory.decodeStream(stream,null,options);
        if(ImageProcessing.checkDecode(options)){
            return bm;
        }else{
            throw new IOException("Image decoding failed, using stream.");
        }
    }

    public static Bitmap getBitmap(String imgPath, int sampleSize, Bitmap.Config bitmapConfig) throws IOException{
        BitmapFactory.Options options=ImageProcessing.getOptionsForSampling(sampleSize, bitmapConfig);
        Bitmap bm = BitmapFactory.decodeFile(imgPath,options);
        if(ImageProcessing.checkDecode(options)){
            return bm;
        }else{
            throw new IOException("Image decoding failed, using file path.");
        }
    }

    public static Dimensions getDimensions(InputStream stream) throws IOException{
        BitmapFactory.Options options=ImageProcessing.getOptionsForDimensions();
        BitmapFactory.decodeStream(stream,null,options);
        if(ImageProcessing.checkDecode(options)){
            return new ImageProcessing.Dimensions(options.outWidth,options.outHeight);
        }else{
            throw new IOException("Image decoding failed, using stream.");
        }
    }

    public static Dimensions getDimensions(String imgPath) throws IOException{
        BitmapFactory.Options options=ImageProcessing.getOptionsForDimensions();
        BitmapFactory.decodeFile(imgPath,options);
        if(ImageProcessing.checkDecode(options)){
            return new ImageProcessing.Dimensions(options.outWidth,options.outHeight);
        }else{
            throw new IOException("Image decoding failed, using file path.");
        }
    }

    private static boolean checkDecode(BitmapFactory.Options options){
        // Did decode work?
        if( options.outWidth<0 || options.outHeight<0 ){
            return false;
        }else{
            return true;
        }
    }

    /**
     * Creates a Bitmap that is of the minimum dimensions necessary
     * @param bm
     * @param min
     * @return
     */
    public static Bitmap createMinimalBitmap(Bitmap bm, ImageProcessing.Minimize min){
        int newWidth, newHeight;
        switch(min.type){
        case WIDTH:
            if(bm.getWidth()>min.minWidth){
                newWidth=min.minWidth;
                newHeight=ImageProcessing.getScaledHeight(newWidth, bm);
            }else{
                // No resize
                newWidth=bm.getWidth();
                newHeight=bm.getHeight();
            }
            break;
        case HEIGHT:
            if(bm.getHeight()>min.minHeight){
                newHeight=min.minHeight;
                newWidth=ImageProcessing.getScaledWidth(newHeight, bm);
            }else{
                // No resize
                newWidth=bm.getWidth();
                newHeight=bm.getHeight();
            }
            break;
        case BOTH: // minimize to the maximum dimension
        case MAX:
            if(bm.getHeight()>bm.getWidth()){
                // Height needs to minimized
                min.minDim=min.minDim!=null ? min.minDim : min.minHeight;
                if(bm.getHeight()>min.minDim){
                    newHeight=min.minDim;
                    newWidth=ImageProcessing.getScaledWidth(newHeight, bm);
                }else{
                    // No resize
                    newWidth=bm.getWidth();
                    newHeight=bm.getHeight();
                }
            }else{
                // Width needs to be minimized
                min.minDim=min.minDim!=null ? min.minDim : min.minWidth;
                if(bm.getWidth()>min.minDim){
                    newWidth=min.minDim;
                    newHeight=ImageProcessing.getScaledHeight(newWidth, bm);
                }else{
                    // No resize
                    newWidth=bm.getWidth();
                    newHeight=bm.getHeight();
                }
            }
            break;
        default:
            // No resize
            newWidth=bm.getWidth();
            newHeight=bm.getHeight();
        }
        return Bitmap.createScaledBitmap(bm, newWidth, newHeight, true);
    }

    public static int getScaledWidth(int height, Bitmap bm){
        return (int)(((double)bm.getWidth()/bm.getHeight())*height);
    }

    public static int getScaledHeight(int width, Bitmap bm){
        return (int)(((double)bm.getHeight()/bm.getWidth())*width);
    }

    /**
     * Get the proper sample size to meet minimization restraints
     * @param dim
     * @param min
     * @param multipleOf2 for fastest processing it is recommended that the sample size be a multiple of 2
     * @return
     */
    public static int getSampleSize(ImageProcessing.Dimensions dim, ImageProcessing.Minimize min, boolean multipleOf2){
        switch(min.type){
        case WIDTH:
            return ImageProcessing.getMaxSampleSize(dim.width, min.minWidth, multipleOf2);
        case HEIGHT:
            return ImageProcessing.getMaxSampleSize(dim.height, min.minHeight, multipleOf2);
        case BOTH:
            int widthMaxSampleSize=ImageProcessing.getMaxSampleSize(dim.width, min.minWidth, multipleOf2);
            int heightMaxSampleSize=ImageProcessing.getMaxSampleSize(dim.height, min.minHeight, multipleOf2);
            // Return the smaller of the two
            if(widthMaxSampleSize<heightMaxSampleSize){
                return widthMaxSampleSize;
            }else{
                return heightMaxSampleSize;
            }
        case MAX:
            // Find the larger dimension and go bases on that
            if(dim.width>dim.height){
                return ImageProcessing.getMaxSampleSize(dim.width, min.minDim, multipleOf2);
            }else{
                return ImageProcessing.getMaxSampleSize(dim.height, min.minDim, multipleOf2);
            }
        }
        return 1;
    }

    public static int getMaxSampleSize(int dim, int min, boolean multipleOf2){
        int add=multipleOf2 ? 2 : 1;
        int size=0;
        while(min<(dim/(size+add))){
            size+=add;
        }
        size = size==0 ? 1 : size;
        return size;        
    }

    public static class Dimensions {
        int width;
        int height;

        public Dimensions(int width, int height) {
            super();
            this.width = width;
            this.height = height;
        }

        @Override
        public String toString() {
            return width+" x "+height;
        }
    }

    public static class Minimize {
        public enum Type {
            WIDTH,HEIGHT,BOTH,MAX
        }
        Integer minWidth;
        Integer minHeight;
        Integer minDim;
        Type type;

        public Minimize(int min, Type type) {
            super();
            this.type = type;
            switch(type){
            case WIDTH:
                this.minWidth=min;
                break;
            case HEIGHT:
                this.minHeight=min;
                break;
            case BOTH:
                this.minWidth=min;
                this.minHeight=min;
                break;
            case MAX:
                this.minDim=min;
                break;
            }
        }

        public Minimize(int minWidth, int minHeight) {
            super();
            this.type=Type.BOTH;
            this.minWidth = minWidth;
            this.minHeight = minHeight;
        }

    }

    /**
     * Estimates size of Bitmap in bytes depending on dimensions and Bitmap.Config
     * @param width
     * @param height
     * @param config
     * @return
     */
    public static long estimateBitmapBytes(int width, int height, Bitmap.Config config){
        long pixels=width*height;
        switch(config){
        case ALPHA_8: // 1 byte per pixel
            return pixels;
        case ARGB_4444: // 2 bytes per pixel, but depreciated
            return pixels*2;
        case ARGB_8888: // 4 bytes per pixel
            return pixels*4;
        case RGB_565: // 2 bytes per pixel
            return pixels*2;
        default:
            return pixels;
        }
    }

    private static BitmapFactory.Options getOptionsForDimensions(){
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds=true;
        return options;
    }

    private static BitmapFactory.Options getOptionsForSampling(int sampleSize, Bitmap.Config bitmapConfig){
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = false;
        options.inDither = false;
        options.inSampleSize = sampleSize;
        options.inScaled = false;
        options.inPreferredConfig = bitmapConfig;
        return options;
    }
}

アプリケーションの1つで、Camera/Galleryから写真を撮る必要があります。ユーザーがカメラから画像をクリックすると(2MP、5MP、または8MP)、画像サイズはkB sからMB sに変わります。画像サイズが正常に機能するコードよりも小さい(または最大1-2MB)場合、4MBまたは5MBを超えるサイズの画像がある場合は、OOMがフレームに入ります:(

この問題の解決に取り組んだ<!> amp;最後に、Fedorの(このような素晴らしいソリューションを作成したFedorの功績)コードを以下のように改善しました:)

private Bitmap decodeFile(String fPath) {
    // Decode image size
    BitmapFactory.Options opts = new BitmapFactory.Options();
    /*
     * If set to true, the decoder will return null (no bitmap), but the
     * out... fields will still be set, allowing the caller to query the
     * bitmap without having to allocate the memory for its pixels.
     */
    opts.inJustDecodeBounds = true;
    opts.inDither = false; // Disable Dithering mode
    opts.inPurgeable = true; // Tell to gc that whether it needs free
                                // memory, the Bitmap can be cleared
    opts.inInputShareable = true; // Which kind of reference will be used to
                                    // recover the Bitmap data after being
                                    // clear, when it will be used in the
                                    // future

    BitmapFactory.decodeFile(fPath, opts);

    // The new size we want to scale to
    final int REQUIRED_SIZE = 70;

    // Find the correct scale value. 
    int scale = 1;

    if (opts.outHeight > REQUIRED_SIZE || opts.outWidth > REQUIRED_SIZE) {

        // Calculate ratios of height and width to requested height and width
        final int heightRatio = Math.round((float) opts.outHeight
                / (float) REQUIRED_SIZE);
        final int widthRatio = Math.round((float) opts.outWidth
                / (float) REQUIRED_SIZE);

        // 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.
        scale = heightRatio < widthRatio ? heightRatio : widthRatio;//
    }

    // Decode bitmap with inSampleSize set
    opts.inJustDecodeBounds = false;

    opts.inSampleSize = scale;

    Bitmap bm = BitmapFactory.decodeFile(fPath, opts).copy(
            Bitmap.Config.RGB_565, false);

    return bm;

}

これが、同じ問題に直面している仲間に役立つことを願っています!

詳細については、 this

数分前にこの問題に遭遇しました。リストビューアダプターの管理を改善することで解決しました。使用している数百の50x50px画像の問題だと思いましたが、行が表示されるたびにカスタムビューを拡大しようとしていたことがわかりました。行が膨らんだかどうかを確認するだけでこのエラーを解消し、数百のビットマップを使用しています。これは実際にはスピナー用ですが、ベースアダプターはListViewでも同じように機能します。この単純な修正により、アダプターのパフォーマンスも大幅に改善されました。

@Override
public View getView(final int position, View convertView, final ViewGroup parent) {

    if(convertView == null){
        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = inflater.inflate(R.layout.spinner_row, null);
    }
...

これらのソリューションをテストするのに丸1日を費やしましたが、画像を取得してGCを手動で呼び出すための上記のアプローチしかありませんでした。アクティビティ間の切り替えを重い負荷テストの下にアプリを置いたときにうまくいったこと。私のアプリにはリストビューにサムネイル画像のリストがあり(アクティビティAと言います)、それらの画像のいずれかをクリックすると、そのアイテムのメイン画像を表示する別のアクティビティ(アクティビティBと言います)に移動します。 2つのアクティビティを切り替えると、最終的にOOMエラーが発生し、アプリが強制的に終了します。

リストビューの途中まで行くとクラッシュします。

今、アクティビティBで以下を実装すると、リストビュー全体を問題なく通過し、行き来し続けることができます...

@Override
public void onDestroy()
{   
    Cleanup();
    super.onDestroy();
}

private void Cleanup()
{    
    bitmap.recycle();
    System.gc();
    Runtime.getRuntime().gc();  
}

この問題は、Androidエミュレーターでのみ発生します。エミュレーターでもこの問題に直面しましたが、デバイスをチェックインすると正常に機能しました。

したがって、デバイスをチェックインしてください。デバイスで実行できます。

My 2 cents: i solved my OOM errors with bitmaps by:

a) scaling my images by a factor of 2

b) using Picasso library in my custom Adapter for a ListView, with a one-call in getView like this: Picasso.with(context).load(R.id.myImage).into(R.id.myImageView);

use these code for every image in select from SdCard or drewable to convert bitmap object.

Resources res = getResources();
WindowManager window = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
Display display = window.getDefaultDisplay();
@SuppressWarnings("deprecation")
int width = display.getWidth();
@SuppressWarnings("deprecation")
int height = display.getHeight();
try {
    if (bitmap != null) {
        bitmap.recycle();
        bitmap = null;
        System.gc();
    }
    bitmap = Bitmap.createScaledBitmap(BitmapFactory
        .decodeFile(ImageData_Path.get(img_pos).getPath()),
        width, height, true);
} catch (OutOfMemoryError e) {
    if (bitmap != null) {
        bitmap.recycle();
        bitmap = null;
        System.gc();
    }
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inPreferredConfig = Config.RGB_565;
    options.inSampleSize = 1;
    options.inPurgeable = true;
    bitmapBitmap.createScaledBitmap(BitmapFactory.decodeFile(ImageData_Path.get(img_pos)
        .getPath().toString(), options), width, height,true);
}
return bitmap;

use your image path instend of ImageData_Path.get(img_pos).getPath() .

All the solutions here require setting a IMAGE_MAX_SIZE. This limits devices with more powerful hardware and if the image size is too low it looks ugly on the HD screen.

I came out with a solution that works with my Samsung Galaxy S3 and several other devices including less powerful ones, with better image quality when a more powerful device is used.

The gist of it is to calculate the maximum memory allocated for the app on a particular device, then set the scale to be lowest possible without exceeding this memory. Here's the code:

public static Bitmap decodeFile(File f)
{
    Bitmap b = null;
    try
    {
        // Decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;

        FileInputStream fis = new FileInputStream(f);
        try
        {
            BitmapFactory.decodeStream(fis, null, o);
        }
        finally
        {
            fis.close();
        }

        // In Samsung Galaxy S3, typically max memory is 64mb
        // Camera max resolution is 3264 x 2448, times 4 to get Bitmap memory of 30.5mb for one bitmap
        // If we use scale of 2, resolution will be halved, 1632 x 1224 and x 4 to get Bitmap memory of 7.62mb
        // We try use 25% memory which equals to 16mb maximum for one bitmap
        long maxMemory = Runtime.getRuntime().maxMemory();
        int maxMemoryForImage = (int) (maxMemory / 100 * 25);

        // Refer to
        // http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
        // A full screen GridView filled with images on a device with
        // 800x480 resolution would use around 1.5MB (800*480*4 bytes)
        // When bitmap option's inSampleSize doubled, pixel height and
        // weight both reduce in half
        int scale = 1;
        while ((o.outWidth / scale) * (o.outHeight / scale) * 4 > maxMemoryForImage)
        scale *= 2;

        // Decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        fis = new FileInputStream(f);
        try
        {
            b = BitmapFactory.decodeStream(fis, null, o2);
        }
        finally
        {
            fis.close();
        }
    }
    catch (IOException e)
    {
    }
    return b;
}

I set the maximum memory used by this bitmap to be 25% of maximum allocated memory, you may need to adjust this to your needs and make sure this bitmap is cleaned up and don't stay in memory when you've finished using it. Typically I use this code to perform image rotation (source and destination bitmap) so my app needs to load 2 bitmaps in memory at the same time, and 25% gives me a good buffer without running out of memory when performing image rotation.

Hope this helps someone out there..

Such OutofMemoryException cannot be totally resolved by calling the System.gc() and so on .

By referring to the Activity Life Cycle

The Activity States are determined by the OS itself subject to the memory usage for each process and the priority of each process.

You may consider the size and the resolution for each of the bitmap pictures used. I recommend to reduce the size ,resample to lower resolution , refer to the design of galleries (one small picture PNG , and one original picture.)

Generally android device heap size is only 16MB (varies from device/OS see post Heap Sizes), if you are loading the images and it crosses the size of 16MB , it will throw out of memory exception, instead of using the Bitmap for , loading images from SD card or from resources or even from network try to using getImageUri , loading bitmap require more memory , or you can set bitmap to null if your work done with that bitmap.

This code will help to load large bitmap from drawable

public class BitmapUtilsTask extends AsyncTask<Object, Void, Bitmap> {

    Context context;

    public BitmapUtilsTask(Context context) {
        this.context = context;
    }

    /**
     * Loads a bitmap from the specified url.
     * 
     * @param url The location of the bitmap asset
     * @return The bitmap, or null if it could not be loaded
     * @throws IOException
     * @throws MalformedURLException
     */
    public Bitmap getBitmap() throws MalformedURLException, IOException {       

        // Get the source image's dimensions
        int desiredWidth = 1000;
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;

        BitmapFactory.decodeResource(context.getResources(), R.drawable.green_background , options);

        int srcWidth = options.outWidth;
        int srcHeight = options.outHeight;

        // Only scale if the source is big enough. This code is just trying
        // to fit a image into a certain width.
        if (desiredWidth > srcWidth)
            desiredWidth = srcWidth;

        // Calculate the correct inSampleSize/scale value. This helps reduce
        // memory use. It should be a power of 2
        int inSampleSize = 1;
        while (srcWidth / 2 > desiredWidth) {
            srcWidth /= 2;
            srcHeight /= 2;
            inSampleSize *= 2;
        }
        // Decode with inSampleSize
        options.inJustDecodeBounds = false;
        options.inDither = false;
        options.inSampleSize = inSampleSize;
        options.inScaled = false;
        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
        options.inPurgeable = true;
        Bitmap sampledSrcBitmap;

        sampledSrcBitmap =  BitmapFactory.decodeResource(context.getResources(), R.drawable.green_background , options);

        return sampledSrcBitmap;
    }

    /**
     * The system calls this to perform work in a worker thread and delivers
     * it the parameters given to AsyncTask.execute()
     */
    @Override
    protected Bitmap doInBackground(Object... item) {
        try { 
          return getBitmap();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top