我试图写一个应用程序,做一些具体的时候,它被带回前台后一段时间。是否有一种方法可以检测应用程序是被发送到后台还是被带到前台?


当前回答

这些答案似乎不正确。当另一个活动开始和结束时,也会调用这些方法。你能做的是保留一个全局标志(是的,全局不好:),并在每次启动一个新活动时将其设置为true。在每个活动的onCreate中将其设置为false。然后,在onPause中检查这个标志。如果为假,你的应用就会进入后台,或者被杀死。

其他回答

这似乎是Android中最复杂的问题之一,因为(在撰写本文时)Android没有iOS中等效的applicationDidEnterBackground()或applicationwillenter前台()回调。我使用了一个由@jenzz组合的AppState库。

[AppState]是一个基于RxJava的简单的响应式Android库,用于监控应用程序状态的变化。每当应用程序进入后台并返回前台时,它都会通知订阅者。

事实证明,这正是我所需要的,特别是因为我的应用程序有多个活动,所以简单地检查onStart()或onStop()对一个活动不会削减它。

首先,我将这些依赖项添加到gradle:

dependencies {
    compile 'com.jenzz.appstate:appstate:3.0.1'
    compile 'com.jenzz.appstate:adapter-rxjava2:3.0.1'
}

然后,将这些行添加到代码中适当的位置就很简单了:

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

根据你订阅可观察对象的方式,你可能不得不取消订阅以避免内存泄漏。更多信息再次在github页面。

我发现了一个很好的方法来检测应用程序是否进入前台或后台。 这是我的代码。 希望这对你有帮助。

/**
 * 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();

}

在你的应用程序中添加回调并检查根活动,如下所示:

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

2021年11月更新

实际设置如下

class App : Application() {
    override fun onCreate() {
        super.onCreate()
        ProcessLifecycleOwner.get().lifecycle.addObserver(AppLifecycleListener())
    }
}

class AppLifecycleListener : DefaultLifecycleObserver {

    override fun onStart(owner: LifecycleOwner) { // app moved to foreground
    }

    override fun onStop(owner: LifecycleOwner) { // app moved to background
    }
}

依赖关系

implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-common:$lifecycle_version"

原来的答案

ProcessLifecycleOwner似乎也是一个很有前途的解决方案。

ProcessLifecycleOwner将分派ON_START, ON_RESUME事件,当第一个活动通过这些事件时。ON_PAUSE, ON_STOP,事件将在最后一个活动通过它们后延迟分派。这个延迟足够长,可以保证ProcessLifecycleOwner在由于配置更改而销毁和重新创建活动时不会发送任何事件。

实现可以简单到

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

根据源代码,当前延迟值为700ms。

使用此特性还需要依赖项:

implementation "androidx.lifecycle:lifecycle-extensions:$lifecycleVersion"

这是我的解决方案。只需在您的主Application类中注册这个ActivityLifecycleCallbacks。在评论中,我提到了一个用户配置文件活动边缘情况。该活动只是一个具有透明边缘的活动。

/**
 * This class used Activity lifecycle callbacks to determine when the application goes to the
 * background as well as when it is brought to the foreground.
 */
public class Foreground implements Application.ActivityLifecycleCallbacks
{
    /**
     * How long to wait before checking onStart()/onStop() count to determine if the app has been
     * backgrounded.
     */
    public static final long BACKGROUND_CHECK_DELAY_MS = 500;

    private static Foreground sInstance;

    private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
    private boolean mIsForeground = false;
    private int mCount;

    public static void init(final Application application)
    {
        if (sInstance == null)
        {
            sInstance = new Foreground();
            application.registerActivityLifecycleCallbacks(sInstance);
        }
    }

    public static Foreground getInstance()
    {
        return sInstance;
    }

    public boolean isForeground()
    {
        return mIsForeground;
    }

    public boolean isBackground()
    {
        return !mIsForeground;
    }

    @Override
    public void onActivityStarted(final Activity activity)
    {
        mCount++;

        // Remove posted Runnables so any Meteor disconnect is cancelled if the user comes back to
        // the app before it runs.
        mMainThreadHandler.removeCallbacksAndMessages(null);

        if (!mIsForeground)
        {
            mIsForeground = true;
        }
    }

    @Override
    public void onActivityStopped(final Activity activity)
    {
        mCount--;

        // A transparent Activity like community user profile won't stop the Activity that launched
        // it. If you launch another Activity from the user profile or hit the Android home button,
        // there are two onStops(). One for the user profile and one for its parent. Remove any
        // posted Runnables so we don't get two session ended events.
        mMainThreadHandler.removeCallbacksAndMessages(null);
        mMainThreadHandler.postDelayed(new Runnable()
        {
            @Override
            public void run()
            {
                if (mCount == 0)
                {
                    mIsForeground = false;
                }
            }
        }, BACKGROUND_CHECK_DELAY_MS);
    }

    @Override
    public void onActivityCreated(final Activity activity, final Bundle savedInstanceState)
    {

    }

    @Override
    public void onActivityResumed(final Activity activity)
    {

    }

    @Override
    public void onActivityPaused(final Activity activity)
    {

    }

    @Override
    public void onActivitySaveInstanceState(final Activity activity, final Bundle outState)
    {

    }

    @Override
    public void onActivityDestroyed(final Activity activity)
    {

    }
}