66

最近、Managing Your App's Memoryに関するこの記事を読みました。あなたが AndroidDev で、一度も読んだことがない場合は、この記事を読むことを強くお勧めします。

グッド プラクティスはたくさんありますが、私がたまたま知らないことの 1 つは、すべてのアクティビティ/フラグメントでシステムによって呼び出されるonTrimMemory(int level)メソッドで、メモリを解放する必要がある、または解放できる可能性のあるイベントを通知します。

以下はその記事からの引用です。

アプリ プロセスのすべての UI コンポーネントがユーザーから非表示になった場合にのみ、アプリが TRIM_MEMORY_UI_HIDDEN で onTrimMemory() コールバックを受け取ることに注意してください。これは、Activity インスタンスが非表示になったときに呼び出される onStop() コールバックとは異なります。これは、ユーザーがアプリ内の別のアクティビティに移動した場合でも発生します。そのため、ネットワーク接続などのアクティビティ リソースを解放したり、ブロードキャスト レシーバーを登録解除したりするために onStop() を実装する必要がありますが、通常は、 onTrimMemory(TRIM_MEMORY_UI_HIDDEN) を受け取るまで UI リソースを解放しないでください。これにより、ユーザーがアプリ内の別のアクティビティから戻った場合でも、UI リソースを使用してアクティビティをすばやく再開できます。

アプリケーションに適切なメモリ管理を実装することに本当に関心があるので、 onTrimMemory()を正しい方法で実装することを楽しみにしています。

私はそれについていくつかの質問しかありません:

  • onTrimMemory(TRIM_MEMORY_UI_HIDDEN)はonStop()の直後に呼び出されますか?

  • そのコンテキストで「UI リソースを解放する」とはどういう意味ですか? たとえば、ビットマップ キャッシュをクリーンアップするか、ビュー ツリー内のすべてのビューを実際に削除して破棄しますか? 私は通常、onDestroy()またはonDestroyView()メソッドでビューを破棄しますが、今はそれが正しいかどうか疑問に思っています。

  • onTrimMemory(TRIM_MEMORY_UI_HIDDEN)へのツイン/対応するコールバックはありますか? onCreate-onDestroy、onStart -onStoponCreateView-onDestroyView などonTrimMemory(TRIM_MEMORY_UI_HIDDEN)が呼び出された後、Activity/Fragment がフォアグラウンドに持ち込まれた後、どこでどのように UI 状態を復元する必要があるかを理解するよう求めています。

4

3 に答える 3

24

サンプル実装

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 システムのメモリが不足しており、システムがすぐにメモリを回復しない場合、プロセスは最初に強制終了されるプロセスの 1 つです。アプリの状態を再開するために重要ではないものはすべて完全に解放する必要があります。14 未満の API レベルをサポートするonLowMemory()には、レベルとほぼ同等のフォールバックとしてメソッドを使用できTRIM_MEMORY_COMPLETEます。

http://developer.android.com/reference/android/content/ComponentCallbacks2.html

于 2015-01-29T08:43:27.217 に答える
1

ディスプレイがオフになったときに 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);
}
于 2016-02-11T10:54:41.837 に答える