Ich versuche, eine Anwendung zu schreiben, die etwas Bestimmtes tut, wenn sie nach einer gewissen Zeit wieder in den Vordergrund gebracht wird. Gibt es eine Möglichkeit zu erkennen, wann eine App in den Hintergrund oder in den Vordergrund gebracht wird?
Antworten
Zu viele Anzeigen?Auf der Grundlage von Martín Marconcinis Antwort (danke!) habe ich schließlich 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 dann Folgendes zu Ihrer onCreate() der 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 funktionieren, aber es wurde in unserer App gut getestet und funktioniert in der Tat überraschend gut in allen Fällen, einschließlich des Wechsels zum Startbildschirm über die Schaltfläche "Home", über die Schaltfläche "Zurück" oder nach der Bildschirmsperre. Probieren Sie es aus.
Die Idee ist, dass Android, wenn es im Vordergrund ist, immer eine neue Aktivität startet, bevor die vorherige beendet wird. Das ist nicht garantiert, aber das ist, wie es funktioniert. BTW, Flurry scheint die gleiche Logik zu verwenden (nur eine Vermutung, ich habe nicht überprüft, dass, aber es hakt an den gleichen Ereignissen).
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: Wie in den Kommentaren erwähnt, sind wir in späteren Versionen des Codes auch zu onStart() übergegangen. Außerdem füge ich Super-Aufrufe hinzu, die in meinem ursprünglichen Beitrag fehlten, weil es sich eher um ein Konzept als um einen funktionierenden Code handelte.
Wenn Ihre Anwendung aus mehreren Aktivitäten und/oder gestapelten Aktivitäten wie einem Tabbar-Widget besteht, dann funktioniert das Überschreiben von onPause() und onResume() nicht. D.h. beim Starten einer neuen Aktivität wird die aktuelle Aktivität pausiert, bevor die neue erstellt wird. Dasselbe gilt für das Beenden einer Aktivität (mittels "Zurück"-Button).
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 den Vergleich von Paketnamen prüft, ob die am häufigsten ausgeführte Aktivität auf dem Gerät zu einer 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 Rahmen von Droid-Fu (jetzt Ignition genannt) gefunden.
Die zweite Methode, die ich selbst implementiert habe, erfordert nicht die Berechtigung GET_TASKS, was gut ist. Stattdessen ist sie ein wenig komplizierter zu implementieren.
In Ihrer MainApplication-Klasse haben Sie eine Variable, die die Anzahl der laufenden Aktivitäten in Ihrer Anwendung verfolgt. In onResume() für jede Aktivität erhöhen Sie die Variable und in onPause() verringern Sie sie.
Wenn die Anzahl der laufenden Aktivitäten 0 erreicht, wird die Anwendung in den Hintergrund gestellt, wenn die folgenden Bedingungen erfüllt sind:
- Die pausierte Aktivität wird nicht beendet (die Schaltfläche "Zurück" wurde verwendet). Dies kann mit Hilfe der Methode activity.isFinishing() festgestellt werden
- Eine neue Aktivität (gleicher Paketname) wird nicht gestartet. Sie können die Methode startActivity() überschreiben, um eine Variable zu setzen, die dies anzeigt, und sie dann in onPostResume() zurücksetzen, die die letzte Methode ist, die ausgeführt wird, wenn eine Aktivität erstellt/fortgesetzt wird.
Wenn Sie erkennen können, dass die Anwendung in den Hintergrund zurückgetreten ist, ist es einfach zu erkennen, wann sie wieder in den Vordergrund gebracht wird.
Erstellen einer Klasse die die Application
. Dann können wir darin seine Überschreibungsmethode verwenden, onTrimMemory()
.
Um zu erkennen, ob die Anwendung in den Hintergrund gegangen 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
}
}
Erwägen Sie die Verwendung von onUserLeaveHint. Dies wird nur aufgerufen, wenn Ihre App in den Hintergrund geht. onPause wird Eckfälle zu behandeln, da es aus anderen Gründen aufgerufen werden kann; zum Beispiel, wenn der Benutzer eine andere Aktivität in Ihrer App wie Ihre Einstellungen Seite öffnet, wird Ihre Hauptaktivität onPause Methode aufgerufen werden, obwohl sie immer noch in Ihrer App sind; Verfolgen, was geht in wird zu Bugs führen, wenn Sie stattdessen einfach die onUserLeaveHint Callback, die tut, was Sie fragen, verwenden können.
Beim Aufruf von UserLeaveHint können Sie ein boolesches inBackground-Flag auf true setzen. Beim Aufruf von onResume wird nur dann angenommen, dass Sie in den Vordergrund zurückgekehrt sind, wenn das inBackground-Flag gesetzt ist. Der Grund dafür ist, dass onResume auch bei Ihrer Hauptaktivität aufgerufen wird, wenn der Benutzer nur in Ihrem Einstellungsmenü war und die App nie verlassen hat.
Denken Sie daran, dass onUserLeaveHint in Ihrer Einstellungsaktivität aufgerufen wird, wenn der Benutzer die Home-Taste drückt, während er sich in Ihrem Einstellungsbildschirm befindet, und onResume in Ihrer Einstellungsaktivität, wenn er zurückkehrt. Wenn Sie diesen Erkennungscode nur in Ihrer Hauptaktivität haben, entgeht Ihnen dieser Anwendungsfall. Um diesen Code in all Ihren Aktivitäten zu haben, ohne Code zu duplizieren, erstellen Sie eine abstrakte Aktivitätsklasse, die Activity erweitert, und fügen Sie Ihren gemeinsamen Code in diese Klasse ein. Dann kann jede Ihrer Aktivitäten 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 {
...
}