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?Ich habe ein Projekt auf Github erstellt app-foreground-background-listen
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 nun diese BaseActivity als Superklasse für alle Ihre Aktivitäten, z. B. MainActivity erweitert BaseActivity, und onAppStart wird aufgerufen, wenn Sie Ihre Anwendung starten, und onAppPause() wird aufgerufen, wenn die Anwendung von einem beliebigen Bildschirm in den Hintergrund geht.
Es gibt keine einfachen Lifecycle-Methoden, die Ihnen sagen, wann die gesamte Anwendung in den Hintergrund oder in den Vordergrund tritt.
Ich habe dies auf einfache Weise getan. Befolgen Sie die folgenden Anweisungen, um die Hintergrund-/Vordergrundphase der Anwendung zu erkennen.
Mit einer kleinen Umgehung ist es möglich. Hier, ActivityLifecycleCallbacks kommt zur Rettung. Lassen Sie mich Schritt für Schritt vorgehen.
-
Erstellen Sie zunächst eine Klasse, die die Klasse Android.app.Anwendung und implementiert die ActivityLifecycleCallbacks Schnittstelle. In Application.onCreate() registrieren Sie den Callback.
public class App extends Application implements Application.ActivityLifecycleCallbacks { @Override public void onCreate() { super.onCreate(); registerActivityLifecycleCallbacks(this); } }
-
Registrieren Sie die Klasse "App" im Manifest wie unten beschrieben,
<application android:name=".App"
. -
Es gibt mindestens eine Aktivität im gestarteten Zustand, wenn die App im Vordergrund ist, und es gibt keine Aktivität im gestarteten Zustand, wenn die App im Hintergrund ist.
Deklarieren Sie 2 Variablen wie unten in der Klasse "App".
private int activityReferences = 0; private boolean isActivityChangingConfigurations = false;
activityReferences
wird die Anzahl der Aktivitäten in der gestartet Zustand.isActivityChangingConfigurations
ist ein Flag, das anzeigt, ob die aktuelle Aktivität eine Konfigurationsänderung durchläuft, wie z. B. einen Orientierungswechsel. -
Mit dem folgenden Code können Sie erkennen, ob die App im Vordergrund ist.
@Override public void onActivityStarted(Activity activity) { if (++activityReferences == 1 && !isActivityChangingConfigurations) { // App enters foreground } }
-
So erkennen Sie, ob die App in den Hintergrund geht.
@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 zusammenhängt, wie die Lebenszyklusmethoden nacheinander aufgerufen werden. Lassen Sie mich ein Szenario durchspielen.
Angenommen, der Benutzer startet die App und die Launcher-Aktivität A wird gestartet. Die Lifecycle-Aufrufe werden sein,
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 geht in den Hintergrund)
Wenn der Benutzer in Aktivität B die Home-Taste anstelle der Zurück-Taste drückt, ist es immer noch dasselbe und die activityReferences sind 0
. Daher können wir erkennen, wie die App in den Hintergrund tritt.
Was ist also die Rolle der isActivityChangingConfigurations
? Im obigen Szenario wird angenommen, dass die Aktivität B die Ausrichtung ändert. Die Rückrufsequenz wird sein,
B.onPause()
B.onStop() (--activityReferences == 0) (App tritt in den Hintergrund??)
B.onDestroy()
B.onCreate()
B.onStart() (++activityReferences == 1) (App tritt in den Vordergrund??)
B.onResume()
Deshalb haben wir eine zusätzliche Prüfung von isActivityChangingConfigurations
um das Szenario zu vermeiden, wenn die Aktivität die Konfigurationsänderungen durchläuft.
Ich habe eine gute Methode gefunden, um zu erkennen, ob eine Anwendung im Vorder- oder Hintergrund läuft. Hier ist mein Code . Ich hoffe, das hilft Ihnen.
/**
* 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, wird nicht wirklich funktionieren. Google hat eine App abgelehnt, die einen Aufruf von ActivityManager.getRunningTasks() enthält. Von die Dokumentation ist es offensichtlich, dass diese API nur für Debugging- und Entwicklungszwecke gedacht ist. 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.
Edit 1: Ich habe eine Blogbeitrag und erstellt ein einfaches GitHub-Repository um dies wirklich einfach zu machen.
Die akzeptierte und die am besten bewertete Antwort sind beide nicht wirklich der beste Ansatz. Die Implementierung von isApplicationBroughtToBackground() in der am besten bewerteten Antwort behandelt nicht die Situation, in der die Hauptaktivität der Anwendung einer Aktivität weicht, die in derselben Anwendung definiert ist, aber ein anderes Java-Paket hat. Ich habe einen Weg gefunden, der in diesem Fall funktioniert.
Rufen Sie dies in onPause() auf, und es wird Ihnen mitteilen, ob Ihre Anwendung in den Hintergrund geht, 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;
}