Managing Your App's Memoryで最近この記事を読みました。AndroidDevであり、一度も行ったことがない場合は読むことを強くお勧めします。
良い習慣がたくさんあり、私が決して知らないことの1つは、メモリが解放されるべき、または解放される可能性のあるイベントを通知するために、すべてのアクティビティ/フラグメントでシステムによって呼び出される onTrimMemory(int level) メソッドです。
その記事からの引用です:
アプリプロセスのすべてのUIコンポーネントがユーザーから非表示になった場合にのみ、アプリがTRIM_MEMORY_UI_HIDDENでonTrimMemory()コールバックを受け取ることに注意してください。これは、アクティビティインスタンスが非表示になったときに呼び出されるonStop()コールバックとは異なります。これは、ユーザーがアプリ内の別のアクティビティに移動した場合でも発生します。したがって、onStop()を実装してネットワーク接続などのアクティビティリソースを解放するか、ブロードキャストレシーバーの登録を解除する必要がありますが、通常、onTrimMemory(TRIM_MEMORY_UI_HIDDEN)を受け取るまでUIリソースを解放しないでください 。これにより、ユーザーがアプリ内の別のアクティビティから戻った場合でも、UIリソースを使用してアクティビティをすばやく再開できます。
アプリケーションに適切なメモリ管理を実装することに本当に興味があるので、正しい方法で onTrimMemory() を実装することを楽しみにしています。
質問がいくつかあります。
onTrimMemory(TRIM_MEMORY_UI_HIDDEN) はonStop()の直後に呼び出されますか?
そのコンテキストで「UIリソースを解放する」とはどういう意味ですか?たとえば、ビットマップキャッシュを消去するだけですか、それとも実際にビューツリー内のすべてのビューを削除しますか?私は通常onDestroy()またはonDestroyView()メソッドでビューを破棄しますが、今それを正しく実行しているかどうか疑問に思っています。
onTrimMemory(TRIM_MEMORY_UI_HIDDEN) へのTwin/correspondentコールバックがありますか? onCreate-onDestroy、onStart-onStop、onCreateView-onDestroyViewなど。 onTrimMemory(TRIM_MEMORY_UI_HIDDEN) が呼び出された後、フォアグラウンドでアクティビティ/フラグメントが呼び出された後、UI状態をどこでどのように復元する必要があるかを理解するよう求めています。
tRIM_MEMORY_UI_HIDDENレベルのonTrimMemoryは、onStopの前に実際に呼び出されます。 onStopが呼び出されると、アクティビティが本当に停止していることを意味し、必要に応じてAndroid OSがすぐにそれを強制終了する可能性があります。 、onRestartおよび場合によってはonDestroyを除きます。
「UIリソースを解放する」とは、実際にはキャッシュなどに関するものです。通常、ビューやUIコンポーネントの管理について心配する必要はありません。OSが既にそれを行っているためです。アクティビティを作成、開始、一時停止、停止、および破棄するためのコールバックがすべてあるのはそのためです。ただし、パフォーマンスを改善するために、アクティビティで使用される一部のデータをキャッシュするなど、メモリ使用量を増やす必要がある場合があります。これは、onTrimMemoryが呼び出されたときに解放する必要があるリソースのタイプです。そのため、パフォーマンスに影響があったとしても、アプリはより少ないメモリを使用します。ただし、メモリリークを心配する必要があります。アクティビティが停止した場合は、ビューへの参照を保持しないでください。アクティビティがガベージコレクションされず、コンテキスト全体が収集されず、アプリの実行を継続したい場合に悪いためです。数時間または数日間(サービスを実装するときなど)。
いいえ、onTrimMemoryに対応するコールバックはありません。ただし、必要ありません。前に言ったように、パフォーマンスを向上させるためにいくつかのリソースのキャッシュを保持する場合は、それを空にして、必要に応じて再度成長させます。メモリレベルが低いままの場合、onTrimMemoryは同じメモリレベルですぐに再び呼び出される場合があります。ちなみに、onTrimMemoryは、TRIM_MEMORY_UI_HIDDENだけでなく、いくつかの異なるメモリレベルで呼び出されることに注意してください。
サンプル実装
_public class AppContext extends Application {
//This my introduce OutOfMemoryException if you don't handle register and removal quiet well, better to replace it with weak reference
private static List<IMemoryInfo> memInfoList = new ArrayList<AppContext.IMemoryInfo>();
public static abstract interface IMemoryInfo {
public void goodTimeToReleaseMemory();
}
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
//don't compare with == as intermediate stages also can be reported, always better to check >= or <=
if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW) {
try {
// Activity at the front will get earliest than activity at the
// back
for (int i = memInfoList.size() - 1; i >= 0; i--) {
try {
memInfoList.get(i).goodTimeToReleaseMemory();
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
*
* @param implementor
* interested listening in memory events
*/
public static void registerMemoryListener(IMemoryInfo implementor) {
memInfoList.add(implementor);
}
public static void unregisterMemoryListener(IMemoryInfo implementor) {
memInfoList.remove(implementor);
}
}
_
_public class ActivityParent extends Activity implements AppContext.IMemoryInfo {
protected ActivityParent child;
@Override
protected void onStop() {
super.onStop();
try {
if (child != null)
AppContext.unregisterMemoryListener(child);
} catch (Exception e) {
}
}
}
_
_public class ActivityChild extends ActivityParent {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
child = this;
}
/---move following onResume() in parent as following eg:
/*
*@Override
* protected void onResume() {
* super.onResume();
* if(null != child){
* AppContext.registerMemoryListener(this);
* }
* }
*/
@Override
protected void onResume() {
super.onResume();
AppContext.registerMemoryListener(this);
}
@Override
public void goodTimeToReleaseMemory() {
super.goodTimeToReleaseMemory();
//remove your Cache etc here
}
//--NO Need because parent implementation will be called first, just for the sake of clarity
@Override
protected void onStop() {
super.onStop();
try {
if (null != child)
AppContext.unregisterMemoryListener(child);
} catch (Exception e) {
}
}
_
詳細:
アプリの実行中:TRIM_MEMORY_RUNNING_MODERATEデバイスのメモリが不足し始めています。アプリは実行中であり、強制終了できません。
TRIM_MEMORY_RUNNING_LOWデバイスのメモリがはるかに少なく実行されています。アプリは実行中ですが、強制終了はできませんが、システムのパフォーマンスを向上させるために未使用のリソースを解放してください(アプリのパフォーマンスに直接影響します)。
TRIM_MEMORY_RUNNING_CRITICALデバイスのメモリが極端に少なくなっています。アプリはまだ強制終了可能なプロセスとは見なされていませんが、アプリがリソースを解放しない場合、システムはバックグラウンドプロセスの強制終了を開始するため、パフォーマンスの低下を防ぐために重要でないリソースをすぐに解放する必要があります。
アプリの可視性が変更された場合:TRIM_MEMORY_UI_HIDDENアプリのUIは表示されなくなったため、UIのみが使用する大きなリソースを解放する良い機会です。
アプリのプロセスがバックグラウンドLRUリストにある場合:TRIM_MEMORY_BACKGROUNDシステムのメモリが不足しており、プロセスはLRU
リストの先頭近くにあります。アプリプロセスが強制終了されるリスクは高くありませんが、システムは既にLRU
リスト内のプロセスを強制終了している可能性があります。そのため、プロセスがリストに残り、ユーザーがアプリに戻るとすぐに再開します。
TRIM_MEMORY_MODERATEシステムのメモリが不足しており、プロセスはLRUリストの中央付近にあります。システムのメモリがさらに制約されると、プロセスが強制終了される可能性があります。
TRIM_MEMORY_COMPLETEシステムのメモリが不足しており、システムが現在メモリを回復しない場合、プロセスは最初に強制終了されます。アプリの状態を再開するために重要ではないすべてのものを完全にリリースする必要があります。 14より低いAPIレベルをサポートするには、onLowMemory()
メソッドを_TRIM_MEMORY_COMPLETE
_レベルとほぼ同等のフォールバックとして使用できます。
http://developer.Android.com/reference/Android/content/ComponentCallbacks2.html
ディスプレイがオフになったときにonTrimMemory()が呼び出されないという問題を強制していました。したがって、ActivityLifecycleCallbacksを使用して回避策を試みました。単純なカウンターを使用しました。
onActivityStarted(){
i++;
}
onActivityStopped(){
i--;
if(i==0) // no more open activities, thus screen probably turned off
}
それは私にとってはうまくいきましたが、それが安全な方法であるかどうかはわかりません。 RFC
更新:カメラインテントを開始すると、アプリケーションも閉じられたため、意図したとおりに動作しません。
代わりにこのコードを使用しました。うまく動作します:
private void registerBroadcastReceiver() {
final IntentFilter theFilter = new IntentFilter();
theFilter.addAction(Intent.ACTION_SCREEN_OFF);
BroadcastReceiver screenOnOffReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String strAction = intent.getAction();
if (strAction.equals(Intent.ACTION_SCREEN_OFF)) {
// do sth
}
}
};
getApplicationContext()
.registerReceiver(screenOnOffReceiver, theFilter);
}