web-dev-qa-db-ja.com

AndroidのJava.lang.OutOfMemoryErrorトラブルを解決する方法

描画可能なフォルダーに非常に小さなサイズの画像がありますが、ユーザーからこのエラーが発生しています。また、コードでビットマップ関数を使用していません。少なくとも意図的に:)

Java.lang.OutOfMemoryError
    at Android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
    at Android.graphics.BitmapFactory.decodeStream(BitmapFactory.Java:683)
    at Android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.Java:513)
    at Android.graphics.drawable.Drawable.createFromResourceStream(Drawable.Java:889)
    at Android.content.res.Resources.loadDrawable(Resources.Java:3436)
    at Android.content.res.Resources.getDrawable(Resources.Java:1909)
    at Android.view.View.setBackgroundResource(View.Java:16251)
    at com.autkusoytas.bilbakalim.SoruEkrani.cevapSecimi(SoruEkrani.Java:666)
    at com.autkusoytas.bilbakalim.SoruEkrani$9$1.run(SoruEkrani.Java:862)
    at Android.os.Handler.handleCallback(Handler.Java:733)
    at Android.os.Handler.dispatchMessage(Handler.Java:95)
    at Android.os.Looper.loop(Looper.Java:146)
    at Android.app.ActivityThread.main(ActivityThread.Java:5602)
    at Java.lang.reflect.Method.invokeNative(Native Method)
    at Java.lang.reflect.Method.invoke(Method.Java:515)
    at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.Java:1283)
    at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:1099)
    at dalvik.system.NativeStart.main(Native Method)

このstackTraceによると、私はこの行でこのエラーを取得しています(「tv」はtextViewです):

tv.setBackgroundResource(R.drawable.yanlis);

何が問題ですか?コードに関する他の情報が必要な場合は、追加できます。ありがとう!

64
Utku Soytaş

ヒープサイズを動的に増やすことはできませんが、使用してさらに使用するように要求できます。

Android:largeHeap = "true"

manifest.xmlで、マニフェストにこれらの行を追加して、いくつかの状況で機能するようにすることができます。

<application
    Android:allowBackup="true"
    Android:icon="@mipmap/ic_launcher"
    Android:label="@string/app_name"
    Android:largeHeap="true"
    Android:supportsRtl="true"
    Android:theme="@style/AppTheme">

アプリケーションのプロセスを大きなDalvikヒープで作成するかどうか。これは、アプリケーション用に作成されたすべてのプロセスに適用されます。プロセスにロードされた最初のアプリケーションにのみ適用されます。共有ユーザーIDを使用して複数のアプリケーションがプロセスを使用できるようにしている場合、すべてのアプリケーションが一貫してこのオプションを使用する必要があります。そうしないと、予測できない結果が生じます。ほとんどのアプリはこれを必要とせず、代わりに全体的なメモリ使用量を減らしてパフォーマンスを向上させることに焦点を当てる必要があります。一部のデバイスは使用可能なメモリの合計によって制約されるため、これを有効にしても、使用可能なメモリが一定に増加することは保証されません。


実行時に使用可能なメモリサイズを照会するには、getMemoryClass()またはgetLargeMemoryClass()メソッドを使用します。

まだ問題に直面している場合、これも動作するはずです

 BitmapFactory.Options options = new BitmapFactory.Options();
 options.inSampleSize = 8;
 mBitmapInsurance = BitmapFactory.decodeFile(mCurrentPhotoPath,options);

値> 1に設定した場合、元の画像をサブサンプリングするようデコーダーに要求し、メモリを節約するためにより小さい画像を返します。

これは、画像の表示速度に関して、BitmapFactory.Options.inSampleSizeの最適な使用方法です。ドキュメントでは、2の累乗の値を使用することに言及しているため、2、4、8、16などで作業しています。

画像のサンプリングをさらに深くします。

たとえば、最終的にImageViewの128x128ピクセルのサムネイルに表示される場合、1024x768ピクセルの画像をメモリにロードする価値はありません。

デコーダーに画像をサブサンプリングし、より小さなバージョンをメモリに読み込むように指示するには、BitmapFactory.OptionsオブジェクトでinSampleSizetrueに設定します。たとえば、inSampleSizeが4でデコードされた解像度2100 x 1500ピクセルの画像は、約512x384のビットマップを生成します。これをメモリにロードすると、フルイメージに12MBではなく0.75MBが使用されます(ARGB_8888のビットマップ構成を想定)。これは、ターゲットの幅と高さに基づいて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;
}

inSampleSizeドキュメンテーションに従って、デコーダは最も近い2のべき乗に切り捨てて最終値を使用するため、2のべき乗の値が計算されます。

このメソッドを使用するには、最初にinJustDecodeBoundstrueに設定してデコードし、オプションを渡してから、新しいinSampleSize値とinJustDecodeBoundsfalseに設定して再度デコードします。

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ピクセルのサムネイルを表示するImageViewに任意のサイズのビットマップを簡単にロードできます。

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

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


私はこのコードも面白いと感じました:

private Bitmap getBitmap(String path) {

Uri uri = getImageUri(path);
InputStream in = null;
try {
    final int IMAGE_MAX_SIZE = 1200000; // 1.2MP
    in = mContentResolver.openInputStream(uri);

    // Decode image size
    BitmapFactory.Options o = new BitmapFactory.Options();
    o.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(in, null, o);
    in.close();

    int scale = 1;
    while ((o.outWidth * o.outHeight) * (1 / Math.pow(scale, 2)) > 
          IMAGE_MAX_SIZE) {
       scale++;
    }
    Log.d(TAG, "scale = " + scale + ", orig-width: " + o.outWidth + ", 
       orig-height: " + o.outHeight);

    Bitmap bitmap = null;
    in = mContentResolver.openInputStream(uri);
    if (scale > 1) {
        scale--;
        // scale to max possible inSampleSize that still yields an image
        // larger than target
        o = new BitmapFactory.Options();
        o.inSampleSize = scale;
        bitmap = BitmapFactory.decodeStream(in, null, o);

        // resize to desired dimensions
        int height = bitmap.getHeight();
        int width = bitmap.getWidth();
        Log.d(TAG, "1th scale operation dimenions - width: " + width + ",
           height: " + height);

        double y = Math.sqrt(IMAGE_MAX_SIZE
                / (((double) width) / height));
        double x = (y / height) * width;

        Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, (int) x, 
           (int) y, true);
        bitmap.recycle();
        bitmap = scaledBitmap;

        System.gc();
    } else {
        bitmap = BitmapFactory.decodeStream(in);
    }
    in.close();

    Log.d(TAG, "bitmap size - width: " +bitmap.getWidth() + ", height: " + 
       bitmap.getHeight());
    return bitmap;
} catch (IOException e) {
    Log.e(TAG, e.getMessage(),e);
    return null;
}

アプリのメモリを管理する方法: link


使用するのは得策ではありませんAndroid:largeHeap="true"ここにそれを説明するGoogleからの抜粋があります、

ただし、大きなヒープを要求する機能は、RAM(より大きな写真編集アプリなど)をさらに消費する必要性を正当化できる少数のアプリのみを対象としています。単にメモリが不足しているため簡単に修正する必要があるため、大きなヒープを要求しないでください。メモリをすべて割り当てている場所と保持する必要がある理由がわかっている場合にのみ使用してください。それでも、アプリが大きなヒープを正当化できると確信している場合でも、可能な限りリクエストすることは避けてください。タスクの切り替えやその他の一般的な操作を実行すると、ガベージコレクションに時間がかかり、システムパフォーマンスが低下する可能性があるため、余分なメモリを使用するとユーザーエクスペリエンス全体がますます損なわれます。

out of memory errorsで極度に作業した後、これをマニフェストに追加してoom問題を回避することは罪ではない


Androidランタイム(ART)でのアプリの動作の検証

Androidランタイム(ART)は、Android 5.0(APIレベル21)以降を実行しているデバイスのデフォルトのランタイムです。このランタイムは、Androidプラットフォームとアプリのパフォーマンスとスムーズさを改善する多くの機能を提供します。 ARTの新機能の詳細については、 Introducing ART をご覧ください。

ただし、Dalvikで機能する一部のテクニックはARTでは機能しません。このドキュメントでは、既存のアプリをARTと互換性を持つように移行する際に注意すべき事項について説明します。ほとんどのアプリは、ARTで実行する場合にのみ機能するはずです。


ガベージコレクション(GC)の問題に対処する

Dalvikの下では、アプリは頻繁にSystem.gc()を明示的に呼び出してガベージコレクション(GC)を呼び出すと便利です。これは、特にGC_FOR_ALLOCタイプの発生を防ぐため、または断片化を減らすためにガベージコレクションを呼び出す場合、ARTではそれほど必要ではありません。 System.getProperty( "Java.vm.version")を呼び出すことにより、使用中のランタイムを確認できます。 ARTが使用されている場合、プロパティの値は「2.0.0」以上です。

さらに、メモリ管理を改善するために、圧縮ガベージコレクタがAndroid Open-Source Project(AOSP)で開発中です。このため、GCの圧縮と互換性のない手法(オブジェクトインスタンスデータへのポインターの保存など)の使用は避けてください。これは、Java Native Interface(JNI)を使用するアプリにとって特に重要です。詳細については、「JNI問題の防止」を参照してください。


JNI問題の防止

ARTのJNIはDalvikのJNIよりもいくらか厳格です。一般的な問題をキャッチするには、CheckJNIモードを使用することを特にお勧めします。アプリでC/C++コードを使用する場合は、次の記事を確認してください。


また、ネイティブメモリ( NDKJNI )を使用できるため、実際にヒープサイズの制限を回避できます。

これについての投稿がいくつかあります。

そして、それはそれのために作られたライブラリです:

130
Maveňツ

次の2つのオプションのみが表示されます。

  1. アプリケーションでメモリリークが発生しています。
  2. アプリケーションの実行時には、デバイスに十分なメモリがありません。
3
Cativail

ビットマップを扱うときは、LRUキャッシュマネージャーを実装する必要があります

http://developer.Android.com/reference/Android/util/LruCache.htmlhttp://developer.Android.com/training/displaying-bitmaps/cache-bitmap。 htmlLRUCacheを使用してビットマップをリサイクルする必要があるのはいつですか?

OR

Universal Image Loaderのような層ライブラリを使用します。

https://github.com/nostra13/Android-Universal-Image-Loader

編集:

今では画像を扱うとき、ほとんどの場合、ビットマップを使用して、Glideを使用します。これにより、GlideモジュールとLRUCacheを構成できます。

https://github.com/bumptech/glide

3
An-droid

Androidアプリのこのようなエラー/例外を処理するためのヒントはほとんどありません。

  1. アクティビティとアプリケーションには次のようなメソッドがあります。

    • onLowMemory
    • onTrimMemoryこれらのメソッドを処理して、メモリ使用量を監視します。
  2. マニフェストのタグでは、属性 'largeHeap'をTRUEに設定できます。これは、アプリサンドボックスのヒープをさらに要求します。

  3. インメモリキャッシュとディスクキャッシュの管理:

    • 画像やその他のデータは、アプリの実行中にメモリ内にキャッシュされる可能性がありました(ローカルでアクティビティ/フラグメントに、グローバルに)。管理または削除する必要があります。
  4. WeakReference、Javaインスタンス作成のSoftReference、特にファイルの使用。

  5. イメージが非常に多い場合は、メモリを管理できる適切なライブラリ/データ構造を使用し、ロードされたイメージのサムリングを使用して、ディスクキャッシュを処理します。

  6. OutOfMemory例外を処理する

  7. コーディングのベストプラクティスに従う

    • 記憶漏れ(すべてを強い参照で保持しないでください)
  8. アクティビティスタックを最小化します。スタック内のアクティビティの数(コンテキスト/アクティビティのすべてを保持しないでください)

    • コンテキストは理にかなっています。これらのデータ/インスタンスはスコープ(アクティビティとフラグメント)の範囲外である必要はなく、グローバルな参照保持ではなく適切なコンテキストに保持されます。
  9. 静的変数の使用を最小限に抑え、さらに多くのシングルトンを使用します。

  10. OSの基本的なメモリ基盤の世話をします

    • メモリの断片化の問題
  11. インメモリキャッシュが不要になったことが確実な場合、GC.Collect()を手動で実行することがあります。

2

このエラーJava.lang.OutOfMemoryErrorが発生している場合、これはAndroidで発生する最も一般的な問題です。このエラーは、メモリスペース不足のためにオブジェクトを割り当てることができない場合に、Java仮想マシン(JVM)によってスローされます。

このAndroid:hardwareAccelerated="false" , Android:largeHeap="true"をアプリケーションの下のmanifest.xmlファイルで次のように試してください。

<application
  Android:name=".MyApplication"
  Android:allowBackup="true"
  Android:icon="@mipmap/ic_launcher"
  Android:label="@string/app_name"
  Android:theme="@style/AppTheme"
  Android:hardwareAccelerated="false"
  Android:largeHeap="true" />
1
Gaurav Lambole