すべてのビットマップが適切にスケーリングされていることなどを確認した後でも、厄介なOutOfMemoryErrors
に直面しています。実際、この問題はビットマップにまったく関連していないようですが、間違っている可能性があります。
テストとエラー分離の目的で、ナビゲーションドロワー([戻る]ボタンを使用しない)を使用して、2つのアクティビティ(メインとリストと呼びます)を切り替えています。 DDMSで、戻るたびに割り当てられたメモリが約180KB増加することがわかります。
メモリダンプを実行し、EclipseMATを使用して3つの異なる時点を分析しました。
メモリリークが疑われますが、原因を特定できません。メモリダンプによると、増加し続けているのは「残り」とJava.lang.FinalizerReference
のようです。 この質問 のユーザーも、メモリダンプに多くのFinalizerReferences
を持っていますが、答えは明確ではありません。
前回作成したリーク容疑者レポートは、Android.content.res.Resources
とAndroid.graphics.Bitmap
の疑いがあるため、あまり役に立ちません。これらは、時間の経過とともに増加していないようです。
レポートの1つ(残念ながら、ここにはありません)で、リークの疑いがあると指摘されたAndroid.widget.ListView
の13のインスタンスを見ました。
これらのメモリの増加は、アクティビティ間の遷移で発生します(この例で使用したメインとリストだけではありません)。
(自明ではない?)メモリリークを見つけるにはどうすればよいですか?私は長い間頭を悩ませてきたので、どんな助けやヒントも素晴らしいでしょう。
ビットマップ(@ OrhanC1):上記の2つのアクティビティでBitmap
のインスタンス化についてコメントしましたが、メモリはまだ増えています。メモリダンプにはまだいくつかのビットマップが表示されますが、それらはリソースに関連しており、私が割り当てた実際のビットマップではないと思います。
カスタムフォント(@erakitin)について:私はそれらを使用していますが、シングルトンを使用して、Typeface
コンテキスト(public class MyApp extends Application
)に各Application
の単一インスタンスを保持しています。上記の2つのアクティビティでフォントへの参照をコメントしようとしましたが、メモリはまだ増えています。
Context
(@ DigCamara)をリークしているとは思わない:これら2つのアクティビティ内に静的参照を保持していません。アダプターを除いて、Application
の代わりにActivity
コンテキストを使用しています。同じActivity
にとどまり、画面を回転させても、メモリは増加しません。
@NickTのコメントに基づく:両方のアクティビティのインスタンスが多数あることがわかります。これらのメモリの増加は、バックスタックのアクティビティ数の増加の結果であり、メモリリークではない可能性があります(OSは処理しましたがそれ、 どうやらそうではない )? FLAG_ACTIVITY_REORDER_TO_FRONT
インテントフラグを使用すると、すべての異なるアクティビティがインスタンス化されるまで(1回)メモリが増加するだけです。この問題に役立ちます: メモリが少ないときにAndroidがスタックからアクティビティを強制終了しない 。
Remainder
が増加している理由は、バックスタック内のActivity
インスタンスの数の増加であるように見えます(たとえば、「リスト」の13インスタンスActivity
+ 13 「メイン」のインスタンスActivity
)。
ユーザーが[ホーム]ボタンをクリックすると(アプリの[ダッシュボード]に移動します)、_Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK
_フラグを設定するようにナビゲーションドロワーを変更しました。アクティビティは再利用され、バックスタックはクリアされました( Androidガイドライン、実際には で推奨されています)。実際、ダッシュボードの複数のインスタンスが必要ないため、これはすでに実行されているはずです( "ホーム」)作成されるアクティビティ。
これを行うことにより、アクティビティが破棄され、割り当てられたヒープサイズ(「Remaining
」スライスを含む)が減少することがわかります。
これを修正しているときに、バックスタックがクリアされた(リークした)場合でも、アクティビティの1つとそれによって使用されるビットマップが破棄されていないことにも気付きました。 MATで分析した後、このサブ問題の原因は、ImageView
に保持しているActivity
への参照であると結論付けました。このコードをonStop()
メソッドに追加することで、アクティビティとBitmap
の両方を破棄することができました。
_@Override
protected void onStop() {
super.onStop();
ImageView myImage = (ImageView) findViewById(R.id.myImage );
if(myImage .getDrawable() != null)
myImage.getDrawable().setCallback(null);
RoundedImageView roundImage = (RoundedImageView) findViewById(R.id.roundImage); // a custom View
if(roundImage.getDrawable() != null)
roundImage.getDrawable().setCallback(null);
}
_
次に、すべてのActivity
とFragmentActivity
を一般化して、unbindDrawables(View view)
でonDestroy()
を呼び出すようにしました。
_private void unbindDrawables(View view)
{
if (view.getBackground() != null)
{
view.getBackground().setCallback(null);
}
if (view instanceof ViewGroup && !(view instanceof AdapterView))
{
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++)
{
unbindDrawables(((ViewGroup) view).getChildAt(i));
}
((ViewGroup) view).removeAllViews();
}
}
_
私を正しい方向に向けてくれた@NickTに感謝します。