Verständnis von onTrimMemory ( int level )

Lesezeit: 8 Minuten

Benutzer-Avatar
Mario Lenci

Ich habe kürzlich diesen Artikel auf gelesen Verwalten des Speichers Ihrer AppIch empfehle dringend, es zu lesen, wenn Sie ein Android-Entwickler sind und es nie getan haben.

Es gibt viele gute Praktiken und eine Sache, von der ich nie etwas weiß, ist die onTrimMemory(int level) Methode, die vom System bei jeder Aktivität/jedem Fragment aufgerufen wird, um Ereignisse zu melden, bei denen Speicher freigegeben werden sollte oder könnte.

Hier ist ein Zitat aus diesem Artikel:

Beachten Sie, dass Ihre App den onTrimMemory()-Callback mit TRIM_MEMORY_UI_HIDDEN nur empfängt, wenn alle UI-Komponenten Ihres App-Prozesses vor dem Benutzer verborgen werden. Dies unterscheidet sich vom onStop()-Callback, der aufgerufen wird, wenn eine Aktivitätsinstanz ausgeblendet wird, was selbst dann auftritt, wenn der Benutzer zu einer anderen Aktivität in Ihrer App wechselt. Obwohl Sie also onStop() implementieren sollten, um Aktivitätsressourcen wie eine Netzwerkverbindung freizugeben oder Broadcast-Empfänger abzumelden, Normalerweise sollten Sie Ihre UI-Ressourcen nicht freigeben, bis Sie onTrimMemory (TRIM_MEMORY_UI_HIDDEN) erhalten.. Dadurch wird sichergestellt, dass Ihre UI-Ressourcen immer noch verfügbar sind, um die Aktivität schnell fortzusetzen, wenn der Benutzer von einer anderen Aktivität in Ihrer App zurücknavigiert.

Ich bin wirklich daran interessiert, eine gute Speicherverwaltung in meiner Anwendung zu implementieren, also freue ich mich darauf, die zu implementieren onTrimMemory() auf die richtige Art und Weise.

Ich habe nur ein paar Fragen dazu:

  • ist onTrimMemory(TRIM_MEMORY_UI_HIDDEN) direkt nach dem onStop() aufgerufen?

  • Was bedeutet „Ihre UI-Ressourcen freigeben“ in diesem Zusammenhang? Nur zum Beispiel den Bitmap-Cache leeren oder tatsächlich jede Ansicht im Ansichtsbaum entfernen und zerstören? Normalerweise zerstöre ich die Ansichten in der onDestroy() oder onDestroyView() Methoden, ich frage mich jetzt, ob ich es richtig mache.

  • Gibt es einen Rückruf für Zwillinge/Korrespondenten? onTrimMemory(TRIM_MEMORY_UI_HIDDEN)? wie onCreate-onDestroy, onStart-onStop, onCreateView-onDestroyView. Ich möchte wissen, wo und wie ich den UI-Status wiederherstellen soll, nachdem eine Aktivität/ein Fragment in den Vordergrund gebracht wurde onTrimMemory(TRIM_MEMORY_UI_HIDDEN) genannt worden.

  • Beachten Sie, dass onTrimMemory(level) vom System auf allen Android-Komponenten aufgerufen wird – nicht nur auf Activity/Fragment.

    – Tom

    28. Januar 2014 um 17:32 Uhr


  • siehe Beispielimplementierung hier stackoverflow.com/a/28210326/185022

    – AZ_

    29. Januar 2015 um 8:50 Uhr

Benutzer-Avatar
Alex Machado

  • onTrimMemory mit TRIM_MEMORY_UI_HIDDEN-Ebene wird tatsächlich vor onStop aufgerufen. Wenn onStop aufgerufen wird, bedeutet dies, dass die Aktivität wirklich angehalten wird, und das Android-Betriebssystem kann sie bei Bedarf sofort beenden. Sie sollten also danach keine weiteren Aufrufe der Rückrufe dieser Aktivität erwarten, außer für onRestart und manchmal onDestroy.

  • “Freigabe Ihrer UI-Ressourcen” bezieht sich eigentlich auf Dinge wie Caches. Sie müssen sich normalerweise nicht um die Verwaltung von Ansichten oder UI-Komponenten kümmern, da das Betriebssystem dies bereits tut, und deshalb gibt es all diese Rückrufe zum Erstellen, Starten, Anhalten, Stoppen und Zerstören einer Aktivität. Um die Leistung zu verbessern, müssen Sie jedoch manchmal die Speichernutzung erhöhen, z. B. einige Daten zwischenspeichern, die von Ihren Aktivitäten verwendet werden. Das ist die Art von Ressource, die Sie freigeben sollten, wenn onTrimMemory aufgerufen wird, damit Ihre App weniger Speicher verwendet, auch wenn dies die Leistung beeinträchtigt. Sie sollten sich jedoch Gedanken über Speicherlecks machen. Wenn Ihre Aktivität stoppt, achten Sie darauf, keine Verweise auf ihre Ansichten beizubehalten, da dies verhindern würde, dass die Aktivität von Garbage Collection erfasst wird, was dazu führen würde, dass der gesamte Kontext nicht erfasst wird, und das ist schlecht, vor allem, wenn Sie Ihre App am Laufen halten möchten für Stunden oder Tage (z. B. wenn Sie Dienste implementieren).

  • Nein, es gibt keinen entsprechenden Callback zu onTrimMemory. Sie sollten jedoch keine benötigen. Wie ich bereits sagte, wenn Sie einige Ressourcen im Cache aufbewahren, um die Leistung zu verbessern, leeren Sie diesen einfach und lassen Sie ihn bei Bedarf wieder wachsen. Wenn der Speicherpegel niedrig bleibt, wird onTrimMemory möglicherweise bald erneut mit demselben Speicherpegel aufgerufen. Beachten Sie übrigens, dass onTrimMemory mit mehreren verschiedenen Speicherebenen aufgerufen wird, nicht nur mit TRIM_MEMORY_UI_HIDDEN.

  • Kannst du das erklären If your activity stops, be sure not to keep any references to its views?

    – Gökhan Arik

    18. Juli 2016 um 13:59 Uhr

  • Ich denke, er meint, dass Sie keinen statischen Bezug zu der Aktivität oder ihren Ansichten behalten sollten. Denn wenn das System diese Aktivität beenden muss, um Speicher zurückzugewinnen, kann es nicht die gesamte gespeicherte Größe freigeben, da es eine statische Referenz gibt, deren Lebensdauer länger ist als die Lebensdauer der Aktivität. Das ist ein Speicherleck.

    – Mahmud

    27. September 2021 um 16:29 Uhr

Benutzer-Avatar
AZ_

Beispielimplementierung

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) {

        }
    }

Mehr Info:

Wenn Ihre App ausgeführt wird:
TRIM_MEMORY_RUNNING_MODERATE
Der Arbeitsspeicher des Geräts geht langsam zur Neige. Ihre App läuft und kann nicht beendet werden.

TRIM_MEMORY_RUNNING_LOW
Das Gerät läuft viel weniger Speicher. Ihre App wird ausgeführt und kann nicht beendet werden, aber geben Sie ungenutzte Ressourcen frei, um die Systemleistung zu verbessern (was sich direkt auf die Leistung Ihrer App auswirkt).

TRIM_MEMORY_RUNNING_CRITICAL
Das Gerät hat extrem wenig Arbeitsspeicher. Ihre App wird noch nicht als Prozess angesehen, der beendet werden kann, aber das System beginnt mit dem Beenden von Hintergrundprozessen, wenn Apps keine Ressourcen freigeben, daher sollten Sie nicht kritische Ressourcen jetzt freigeben, um Leistungseinbußen zu vermeiden.

Wenn sich die Sichtbarkeit Ihrer App ändert:
TRIM_MEMORY_UI_HIDDEN
Die Benutzeroberfläche Ihrer App ist nicht mehr sichtbar, daher ist dies ein guter Zeitpunkt, um große Ressourcen freizugeben, die nur von Ihrer Benutzeroberfläche verwendet werden.

Wenn sich der Prozess Ihrer App in der Hintergrund-LRU-Liste befindet:
TRIM_MEMORY_BACKGROUND
Der Arbeitsspeicher des Systems geht zur Neige und Ihr Prozess steht kurz vor dem Beginn des LRU aufführen. Obwohl für Ihren App-Prozess kein hohes Risiko besteht, dass er beendet wird, beendet das System möglicherweise bereits Prozesse in der LRU Daher sollten Sie Ressourcen freigeben, die leicht wiederhergestellt werden können, damit Ihr Prozess in der Liste bleibt und schnell fortgesetzt wird, wenn der Benutzer zu Ihrer App zurückkehrt.

TRIM_MEMORY_MODERATE
Der Arbeitsspeicher des Systems geht zur Neige und Ihr Prozess befindet sich in der Mitte der LRU-Liste. Wenn der Speicher des Systems weiter eingeschränkt wird, besteht die Möglichkeit, dass Ihr Prozess beendet wird.

TRIM_MEMORY_COMPLETE
Dem System geht der Arbeitsspeicher aus und Ihr Prozess ist einer der ersten, der beendet wird, wenn das System den Arbeitsspeicher jetzt nicht wiederherstellt. Sie sollten absolut alles freigeben, was für die Wiederaufnahme Ihres App-Status nicht entscheidend ist. Um API-Ebenen unter 14 zu unterstützen, können Sie die verwenden onLowMemory() Methode als Fallback, die ungefähr der entspricht TRIM_MEMORY_COMPLETE eben.

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

  • Danke für die Erklärung. Wenn ich onTrimMemory(int level) in Activity verwende, wird es dann automatisch aufgerufen?

    – Akhilesh Mani

    28. Juli 2016 um 7:29 Uhr

  • @AkhileshMani ja, es ist ein Rückruf, basierend auf Ihrer App-Nutzung erhalten Sie einen entsprechenden Rückruf.

    – Lebensmacher

    19. Dezember 2019 um 7:23 Uhr

  • aber wie kann man speicher freigeben? wir bekommen Rückrufe aber danach was ich soll. Bitte, wenn Sie mir ein Beispiel geben können. wie Cache entfernen.

    – Nitisch

    8. Juli 2020 um 17:26 Uhr

Benutzer-Avatar
Bluscreen

Ich habe das Problem erzwungen, dass onTrimMemory() nie aufgerufen wurde, als das Display ausgeschaltet wurde. Daher habe ich einen Workaround mit ActivityLifecycleCallbacks versucht: Ich habe einen einfachen Zähler verwendet:

onActivityStarted(){
    i++;
}

onActivityStopped(){
    i--;
    if(i==0) // no more open activities, thus screen probably turned off
}

Es hat für mich funktioniert, aber ich bin mir nicht sicher, ob es ein sicherer Weg ist. RFC

UPDATE: Beim Starten einer Kameraabsicht wurde die Anwendung ebenfalls geschlossen, sodass sie sich nicht genau wie gewünscht verhält.

Habe stattdessen diesen Code verwendet. funktioniert ganz gut:

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);
}

1226950cookie-checkVerständnis von onTrimMemory ( int level )

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy