Ich versuche, eine App zu schreiben, die etwas Bestimmtes tut, wenn sie nach einiger Zeit wieder in den Vordergrund gerückt wird. Gibt es eine Möglichkeit zu erkennen, wann eine App in den Hintergrund gesendet oder in den Vordergrund gestellt wird?
Ich versuche, eine App zu schreiben, die etwas Bestimmtes tut, wenn sie nach einiger Zeit wieder in den Vordergrund gerückt wird. Gibt es eine Möglichkeit zu erkennen, wann eine App in den Hintergrund gesendet oder in den Vordergrund gestellt wird?
Antworten:
Die Methoden onPause()
und onResume()
werden aufgerufen, wenn die Anwendung wieder in den Hintergrund und in den Vordergrund gerückt wird. Sie werden jedoch auch aufgerufen, wenn die Anwendung zum ersten Mal gestartet wird und bevor sie beendet wird. Weitere Informationen finden Sie unter Aktivität .
Es gibt keinen direkten Ansatz, um den Anwendungsstatus im Hintergrund oder im Vordergrund abzurufen, aber selbst ich habe mich diesem Problem gestellt und die Lösung mit onWindowFocusChanged
und gefunden onStop
.
Weitere Informationen finden Sie hier. Android: Lösung, um zu erkennen, wann eine Android-App in den Hintergrund tritt und ohne getRunningTasks oder getRunningAppProcesses wieder in den Vordergrund zurückkehrt .
März 2018 UPDATE : Es gibt jetzt eine bessere Lösung. Siehe ProcessLifecycleOwner . Sie müssen die neuen Architekturkomponenten 1.1.0 (aktuell zu diesem Zeitpunkt) verwenden, diese wurden jedoch speziell dafür entwickelt.
Diese Antwort enthält ein einfaches Beispiel , aber ich habe eine Beispiel-App und einen Blog-Beitrag darüber geschrieben.
Seit ich das 2014 geschrieben habe, sind verschiedene Lösungen entstanden. Einige funktionierten, andere funktionierten , hatten aber Fehler (einschließlich meiner!) Und wir als Community (Android) lernten, mit den Konsequenzen zu leben, und schrieben Problemumgehungen für die Sonderfälle.
Gehen Sie niemals davon aus, dass ein einziger Codeausschnitt die Lösung ist, nach der Sie suchen. Dies ist unwahrscheinlich. Besser noch, versuchen Sie zu verstehen, was es tut und warum es es tut.
Die MemoryBoss
Klasse wurde von mir nie so verwendet, wie hier geschrieben, es war nur ein Stück Pseudocode, der zufällig funktionierte.
Wenn es keinen triftigen Grund für Sie gibt, die neuen Architekturkomponenten nicht zu verwenden (und es gibt einige, insbesondere wenn Sie auf super alte APIs abzielen), fahren Sie fort und verwenden Sie sie. Sie sind alles andere als perfekt, aber sie waren es auch nicht ComponentCallbacks2
.
UPDATE / NOTES (November 2015) : Die Leute haben zwei Kommentare abgegeben. Erstens >=
sollte diese verwendet werden, anstatt, ==
weil in der Dokumentation angegeben ist, dass Sie nicht nach genauen Werten suchen sollten . Dies ist in den meisten Fällen in Ordnung, aber denken Sie daran, dass Sie == verwenden und es auch mit einer anderen Lösung (wie Activity Lifecycle-Rückrufen) oder Ihnen kombinieren müssen , wenn Sie nur etwas tun möchten, wenn die App in den Hintergrund tritt möglicherweise nicht den gewünschten Effekt erzielen. Das Beispiel (und das ist mir passiert) ist das, wenn Sie sperren möchtenWenn Ihre App mit einem Kennwortbildschirm in den Hintergrund tritt (wie 1Password, wenn Sie damit vertraut sind), können Sie Ihre App versehentlich sperren, wenn Ihnen der Arbeitsspeicher ausgeht und Sie plötzlich darauf testen >= TRIM_MEMORY
, da Android einen LOW MEMORY
Anruf auslöst höher als deins. Seien Sie also vorsichtig, wie / was Sie testen.
Darüber hinaus haben einige Leute gefragt, wie Sie erkennen können, wann Sie zurückkommen.
Der einfachste Weg, den ich mir vorstellen kann, wird unten erklärt, aber da einige Leute damit nicht vertraut sind, füge ich hier einen Pseudocode hinzu. Angenommen, Sie haben YourApplication
und die MemoryBoss
Klassen in Ihrer class BaseActivity extends Activity
(Sie müssen eine erstellen, wenn Sie keine haben).
@Override
protected void onStart() {
super.onStart();
if (mApplication.wasInBackground()) {
// HERE YOU CALL THE CODE YOU WANT TO HAPPEN ONLY ONCE WHEN YOUR APP WAS RESUMED FROM BACKGROUND
mApplication.setWasInBackground(false);
}
}
Ich empfehle onStart, da Dialoge eine Aktivität anhalten können. Ich wette, Sie möchten nicht, dass Ihre App denkt, dass sie in den Hintergrund tritt, wenn Sie nur einen Vollbilddialog angezeigt haben, aber Ihr Kilometerstand kann variieren.
Und das ist alles. Der Code in der if - Block wird nur einmal ausgeführt werden , auch wenn Sie auf eine andere Tätigkeit gehen, die neue (das auch extends BaseActivity
) berichten wasInBackground
ist , false
so dass es den Code nicht ausgeführt wird, bis onMemoryTrimmed
aufgerufen wird und das Flag auf true gesetzt wieder .
Ich hoffe, das hilft.
UPDATE / NOTES (April 2015) : Bevor Sie mit dem Kopieren und Einfügen dieses Codes beginnen, beachten Sie, dass ich einige Fälle gefunden habe, in denen er möglicherweise nicht 100% zuverlässig ist und mit anderen Methoden kombiniert werden muss , um die besten Ergebnisse zu erzielen. Insbesondere sind zwei Fälle bekannt, in denen die onTrimMemory
Ausführung des Rückrufs nicht garantiert werden kann:
Wenn Ihr Telefon den Bildschirm sperrt, während Ihre App sichtbar ist (sagen wir, Ihr Gerät wird nach nn Minuten gesperrt), wird dieser Rückruf nicht (oder nicht immer) aufgerufen, da der Sperrbildschirm nur oben angezeigt wird, Ihre App jedoch weiterhin "ausgeführt" wird, obwohl sie abgedeckt ist.
Wenn Ihr Gerät relativ wenig Speicherplatz hat (und unter Speicherstress steht), scheint das Betriebssystem diesen Aufruf zu ignorieren und direkt zu kritischeren Ebenen zu wechseln.
Abhängig davon, wie wichtig es für Sie ist, zu wissen, wann Ihre App in den Hintergrund getreten ist, müssen Sie diese Lösung möglicherweise erweitern oder nicht, um den Aktivitätslebenszyklus und so weiter zu verfolgen.
Denken Sie einfach an die oben genannten Punkte und haben Sie ein gutes QS-Team;)
ENDE DES UPDATE
Es mag spät sein, aber es gibt eine zuverlässige Methode in Ice Cream Sandwich (API 14) und höher .
Es stellt sich heraus, dass ein Rückruf ausgelöst wird, wenn Ihre App keine sichtbare Benutzeroberfläche mehr hat. Der Rückruf, den Sie in einer benutzerdefinierten Klasse implementieren können, heißt ComponentCallbacks2 (ja, mit zwei). Dieser Rückruf ist nur in API Level 14 (Ice Cream Sandwich) und höher verfügbar .
Grundsätzlich erhalten Sie einen Aufruf der Methode:
public abstract void onTrimMemory (int level)
Das Level ist 20 oder genauer
public static final int TRIM_MEMORY_UI_HIDDEN
Ich habe dies getestet und es funktioniert immer, da Level 20 nur ein "Vorschlag" ist, dass Sie möglicherweise einige Ressourcen freigeben möchten, da Ihre App nicht mehr sichtbar ist.
Um die offiziellen Dokumente zu zitieren:
Level für onTrimMemory (int): Der Prozess hat eine Benutzeroberfläche angezeigt und tut dies nicht mehr. Zu diesem Zeitpunkt sollten große Zuordnungen mit der Benutzeroberfläche freigegeben werden, damit der Speicher besser verwaltet werden kann.
Natürlich sollten Sie dies implementieren , um tatsächlich tun , was er sagt (Purge - Speicher , die in bestimmten Zeit verwendet hat, deaktivieren Sie einige Sammlungen nicht die ungenutzt gesessen haben usw. Die Möglichkeiten sind endlos (siehe die offiziellen Dokumente für andere möglich mehr kritische Ebenen).
Aber das Interessante ist, dass das Betriebssystem Ihnen sagt: Hey, Ihre App ist in den Hintergrund getreten!
Welches ist genau das, was Sie zuerst wissen wollten.
Wie bestimmen Sie, wann Sie zurück sind?
Nun, das ist einfach. Ich bin sicher, Sie haben eine "BaseActivity", sodass Sie mit onResume () darauf hinweisen können , dass Sie zurück sind. Denn Sie werden nur dann sagen, dass Sie nicht zurück sind, wenn Sie tatsächlich einen Anruf bei der oben genannten onTrimMemory
Methode erhalten.
Es klappt. Sie erhalten keine Fehlalarme. Wenn eine Aktivität wieder aufgenommen wird, sind Sie zu 100% zurück. Wenn der Benutzer wieder nach hinten geht, erhalten Sie einen weiteren onTrimMemory()
Anruf.
Sie müssen Ihre Aktivitäten (oder noch besser eine benutzerdefinierte Klasse) abonnieren.
Der einfachste Weg, um sicherzustellen, dass Sie dies immer erhalten, besteht darin, eine einfache Klasse wie die folgende zu erstellen:
public class MemoryBoss implements ComponentCallbacks2 {
@Override
public void onConfigurationChanged(final Configuration newConfig) {
}
@Override
public void onLowMemory() {
}
@Override
public void onTrimMemory(final int level) {
if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
// We're in the Background
}
// you might as well implement some memory cleanup here and be a nice Android dev.
}
}
Um dies zu verwenden, gehen Sie in Ihrer Anwendungsimplementierung ( Sie haben eine, RECHTS? ) So vor:
MemoryBoss mMemoryBoss;
@Override
public void onCreate() {
super.onCreate();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
mMemoryBoss = new MemoryBoss();
registerComponentCallbacks(mMemoryBoss);
}
}
Wenn Sie ein erstellen Interface
können Sie eine hinzufügen else
zu , dass if
und implementieren ComponentCallbacks
(ohne die 2) in etwas unterhalb API verwendet 14. Das Callback nur das hat onLowMemory()
Methode und nicht aufgerufen wird erhalten , wenn Sie in den Hintergrund , aber Sie es zu trimmen Speicher verwenden sollen .
Starten Sie jetzt Ihre App und drücken Sie nach Hause. Ihre onTrimMemory(final int level)
Methode sollte aufgerufen werden (Hinweis: Protokollierung hinzufügen).
Der letzte Schritt besteht darin, die Registrierung vom Rückruf aufzuheben. Der wahrscheinlich beste Ort ist die onTerminate()
Methode Ihrer App, aber diese Methode wird auf einem realen Gerät nicht aufgerufen:
/** * This method is for use in emulated process environments. It will * never be called on a production Android device, where processes are * removed by simply killing them; no user code (including this callback) * is executed when doing so. */
Wenn Sie also nicht wirklich eine Situation haben, in der Sie nicht mehr registriert werden möchten, können Sie dies ignorieren, da Ihr Prozess auf Betriebssystemebene ohnehin zum Erliegen kommt.
Wenn Sie sich irgendwann entscheiden, die Registrierung aufzuheben (wenn Sie beispielsweise einen Mechanismus zum Herunterfahren Ihrer App bereitstellen, um zu bereinigen und zu sterben), können Sie Folgendes tun:
unregisterComponentCallbacks(mMemoryBoss);
Und das ist es.
level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
was das Problem in Ihrem Update vermeidet, Punkt 2. In Bezug auf Punkt 1 ist es mir kein Problem, da die App nicht wirklich in den Hintergrund getreten ist, also sollte es so funktionieren.
So habe ich es geschafft, das zu lösen. Es wird davon ausgegangen, dass die Verwendung einer Zeitreferenz zwischen Aktivitätsübergängen höchstwahrscheinlich einen ausreichenden Beweis dafür liefert, dass eine App "im Hintergrund" ist oder nicht.
Zuerst habe ich eine android.app.Application-Instanz (nennen wir sie MyApplication) verwendet, die einen Timer, eine TimerTask, eine Konstante hat, um die maximale Anzahl von Millisekunden darzustellen, die der Übergang von einer Aktivität zu einer anderen vernünftigerweise dauern könnte (ich ging mit einem Wert von 2s) und einem Booleschen Wert, der angibt, ob sich die App "im Hintergrund" befand oder nicht:
public class MyApplication extends Application {
private Timer mActivityTransitionTimer;
private TimerTask mActivityTransitionTimerTask;
public boolean wasInBackground;
private final long MAX_ACTIVITY_TRANSITION_TIME_MS = 2000;
...
Die Anwendung bietet außerdem zwei Methoden zum Starten und Stoppen des Timers / der Aufgabe:
public void startActivityTransitionTimer() {
this.mActivityTransitionTimer = new Timer();
this.mActivityTransitionTimerTask = new TimerTask() {
public void run() {
MyApplication.this.wasInBackground = true;
}
};
this.mActivityTransitionTimer.schedule(mActivityTransitionTimerTask,
MAX_ACTIVITY_TRANSITION_TIME_MS);
}
public void stopActivityTransitionTimer() {
if (this.mActivityTransitionTimerTask != null) {
this.mActivityTransitionTimerTask.cancel();
}
if (this.mActivityTransitionTimer != null) {
this.mActivityTransitionTimer.cancel();
}
this.wasInBackground = false;
}
Der letzte Teil dieser Lösung besteht darin, jeder dieser Methoden einen Aufruf aus den Ereignissen onResume () und onPause () aller Aktivitäten oder vorzugsweise einer Basisaktivität hinzuzufügen, von der alle Ihre konkreten Aktivitäten erben:
@Override
public void onResume()
{
super.onResume();
MyApplication myApp = (MyApplication)this.getApplication();
if (myApp.wasInBackground)
{
//Do specific came-here-from-background code
}
myApp.stopActivityTransitionTimer();
}
@Override
public void onPause()
{
super.onPause();
((MyApplication)this.getApplication()).startActivityTransitionTimer();
}
Wenn der Benutzer also einfach zwischen den Aktivitäten Ihrer App navigiert, startet die onPause () der abgehenden Aktivität den Timer, aber fast sofort bricht die neu eingegebene Aktivität den Timer ab, bevor er die maximale Übergangszeit erreichen kann. Und so wäre InBackground falsch .
Wenn andererseits eine Aktivität vom Launcher in den Vordergrund tritt, das Gerät aufwacht, den Telefonanruf beendet usw., wurde die Timer-Task höchstwahrscheinlich vor diesem Ereignis ausgeführt und somit wurde InBackground auf true gesetzt .
Bearbeiten: Die neuen Architekturkomponenten brachten etwas Vielversprechendes: ProcessLifecycleOwner , siehe @ vokilams Antwort
class YourApplication : Application() {
override fun onCreate() {
super.onCreate()
registerActivityLifecycleCallbacks(AppLifecycleTracker())
}
}
class AppLifecycleTracker : Application.ActivityLifecycleCallbacks {
private var numStarted = 0
override fun onActivityStarted(activity: Activity?) {
if (numStarted == 0) {
// app went to foreground
}
numStarted++
}
override fun onActivityStopped(activity: Activity?) {
numStarted--
if (numStarted == 0) {
// app went to background
}
}
}
Ja. Ich weiß, es ist schwer zu glauben, dass diese einfache Lösung funktioniert, da wir hier so viele seltsame Lösungen haben.
Aber es gibt Hoffnung.
ProcessLifecycleOwner
scheint auch eine vielversprechende Lösung zu sein.
ProcessLifecycleOwner versenden
ON_START
,ON_RESUME
Ereignisse, als erste Aktivität bewegt sich durch diese Ereignisse.ON_PAUSE
,ON_STOP
Wird Veranstaltungen mit versendet werden Verzögerung nach einer letzten Aktivität durch sie geleitet. Diese Verzögerung ist lang genug, um sicherzustellen, dassProcessLifecycleOwner
keine Ereignisse gesendet werden, wenn Aktivitäten aufgrund einer Konfigurationsänderung zerstört und neu erstellt werden.
Eine Implementierung kann so einfach sein wie
class AppLifecycleListener : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onMoveToForeground() { // app moved to foreground
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onMoveToBackground() { // app moved to background
}
}
// register observer
ProcessLifecycleOwner.get().lifecycle.addObserver(AppLifecycleListener())
Der aktuelle Verzögerungswert beträgt laut Quellcode 700ms
.
Für die Verwendung dieser Funktion ist Folgendes erforderlich dependencies
:
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycleVersion"
implementation "android.arch.lifecycle:extensions:1.0.0"
und annotationProcessor "android.arch.lifecycle:compiler:1.0.0"
aus dem Google-Repository (dh google()
)
Basierend auf Martín Marconcinis Antwort (danke!) Habe ich endlich eine zuverlässige (und sehr einfache) Lösung gefunden.
public class ApplicationLifecycleHandler implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {
private static final String TAG = ApplicationLifecycleHandler.class.getSimpleName();
private static boolean isInBackground = false;
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
if(isInBackground){
Log.d(TAG, "app went to foreground");
isInBackground = false;
}
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
@Override
public void onConfigurationChanged(Configuration configuration) {
}
@Override
public void onLowMemory() {
}
@Override
public void onTrimMemory(int i) {
if(i == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN){
Log.d(TAG, "app went to background");
isInBackground = true;
}
}
}
Fügen Sie dies dann zu Ihrem onCreate () Ihrer Anwendungsklasse hinzu
public class MyApp extends android.app.Application {
@Override
public void onCreate() {
super.onCreate();
ApplicationLifeCycleHandler handler = new ApplicationLifeCycleHandler();
registerActivityLifecycleCallbacks(handler);
registerComponentCallbacks(handler);
}
}
Wir verwenden diese Methode. Es sieht zu einfach aus, um zu arbeiten, aber es wurde in unserer App gut getestet und funktioniert in allen Fällen überraschend gut, einschließlich des Aufrufen des Startbildschirms über die Schaltfläche "Startseite", über die Schaltfläche "Zurück" oder nach der Bildschirmsperre. Versuche es.
Die Idee ist, dass Android im Vordergrund immer neue Aktivitäten startet, bevor die vorherige beendet wird. Das ist nicht garantiert, aber so funktioniert es. Übrigens scheint Flurry dieselbe Logik zu verwenden (nur eine Vermutung, das habe ich nicht überprüft, aber es hängt mit denselben Ereignissen zusammen).
public abstract class BaseActivity extends Activity {
private static int sessionDepth = 0;
@Override
protected void onStart() {
super.onStart();
sessionDepth++;
if(sessionDepth == 1){
//app came to foreground;
}
}
@Override
protected void onStop() {
super.onStop();
if (sessionDepth > 0)
sessionDepth--;
if (sessionDepth == 0) {
// app went to background
}
}
}
Bearbeiten: Gemäß den Kommentaren sind wir in späteren Versionen des Codes auch zu onStart () übergegangen. Außerdem füge ich Superanrufe hinzu, die in meinem ersten Beitrag fehlten, da dies eher ein Konzept als ein Arbeitscode war.
onStop is called when the activity is no longer visible to the user
.
Wenn Ihre App aus mehreren Aktivitäten und / oder gestapelten Aktivitäten wie einem Registerkartenleisten-Widget besteht, funktioniert das Überschreiben von onPause () und onResume () nicht. Das heißt, wenn eine neue Aktivität gestartet wird, werden die aktuellen Aktivitäten angehalten, bevor die neue erstellt wird. Gleiches gilt, wenn Sie eine Aktivität beenden (mit der Schaltfläche "Zurück").
Ich habe zwei Methoden gefunden, die wie gewünscht zu funktionieren scheinen.
Die erste erfordert die Berechtigung GET_TASKS und besteht aus einer einfachen Methode, die durch Vergleichen der Paketnamen überprüft, ob die am häufigsten ausgeführte Aktivität auf dem Gerät zur Anwendung gehört:
private boolean isApplicationBroughtToBackground() {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> tasks = am.getRunningTasks(1);
if (!tasks.isEmpty()) {
ComponentName topActivity = tasks.get(0).topActivity;
if (!topActivity.getPackageName().equals(context.getPackageName())) {
return true;
}
}
return false;
}
Diese Methode wurde im Droid-Fu-Framework (jetzt Ignition genannt) gefunden.
Die zweite Methode, die ich selbst implementiert habe, erfordert nicht die Berechtigung GET_TASKS, was gut ist. Stattdessen ist die Implementierung etwas komplizierter.
In Ihrer MainApplication-Klasse haben Sie eine Variable, die die Anzahl der ausgeführten Aktivitäten in Ihrer Anwendung verfolgt. In onResume () erhöhen Sie für jede Aktivität die Variable und in onPause () verringern Sie sie.
Wenn die Anzahl der ausgeführten Aktivitäten 0 erreicht, wird die Anwendung in den Hintergrund gestellt, wenn die folgenden Bedingungen erfüllt sind:
Wenn Sie feststellen können, dass die Anwendung im Hintergrund zurückgetreten ist, können Sie leicht erkennen, wenn sie ebenfalls wieder in den Vordergrund gestellt wird.
Erstellen Sie eine Klasse , die erweitert wird Application
. Dann können wir darin seine Override-Methode verwenden onTrimMemory()
.
Um festzustellen, ob die Anwendung in den Hintergrund getreten ist, verwenden wir:
@Override
public void onTrimMemory(final int level) {
if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { // Works for Activity
// Get called every-time when application went to background.
}
else if (level == ComponentCallbacks2.TRIM_MEMORY_COMPLETE) { // Works for FragmentActivty
}
}
FragmentActivity
Sie auch hinzufügen möchten level == ComponentCallbacks2.TRIM_MEMORY_COMPLETE
auch.
Erwägen Sie die Verwendung von onUserLeaveHint. Dies wird nur aufgerufen, wenn Ihre App in den Hintergrund tritt. onPause muss Eckfälle behandeln, da es aus anderen Gründen aufgerufen werden kann. Wenn der Benutzer beispielsweise eine andere Aktivität in Ihrer App öffnet, z. B. Ihre Einstellungsseite, wird die onPause-Methode Ihrer Hauptaktivität aufgerufen, obwohl sie sich noch in Ihrer App befindet. Das Verfolgen der Vorgänge führt zu Fehlern, wenn Sie stattdessen einfach den onUserLeaveHint-Rückruf verwenden können, der genau das tut, was Sie verlangen.
Wenn auf UserLeaveHint aufgerufen wird, können Sie ein boolesches inBackground-Flag auf true setzen. Wenn onResume aufgerufen wird, nehmen Sie nur an, dass Sie wieder in den Vordergrund zurückgekehrt sind, wenn das Flag inBackground gesetzt ist. Dies liegt daran, dass onResume auch für Ihre Hauptaktivität aufgerufen wird, wenn sich der Benutzer nur in Ihrem Einstellungsmenü befand und die App nie verlassen hat.
Denken Sie daran, dass onUserLeaveHint in Ihrer Einstellungsaktivität aufgerufen wird, wenn der Benutzer in Ihrem Einstellungsbildschirm auf die Home-Schaltfläche drückt, und wenn er zurückkehrt, wird onResume in Ihrer Einstellungsaktivität aufgerufen. Wenn Sie nur diesen Erkennungscode in Ihrer Hauptaktivität haben, werden Sie diesen Anwendungsfall verpassen. Um diesen Code in all Ihren Aktivitäten zu haben, ohne Code zu duplizieren, verfügen Sie über eine abstrakte Aktivitätsklasse, die die Aktivität erweitert, und fügen Sie Ihren gemeinsamen Code ein. Dann kann jede Aktivität, die Sie haben, diese abstrakte Aktivität erweitern.
Zum Beispiel:
public abstract AbstractActivity extends Activity {
private static boolean inBackground = false;
@Override
public void onResume() {
if (inBackground) {
// You just came from the background
inBackground = false;
}
else {
// You just returned from another activity within your own app
}
}
@Override
public void onUserLeaveHint() {
inBackground = true;
}
}
public abstract MainActivity extends AbstractActivity {
...
}
public abstract SettingsActivity extends AbstractActivity {
...
}
ActivityLifecycleCallbacks sind möglicherweise von Interesse, aber nicht gut dokumentiert.
Wenn Sie registerActivityLifecycleCallbacks () aufrufen , sollten Sie in der Lage sein, Rückrufe zu erhalten, wenn Aktivitäten erstellt, zerstört usw. werden. Sie können getComponentName () für die Aktivität aufrufen .
Das Paket android.arch.lifecycle bietet Klassen und Schnittstellen, mit denen Sie lebenszyklusbewusste Komponenten erstellen können
Ihre Anwendung sollte die LifecycleObserver-Schnittstelle implementieren:
public class MyApplication extends Application implements LifecycleObserver {
@Override
public void onCreate() {
super.onCreate();
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
private void onAppBackgrounded() {
Log.d("MyApp", "App in background");
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
private void onAppForegrounded() {
Log.d("MyApp", "App in foreground");
}
}
Dazu müssen Sie diese Abhängigkeit zu Ihrer build.gradle-Datei hinzufügen:
dependencies {
implementation "android.arch.lifecycle:extensions:1.1.1"
}
Wie von Google empfohlen, sollten Sie den Code minimieren, der in den Lebenszyklusmethoden von Aktivitäten ausgeführt wird:
Ein übliches Muster besteht darin, die Aktionen der abhängigen Komponenten in den Lebenszyklusmethoden von Aktivitäten und Fragmenten zu implementieren. Dieses Muster führt jedoch zu einer schlechten Organisation des Codes und zur Verbreitung von Fehlern. Durch die Verwendung lebenszyklusbewusster Komponenten können Sie den Code abhängiger Komponenten aus den Lebenszyklusmethoden in die Komponenten selbst verschieben.
Weitere Informationen finden Sie hier: https://developer.android.com/topic/libraries/architecture/lifecycle
Fügen Sie in Ihrer Anwendung den Rückruf hinzu und suchen Sie auf folgende Weise nach Root-Aktivitäten:
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
if (activity.isTaskRoot() && !(activity instanceof YourSplashScreenActivity)) {
Log.e(YourApp.TAG, "Reload defaults on restoring from background.");
loadDefaults();
}
}
});
}
Ich habe ein Projekt auf Github App-Vordergrund-Hintergrund-Listen erstellt
Erstellen Sie eine BaseActivity für alle Aktivitäten in Ihrer Anwendung.
public class BaseActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
public static boolean isAppInFg = false;
public static boolean isScrInFg = false;
public static boolean isChangeScrFg = false;
@Override
protected void onStart() {
if (!isAppInFg) {
isAppInFg = true;
isChangeScrFg = false;
onAppStart();
}
else {
isChangeScrFg = true;
}
isScrInFg = true;
super.onStart();
}
@Override
protected void onStop() {
super.onStop();
if (!isScrInFg || !isChangeScrFg) {
isAppInFg = false;
onAppPause();
}
isScrInFg = false;
}
public void onAppStart() {
// Remove this toast
Toast.makeText(getApplicationContext(), "App in foreground", Toast.LENGTH_LONG).show();
// Your code
}
public void onAppPause() {
// Remove this toast
Toast.makeText(getApplicationContext(), "App in background", Toast.LENGTH_LONG).show();
// Your code
}
}
Verwenden Sie diese BaseActivity nun als Superklasse für alle Ihre Aktivitäten, wie MainActivity BaseActivity erweitert, und onAppStart wird aufgerufen, wenn Sie Ihre Anwendung starten, und onAppPause () wird aufgerufen, wenn die Anwendung von einem beliebigen Bildschirm aus in den Hintergrund tritt.
Dies ist mit ProcessLifecycleOwner ziemlich einfach
Fügen Sie diese Abhängigkeiten hinzu
implementation "android.arch.lifecycle:extensions:$project.archLifecycleVersion"
kapt "android.arch.lifecycle:compiler:$project.archLifecycleVersion"
In Kotlin :
class ForegroundBackgroundListener : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun startSomething() {
Log.v("ProcessLog", "APP IS ON FOREGROUND")
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stopSomething() {
Log.v("ProcessLog", "APP IS IN BACKGROUND")
}
}
Dann in Ihrer Basisaktivität:
override fun onCreate() {
super.onCreate()
ProcessLifecycleOwner.get()
.lifecycle
.addObserver(
ForegroundBackgroundListener()
.also { appObserver = it })
}
Siehe meinen Artikel zu diesem Thema: https://medium.com/@egek92/how-to-actually-detect-foreground-background-changes-in-your-android-application-without-wanting-9719cc822c48
Sie können den ProcessLifecycleOwner verwenden, um einen Lebenszyklusbeobachter daran anzuhängen .
public class ForegroundLifecycleObserver implements LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
public void onAppCreated() {
Timber.d("onAppCreated() called");
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onAppStarted() {
Timber.d("onAppStarted() called");
}
@OnLifecycleEvent(Event.ON_RESUME)
public void onAppResumed() {
Timber.d("onAppResumed() called");
}
@OnLifecycleEvent(Event.ON_PAUSE)
public void onAppPaused() {
Timber.d("onAppPaused() called");
}
@OnLifecycleEvent(Event.ON_STOP)
public void onAppStopped() {
Timber.d("onAppStopped() called");
}
}
dann onCreate()
nennen Sie in Ihrer Anwendungsklasse Folgendes:
ProcessLifecycleOwner.get().getLifecycle().addObserver(new ForegroundLifecycleObserver());
Auf diese Weise können Sie die Ereignisse ON_PAUSE
und ON_STOP
Anwendungen Ihrer Anwendung erfassen, die im Hintergrund auftreten.
Es gibt keine einfachen Lebenszyklusmethoden, mit denen Sie feststellen können, wann die gesamte Anwendung in den Hintergrund tritt.
Ich habe das auf einfache Weise gemacht. Befolgen Sie die folgenden Anweisungen, um die Hintergrund- / Vordergrundphase der Anwendung zu erkennen.
Mit einer kleinen Problemumgehung ist dies möglich. Hier hilft ActivityLifecycleCallbacks . Lassen Sie mich Schritt für Schritt durchgehen.
Erstellen Sie zunächst eine Klasse, die die android.app.Application erweitert und die ActivityLifecycleCallbacks- Schnittstelle implementiert . Registrieren Sie in Application.onCreate () den Rückruf.
public class App extends Application implements
Application.ActivityLifecycleCallbacks {
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(this);
}
}
Registrieren Sie die App-Klasse im Manifest wie folgt <application android:name=".App"
.
Es befindet sich mindestens eine Aktivität im gestarteten Zustand, wenn sich die App im Vordergrund befindet, und es befindet sich keine Aktivität im gestarteten Zustand, wenn sich die App im Hintergrund befindet.
Deklarieren Sie 2 Variablen wie folgt in der Klasse „App“.
private int activityReferences = 0;
private boolean isActivityChangingConfigurations = false;
activityReferences
hält die Anzahl der Aktivitäten im gestarteten Zustand. isActivityChangingConfigurations
ist ein Flag, das angibt, ob die aktuelle Aktivität wie ein Orientierungsschalter eine Konfigurationsänderung durchläuft.
Mit dem folgenden Code können Sie feststellen, ob die App im Vordergrund steht.
@Override
public void onActivityStarted(Activity activity) {
if (++activityReferences == 1 && !isActivityChangingConfigurations) {
// App enters foreground
}
}
So erkennen Sie, ob die App in den Hintergrund tritt.
@Override
public void onActivityStopped(Activity activity) {
isActivityChangingConfigurations = activity.isChangingConfigurations();
if (--activityReferences == 0 && !isActivityChangingConfigurations) {
// App enters background
}
}
Wie es funktioniert:
Dies ist ein kleiner Trick, der mit der Art und Weise ausgeführt wird, wie die Lifecycle-Methoden nacheinander aufgerufen werden. Lassen Sie mich ein Szenario durchgehen.
Angenommen, der Benutzer startet die App und die Startaktivität A wird gestartet. Die Lifecycle-Aufrufe lauten:
A.onCreate ()
A.onStart () (++ activityReferences == 1) (App tritt in den Vordergrund)
A.onResume ()
Jetzt startet Aktivität A Aktivität B.
A.onPause ()
B.onCreate ()
B.onStart () (++ activityReferences == 2)
B.onResume ()
A.onStop () (--activityReferences == 1)
Dann navigiert der Benutzer von Aktivität B zurück.
B.onPause ()
A.onStart () (++ activityReferences == 2)
A.onResume ()
B.onStop () (--activityReferences == 1)
B.onDestroy ()
Dann drückt der Benutzer die Home-Taste.
A.onPause ()
A.onStop () (--activityReferences == 0) (App gibt Hintergrund ein)
Falls der Benutzer die Home-Taste von Aktivität B anstelle der Zurück-Taste drückt, ist dies immer noch derselbe und die Aktivitätsreferenzen 0
. Daher können wir erkennen, wie die App den Hintergrund betritt.
Also, was ist die Rolle von isActivityChangingConfigurations
? Angenommen, die Aktivität B ändert im obigen Szenario die Ausrichtung. Die Rückrufsequenz lautet:
B.onPause ()
B.onStop () (--activityReferences == 0) (App gibt Hintergrund ein ??)
B.onDestroy ()
B.onCreate ()
B.onStart () (++ activityReferences == 1) (App tritt in den Vordergrund ??)
B.onResume ()
Aus diesem Grund haben wir eine zusätzliche Überprüfung isActivityChangingConfigurations
, um das Szenario zu vermeiden, in dem die Aktivität die Konfigurationsänderungen durchläuft.
Ich habe eine gute Methode gefunden, um Anwendungen zu erkennen, unabhängig davon, ob sie in den Vordergrund oder in den Hintergrund treten. Hier ist mein Code . Hoffe das hilft dir.
/**
* Custom Application which can detect application state of whether it enter
* background or enter foreground.
*
* @reference http://www.vardhan-justlikethat.blogspot.sg/2014/02/android-solution-to-detect-when-android.html
*/
public abstract class StatusApplication extends Application implements ActivityLifecycleCallbacks {
public static final int STATE_UNKNOWN = 0x00;
public static final int STATE_CREATED = 0x01;
public static final int STATE_STARTED = 0x02;
public static final int STATE_RESUMED = 0x03;
public static final int STATE_PAUSED = 0x04;
public static final int STATE_STOPPED = 0x05;
public static final int STATE_DESTROYED = 0x06;
private static final int FLAG_STATE_FOREGROUND = -1;
private static final int FLAG_STATE_BACKGROUND = -2;
private int mCurrentState = STATE_UNKNOWN;
private int mStateFlag = FLAG_STATE_BACKGROUND;
@Override
public void onCreate() {
super.onCreate();
mCurrentState = STATE_UNKNOWN;
registerActivityLifecycleCallbacks(this);
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
// mCurrentState = STATE_CREATED;
}
@Override
public void onActivityStarted(Activity activity) {
if (mCurrentState == STATE_UNKNOWN || mCurrentState == STATE_STOPPED) {
if (mStateFlag == FLAG_STATE_BACKGROUND) {
applicationWillEnterForeground();
mStateFlag = FLAG_STATE_FOREGROUND;
}
}
mCurrentState = STATE_STARTED;
}
@Override
public void onActivityResumed(Activity activity) {
mCurrentState = STATE_RESUMED;
}
@Override
public void onActivityPaused(Activity activity) {
mCurrentState = STATE_PAUSED;
}
@Override
public void onActivityStopped(Activity activity) {
mCurrentState = STATE_STOPPED;
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
mCurrentState = STATE_DESTROYED;
}
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
if (mCurrentState == STATE_STOPPED && level >= TRIM_MEMORY_UI_HIDDEN) {
if (mStateFlag == FLAG_STATE_FOREGROUND) {
applicationDidEnterBackground();
mStateFlag = FLAG_STATE_BACKGROUND;
}
}else if (mCurrentState == STATE_DESTROYED && level >= TRIM_MEMORY_UI_HIDDEN) {
if (mStateFlag == FLAG_STATE_FOREGROUND) {
applicationDidDestroyed();
mStateFlag = FLAG_STATE_BACKGROUND;
}
}
}
/**
* The method be called when the application been destroyed. But when the
* device screen off,this method will not invoked.
*/
protected abstract void applicationDidDestroyed();
/**
* The method be called when the application enter background. But when the
* device screen off,this method will not invoked.
*/
protected abstract void applicationDidEnterBackground();
/**
* The method be called when the application enter foreground.
*/
protected abstract void applicationWillEnterForeground();
}}
Edit 2: Was ich unten geschrieben habe, funktioniert eigentlich nicht. Google hat eine App abgelehnt, die einen Aufruf von ActivityManager.getRunningTasks () enthält. Aus der Dokumentation geht hervor, dass diese API nur zu Debugging- und Entwicklungszwecken dient. Ich werde diesen Beitrag aktualisieren, sobald ich Zeit habe, das GitHub-Projekt unten mit einem neuen Schema zu aktualisieren, das Timer verwendet und fast genauso gut ist.
Bearbeiten 1: Ich habe einen Blog-Beitrag geschrieben und ein einfaches GitHub-Repository erstellt , um dies wirklich einfach zu machen.
Die akzeptierte und die am besten bewertete Antwort sind nicht wirklich der beste Ansatz. Die Implementierung von isApplicationBroughtToBackground () durch die am besten bewertete Antwort behandelt nicht die Situation, in der die Hauptaktivität der Anwendung einer Aktivität nachgibt, die in derselben Anwendung definiert ist, aber ein anderes Java-Paket enthält. Ich habe einen Weg gefunden, dies zu tun, der in diesem Fall funktioniert.
Rufen Sie dies in onPause () auf, und Sie erfahren, ob Ihre Anwendung in den Hintergrund tritt, weil eine andere Anwendung gestartet wurde oder der Benutzer die Home-Taste gedrückt hat.
public static boolean isApplicationBroughtToBackground(final Activity activity) {
ActivityManager activityManager = (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> tasks = activityManager.getRunningTasks(1);
// Check the top Activity against the list of Activities contained in the Application's package.
if (!tasks.isEmpty()) {
ComponentName topActivity = tasks.get(0).topActivity;
try {
PackageInfo pi = activity.getPackageManager().getPackageInfo(activity.getPackageName(), PackageManager.GET_ACTIVITIES);
for (ActivityInfo activityInfo : pi.activities) {
if(topActivity.getClassName().equals(activityInfo.name)) {
return false;
}
}
} catch( PackageManager.NameNotFoundException e) {
return false; // Never happens.
}
}
return true;
}
Richtige Antwort hier
Erstellen Sie eine Klasse mit dem Namen MyApp wie folgt:
public class MyApp implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {
private Context context;
public void setContext(Context context)
{
this.context = context;
}
private boolean isInBackground = false;
@Override
public void onTrimMemory(final int level) {
if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
isInBackground = true;
Log.d("status = ","we are out");
}
}
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
if(isInBackground){
isInBackground = false;
Log.d("status = ","we are in");
}
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
@Override
public void onConfigurationChanged(Configuration configuration) {
}
@Override
public void onLowMemory() {
}
}
Fügen Sie dann überall den gewünschten Code hinzu (bessere erste Aktivität, die in der App gestartet wurde):
MyApp myApp = new MyApp();
registerComponentCallbacks(myApp);
getApplication().registerActivityLifecycleCallbacks(myApp);
Erledigt! Wenn sich die App im Hintergrund befindet, erhalten wir ein Protokoll, status : we are out
und wenn wir in die App gehen, erhalten wir ein Protokollstatus : we are out
Meine Lösung wurde von der Antwort von @ d60402 inspiriert und basiert auch auf einem Zeitfenster, verwendet jedoch nicht Folgendes Timer
:
public abstract class BaseActivity extends ActionBarActivity {
protected boolean wasInBackground = false;
@Override
protected void onStart() {
super.onStart();
wasInBackground = getApp().isInBackground;
getApp().isInBackground = false;
getApp().lastForegroundTransition = System.currentTimeMillis();
}
@Override
protected void onStop() {
super.onStop();
if( 1500 < System.currentTimeMillis() - getApp().lastForegroundTransition )
getApp().isInBackground = true;
}
protected SingletonApplication getApp(){
return (SingletonApplication)getApplication();
}
}
wo das SingletonApplication
eine Erweiterung der Application
Klasse ist:
public class SingletonApplication extends Application {
public boolean isInBackground = false;
public long lastForegroundTransition = 0;
}
Ich habe dies mit Google Analytics EasyTracker verwendet und es hat funktioniert. Es könnte erweitert werden, um das zu tun, was Sie suchen, indem Sie eine einfache Ganzzahl verwenden.
public class MainApplication extends Application {
int isAppBackgrounded = 0;
@Override
public void onCreate() {
super.onCreate();
appBackgroundedDetector();
}
private void appBackgroundedDetector() {
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}
@Override
public void onActivityStarted(Activity activity) {
EasyTracker.getInstance(MainApplication.this).activityStart(activity);
}
@Override
public void onActivityResumed(Activity activity) {
isAppBackgrounded++;
if (isAppBackgrounded > 0) {
// Do something here
}
}
@Override
public void onActivityPaused(Activity activity) {
isAppBackgrounded--;
}
@Override
public void onActivityStopped(Activity activity) {
EasyTracker.getInstance(MainApplication.this).activityStop(activity);
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
});
}
}
Ich weiß, dass es etwas spät ist, aber ich denke, all diese Antworten haben einige Probleme, während ich es wie unten gemacht habe, und das funktioniert perfekt.
Erstellen Sie einen Aktivitätslebenszyklus-Rückruf wie folgt:
class ActivityLifeCycle implements ActivityLifecycleCallbacks{
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override
public void onActivityStarted(Activity activity) {
}
Activity lastActivity;
@Override
public void onActivityResumed(Activity activity) {
//if (null == lastActivity || (activity != null && activity == lastActivity)) //use this condition instead if you want to be informed also when app has been killed or started for the first time
if (activity != null && activity == lastActivity)
{
Toast.makeText(MyApp.this, "NOW!", Toast.LENGTH_LONG).show();
}
lastActivity = activity;
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
}
und registrieren Sie es einfach in Ihrer Anwendungsklasse wie folgt:
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new ActivityLifeCycle());
}
Dies scheint eine der kompliziertesten Fragen in Android zu sein, da Android (zum jetzigen Zeitpunkt) keine iOS-Entsprechungen applicationDidEnterBackground()
oder applicationWillEnterForeground()
Rückrufe hat. Ich habe eine AppState-Bibliothek verwendet , die von @jenzz zusammengestellt wurde .
[AppState] ist eine einfache, reaktive Android-Bibliothek, die auf RxJava basiert und Änderungen des App-Status überwacht. Es benachrichtigt Abonnenten jedes Mal, wenn die App in den Hintergrund tritt und wieder in den Vordergrund tritt.
Es stellte sich heraus, dass dies genau das ist, was ich brauchte, insbesondere weil meine App mehrere Aktivitäten hatte, sodass das einfache Überprüfen onStart()
oder Aktivieren onStop()
einer Aktivität dies nicht verhindern würde.
Zuerst habe ich diese Abhängigkeiten zu gradle hinzugefügt:
dependencies {
compile 'com.jenzz.appstate:appstate:3.0.1'
compile 'com.jenzz.appstate:adapter-rxjava2:3.0.1'
}
Dann war es einfach, diese Zeilen an einer geeigneten Stelle in Ihrem Code hinzuzufügen:
//Note that this uses RxJava 2.x adapter. Check the referenced github site for other ways of using observable
Observable<AppState> appState = RxAppStateMonitor.monitor(myApplication);
//where myApplication is a subclass of android.app.Application
appState.subscribe(new Consumer<AppState>() {
@Override
public void accept(@io.reactivex.annotations.NonNull AppState appState) throws Exception {
switch (appState) {
case FOREGROUND:
Log.i("info","App entered foreground");
break;
case BACKGROUND:
Log.i("info","App entered background");
break;
}
}
});
Abhängig davon, wie Sie das Observable abonnieren, müssen Sie es möglicherweise abbestellen, um Speicherverluste zu vermeiden. Nochmals mehr Infos auf der Github-Seite .
Dies ist die geänderte Version der Antwort von @ d60402: https://stackoverflow.com/a/15573121/4747587
Mach alles was dort erwähnt wird. Aber anstatt ein zu haben Base Activity
und das als Elternteil für jede Aktivität und das Überschreiben des onResume()
und zu machen onPause
, gehen Sie wie folgt vor:
Fügen Sie in Ihrer Anwendungsklasse die folgende Zeile hinzu:
registerActivityLifecycleCallbacks (Rückruf Application.ActivityLifecycleCallbacks);
Dies callback
hat alle Methoden des Aktivitätslebenszyklus und Sie können jetzt onActivityResumed()
und überschreiben onActivityPaused()
.
Schauen Sie sich diese Übersicht an: https://gist.github.com/thsaravana/1fa576b6af9fc8fff20acfb2ac79fa1b
Sie können dies leicht mit Hilfe von ActivityLifecycleCallbacks
und so ComponentCallbacks2
etwas wie unten erreichen.
Erstellen Sie eine Klasse, AppLifeCycleHandler
die über diesen Schnittstellen implementiert ist.
package com.sample.app;
import android.app.Activity;
import android.app.Application;
import android.content.ComponentCallbacks2;
import android.content.res.Configuration;
import android.os.Bundle;
/**
* Created by Naveen on 17/04/18
*/
public class AppLifeCycleHandler
implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {
AppLifeCycleCallback appLifeCycleCallback;
boolean appInForeground;
public AppLifeCycleHandler(AppLifeCycleCallback appLifeCycleCallback) {
this.appLifeCycleCallback = appLifeCycleCallback;
}
@Override
public void onActivityResumed(Activity activity) {
if (!appInForeground) {
appInForeground = true;
appLifeCycleCallback.onAppForeground();
}
}
@Override
public void onTrimMemory(int i) {
if (i == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
appInForeground = false;
appLifeCycleCallback.onAppBackground();
}
}
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
@Override
public void onConfigurationChanged(Configuration configuration) {
}
@Override
public void onLowMemory() {
}
interface AppLifeCycleCallback {
void onAppBackground();
void onAppForeground();
}
}
In Ihrer Klasse, die Application
implementiert AppLifeCycleCallback
, implementieren Sie , um die Rückrufe zu erhalten, wenn die App zwischen Vordergrund und Hintergrund wechselt. So etwas wie unten.
public class BaseApplication extends Application implements AppLifeCycleHandler.AppLifeCycleCallback{
@Override
public void onCreate() {
super.onCreate();
AppLifeCycleHandler appLifeCycleHandler = new AppLifeCycleHandler(this);
registerActivityLifecycleCallbacks(appLifeCycleHandler);
registerComponentCallbacks(appLifeCycleHandler);
}
@Override
public void onAppBackground() {
Log.d("LifecycleEvent", "onAppBackground");
}
@Override
public void onAppForeground() {
Log.d("LifecycleEvent", "onAppForeground");
}
}
Hoffe das hilft.
BEARBEITEN Alternativ können Sie jetzt die Life Cycle-fähige Architekturkomponente verwenden.
Da ich keinen Ansatz gefunden habe, der auch die Rotation ohne Überprüfung der Zeitstempel handhabt, dachte ich, ich teile auch, wie wir es jetzt in unserer App machen. Die einzige Ergänzung zu dieser Antwort https://stackoverflow.com/a/42679191/5119746 ist, dass wir auch die Ausrichtung berücksichtigen.
class MyApplication : Application(), Application.ActivityLifecycleCallbacks {
// Members
private var mAppIsInBackground = false
private var mCurrentOrientation: Int? = null
private var mOrientationWasChanged = false
private var mResumed = 0
private var mPaused = 0
Dann haben wir für die Rückrufe zuerst den Lebenslauf:
// ActivityLifecycleCallbacks
override fun onActivityResumed(activity: Activity?) {
mResumed++
if (mAppIsInBackground) {
// !!! App came from background !!! Insert code
mAppIsInBackground = false
}
mOrientationWasChanged = false
}
Und onActivityStopped:
override fun onActivityStopped(activity: Activity?) {
if (mResumed == mPaused && !mOrientationWasChanged) {
// !!! App moved to background !!! Insert code
mAppIsInBackground = true
}
Und dann kommt hier der Zusatz: Auf Orientierungsänderungen prüfen:
override fun onConfigurationChanged(newConfig: Configuration) {
if (newConfig.orientation != mCurrentOrientation) {
mCurrentOrientation = newConfig.orientation
mOrientationWasChanged = true
}
super.onConfigurationChanged(newConfig)
}
Das ist es. Hoffe das hilft jemandem :)
Wir können diese Lösung erweitern mit LiveData
:
class AppForegroundStateLiveData : LiveData<AppForegroundStateLiveData.State>() {
private var lifecycleListener: LifecycleObserver? = null
override fun onActive() {
super.onActive()
lifecycleListener = AppLifecycleListener().also {
ProcessLifecycleOwner.get().lifecycle.addObserver(it)
}
}
override fun onInactive() {
super.onInactive()
lifecycleListener?.let {
this.lifecycleListener = null
ProcessLifecycleOwner.get().lifecycle.removeObserver(it)
}
}
internal inner class AppLifecycleListener : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onMoveToForeground() {
value = State.FOREGROUND
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onMoveToBackground() {
value = State.BACKGROUND
}
}
enum class State {
FOREGROUND, BACKGROUND
}
}
Jetzt können wir diese LiveData abonnieren und die erforderlichen Ereignisse abfangen. Zum Beispiel:
appForegroundStateLiveData.observeForever { state ->
when(state) {
AppForegroundStateLiveData.State.FOREGROUND -> { /* app move to foreground */ }
AppForegroundStateLiveData.State.BACKGROUND -> { /* app move to background */ }
}
}
Diese Antworten scheinen nicht richtig zu sein. Diese Methoden werden auch aufgerufen, wenn eine andere Aktivität beginnt und endet. Was Sie tun können, ist, ein globales Flag beizubehalten (ja, globale Werte sind schlecht :) und dies bei jedem Start einer neuen Aktivität auf true zu setzen. Setzen Sie es im onCreate jeder Aktivität auf false. Dann aktivieren Sie in der onPause dieses Flag. Wenn es falsch ist, tritt Ihre App in den Hintergrund oder wird getötet.