我正在尝试编写一个应用程序,在一段时间后恢复到前台时会执行特定操作。是否有一种方法可以检测应用程序何时被发送到后台或恢复到前台?
我正在尝试编写一个应用程序,在一段时间后恢复到前台时会执行特定操作。是否有一种方法可以检测应用程序何时被发送到后台或恢复到前台?
android.arch.lifecycle包提供了类和接口,让您构建具有生命周期意识的组件。
您的应用程序应该实现LifecycleObserver接口:
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");
}
}
要做到这一点,您需要将此依赖项添加到您的build.gradle文件中:dependencies {
implementation "android.arch.lifecycle:extensions:1.1.1"
}
根据Google的建议,您应该尽量减少在活动的生命周期方法中执行的代码:
一种常见的模式是在活动和片段的生命周期方法中实现依赖组件的操作。然而,这种模式会导致代码组织不良和错误的繁殖。通过使用生命周期感知组件,您可以将相关组件的代码从生命周期方法中移出并放入组件本身。
您可以在此处阅读更多: https://developer.android.com/topic/libraries/architecture/lifecycle
@OnLifecycleEvent
已被弃用。请参考https://developer.android.com/jetpack/androidx/releases/lifecycle#2.4.0-beta01。 - AkitoActivityLifecycleCallbacks 可能会引起你的兴趣,但是它的文档不是很详细。
不过,如果你调用 registerActivityLifecycleCallbacks() 方法,你就可以获得在 Activity 创建、销毁等时刻的回调。你也可以为所创建的 Activity 调用 getComponentName() 方法。
@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();
}
}
});
}
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");
}
}
ProcessLifecycleOwner.get().getLifecycle().addObserver(new ForegroundLifecycleObserver());
ON_PAUSE
和 ON_STOP
事件。我在Github上创建了一个项目app-foreground-background-listen
为你的应用程序中的所有Activity创建一个BaseActivity。
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
}
}
现在将这个BaseActivity用作所有Activity的超类,例如MainActivity扩展BaseActivity,当您启动应用程序时将调用onAppStart,当应用程序从任何屏幕进入后台时将调用onAppPause()。
implementation "android.arch.lifecycle:extensions:$project.archLifecycleVersion"
kapt "android.arch.lifecycle:compiler:$project.archLifecycleVersion"
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")
}
}
然后在您的基本活动中:
override fun onCreate() {
super.onCreate()
ProcessLifecycleOwner.get()
.lifecycle
.addObserver(
ForegroundBackgroundListener()
.also { appObserver = it })
}
@OnLifecycleEvent
已被弃用。请参阅 https://developer.android.com/jetpack/androidx/releases/lifecycle#2.4.0-beta01。 - Akito没有直接的生命周期方法可以告诉您整个应用何时进入后台/前台。
我用简单的方法做到了这一点。按照以下说明检测应用程序的后台/前台阶段。
通过一些小技巧,是可能的。在这里,ActivityLifecycleCallbacks 来挽救。让我一步步地走过去。
First, create a class that extends the android.app.Application and implements the ActivityLifecycleCallbacks interface. In the Application.onCreate(), register the callback.
public class App extends Application implements
Application.ActivityLifecycleCallbacks {
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(this);
}
}
Register the “App” class in the Manifest as below, <application android:name=".App"
.
There will be at least one Activity in the started state when the app is in the foreground and there will be no Activity in the started state when the app is in the background.
Declare 2 variables as below in the “App” class.
private int activityReferences = 0;
private boolean isActivityChangingConfigurations = false;
activityReferences
will keep the count of number of activities in the started state. isActivityChangingConfigurations
is a flag to indicate if the current Activity is going through configuration change like an orientation switch.
Using the following code you can detect if the App comes foreground.
@Override
public void onActivityStarted(Activity activity) {
if (++activityReferences == 1 && !isActivityChangingConfigurations) {
// App enters foreground
}
}
This is how to detect if the App goes background.
@Override
public void onActivityStopped(Activity activity) {
isActivityChangingConfigurations = activity.isChangingConfigurations();
if (--activityReferences == 0 && !isActivityChangingConfigurations) {
// App enters background
}
}
工作原理:
这是通过按顺序调用生命周期方法来完成的小技巧。让我演示一个场景。
假设用户启动应用程序并启动启动器活动 A。生命周期调用将是,
A.onCreate()
A.onStart() (++activityReferences == 1)(应用进入前台)
A.onResume()
现在 Activity A 启动了 Activity B。
A.onPause()
B.onCreate()
B.onStart() (++activityReferences == 2)
B.onResume()
A.onStop() (--activityReferences == 1)
然后用户从 Activity B 返回,
B.onPause()
A.onStart() (++activityReferences == 2)
A.onResume()
B.onStop() (--activityReferences == 1)
B.onDestroy()
然后用户按 Home 按钮,
A.onPause()
A.onStop() (--activityReferences == 0)(应用进入后台)
如果用户从 Activity B 而不是 Back 按钮按 Home 按钮,则仍然是相同的,并且 activityReferences 将为 0
。因此,我们可以检测到应用程序进入后台。
那么,isActivityChangingConfigurations
的作用是什么?在上述情况下,假设 Activity B 更改了方向。回调序列将是,
B.onPause()
B.onStop() (--activityReferences == 0)(应用进入后台??)
B.onDestroy()
B.onCreate()
B.onStart() (++activityReferences == 1)(应用进入前台??)
B.onResume()
这就是为什么我们有一个额外的检查 isActivityChangingConfigurations
来避免 Activity 正在进行配置更改时的情况。
我发现了一种很好的方法来检测应用程序是否进入前台或后台。 这是我的代码。 希望这可以帮助你。
/**
* 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();
}
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;
}