4

Androidアプリを約4〜5回最小化すると、常に次のエラーが発生します。

02-01 19:24:11.980: E/dalvikvm-heap(22362): Out of memory on a 3686416-byte allocation.
02-01 19:24:12.000: E/dalvikvm(22362): Out of memory: Heap Size=62755KB, Allocated=55237KB, Limit=65536KB
02-01 19:24:12.000: E/dalvikvm(22362): Extra info: Footprint=62435KB, Allowed Footprint=62755KB, Trimmed=2144KB
02-01 19:24:12.000: E/Bitmap_JNI(22362): Create Bitmap Failed.    
02-01 19:24:12.000: E/Bitmap_JNI(22362): Failed to create SkBitmap!
02-01 19:24:12.000: E/AndroidRuntime(22362): FATAL EXCEPTION: main
02-01 19:24:12.000: E/AndroidRuntime(22362): java.lang.OutOfMemoryError: (Heap Size=62755KB, Allocated=55237KB)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at android.graphics.Bitmap.nativeCreateScaledBitmap(Native Method)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:744)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at de.vauge.mb.Utils.getResizedBitmap(Utils.java:56)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at de.vauge.mb.MenuView.initialize(MenuView.java:74)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at de.vauge.mb.MenuView$1.handleMessage(MenuView.java:137)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at android.os.Handler.dispatchMessage(Handler.java:99)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at android.os.Looper.loop(Looper.java:156)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at android.app.ActivityThread.main(ActivityThread.java:5045)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at java.lang.reflect.Method.invokeNative(Native Method)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at java.lang.reflect.Method.invoke(Method.java:511)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
02-01 19:24:12.000: E/AndroidRuntime(22362):    at dalvik.system.NativeStart.main(Native Method)

私のアプリは、7つの異なる自作のビュー(すべてにいくつかのビットマップが含まれています)を含む1つのアクティビティのみで構成されており、不要な場合は非表示に切り替えられます(おそらくスタイルは良くありませんが、今まではうまくいきました...)。これらのビューのすべてに、使用されているすべてのビットマップをリサイクルするdestroy()関数があり、MainActivityのonDestroy()はそれらすべてのdestroy()関数を呼び出します。さらに、静的ビットマップは使用しませんでした。

それで、すべてのビットマップをリサイクルし、静的ビットマップを使用しないことに加えて、他に試すことができるものはありますか?

4

4 に答える 4

9

上手。Androidのビットマップは少し注意が必要です。ビットマップのソースとそのサイズに関するより良い情報を提供できますか?

それ以外の場合は、次のことを調べることをお勧めします。

  1. リモート画像をロードしている場合は、frescoをチェックしてください。Picassoをチェックすることもできます。個人的にはImageLoaderが好きでしたが、現在はメンテナンスされていません。

  2. 以前は推奨オプションであったinPurgableフラグを使用している場合は、実際には各イメージにより多くのメモリが割り当てられるため、このフラグを回避する方法を見つけてください。

  3. 小さなローカルアセットを頻繁にデコードする場合は、ドローアブルをハッシュマップに保存し、必要に応じて再利用することを検討してください。より少ないGC。

  4. アプリケーションをサブクラス化する場合は、OnLowMemory呼び出しを使用して、いつ本当にクリーンアップする必要があるかを知ることができます(実際の状況ではなく、デバッグに最適です)...手遅れでない場合... :)

  5. ChrisBanesのブログをご覧ください。これは非常に興味深いメモリキャッシュソリューションです

  6. 必要かつ可能な場合はいつでも呼び出すメモリトリマーを実装します。

  7. もう1つの驚くべき最適化は、可能な場合は小さいオブジェクトを使用することです...最小限のデータモデルと画像サイズを考えて、それらに準拠するAPIを用意してください。

于 2013-02-01T19:56:43.043 に答える
1

ベンマックスのコメントからの#3のために私は2つの有用なクラスを作りました:

public abstract class SoftReferenceStorage<K, V>{
private static HashMap<Object, SoftReference<Object>> objectsHash = new HashMap<Object, SoftReference<Object>>();

@SuppressWarnings("unchecked")
public V get(K key) {
    if (objectsHash.containsKey(key)) {
        SoftReference<Object> ref = objectsHash.get(key);
        if (ref.get() == null) {
            objectsHash.put(key, new SoftReference<Object>(createValueForKey(key)));
            return (V)objectsHash.get(key).get();
        } else {
            return (V)ref.get();
        }
    } else {
        objectsHash.put(key, new SoftReference<Object>(createValueForKey(key)));
        return (V)objectsHash.get(key).get();
    }
}

protected abstract V createValueForKey(K key);
}

public class FrequentlyUsedBitmapResources extends SoftReferenceStorage<Integer, Bitmap>{
private static FrequentlyUsedBitmapResources instance = null;

private Resources resources;

public FrequentlyUsedBitmapResources(Resources resources) {
    super();
    this.resources = resources;
}

public static FrequentlyUsedBitmapResources getInstance() {
    if (instance == null) {
        instance = new FrequentlyUsedBitmapResources(HiDriveApp.getContext().getResources());
    }
    return instance;
}

@Override
protected Bitmap createValueForKey(Integer resId) {
    return BitmapFactory.decodeResource(resources, resId);
}
}

次のように使用できます:

Bitmap b = FrequentlyUsedBitmapResources.getInstance().get(R.drawable.overview_photo_placeholder);
于 2013-09-03T14:31:04.297 に答える
0

またはonCreate()ではなく、にロードしていることを確認してください。再開するたびにリロードされているように見えますが、アプリを最小化したときに呼び出されないため、破棄されていません。onStart()onResume()onDestroy()

于 2013-02-01T18:37:47.237 に答える
0

画像がローカルデバイス上にある場合(つまり、コードに組み込まれているか、ユーザー画像ライブラリから取得されている場合)、画像を単に非表示にするかどうかではなく、ディスクからその場で取り込むことを選択できます。結局のところ、これらのデバイスはすべて基本的にフラッシュベースであり、スピンドルディスクと比較して非常に高速です。ほとんどの場合、ユーザーはイメージのディスクIOのパフォーマンスへの影響を感じることができません。

この方法でも、一度にメモリに保持する画像の量を制限します。

ティムの評価も検討する必要があることに同意します。

于 2013-02-01T18:38:19.273 に答える