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


当前回答

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

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

}

其他回答

这是我的解决方案https://github.com/doridori/AndroidUtils/blob/master/App/src/main/java/com/doridori/lib/app/ActivityCounter.java

基本上涉及到用计时器计算所有Activity的生命周期方法,以捕捉当前前台没有活动但应用程序(即在旋转)的情况

编辑:新的体系结构组件带来了一些有希望的东西:ProcessLifecycleOwner,参见@vokilam的回答


实际解决方案根据谷歌I/O谈话:

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
    }
  }

}

是的。我知道很难相信这个简单的解决方案有效,因为我们有这么多奇怪的解决方案。

但还是有希望的。

使用ProcessLifecycleOwner在Activity(或任何类)中从后台到前台检测app的示例。 当应用程序启动时,我缓存启动时间,然后在每个活动中,我将检查应用程序时间,以知道活动是否在第一时间启动或从后台启动

class MyApplication : Application(), LifecycleObserver {

    var appStartBeginTime: Long? = null

    override fun onCreate() {
        super.onCreate()
        ProcessLifecycleOwner.get().lifecycle.addObserver(this);
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun onMoveToForeground() {
        Log.i("TAG", "onMoveToForeground")
        appStartBeginTime = System.currentTimeMillis()
    }
}

LoginActivity

class LoginActivity : AppCompatActivity() {
    var localAppStartBeginTime: Long? = null

    ...
    
    // Detect in onResume() instead of onStart because 
    // onMoveToForeground() in MyApplication will fired before onStart 
    override fun onResume() {
        super.onResume()
        if (isOpenedFirstTimeOrFromBackground()) {
            Log.i("TAG", "open first time or from background")

            // do something: eg, call API
        } else {
            Log.i("TAG", "on in another time")
        }
    }

    private fun isOpenedFirstTimeOrFromBackground(): Boolean {
        val globalStartBeginTime = (application as MyApplication).appStartBeginTime
        if (localAppStartBeginTime != globalStartBeginTime) {
            localAppStartBeginTime = globalStartBeginTime
            return true
        }
        return false
    }
}

安卓清单

<manifest ...>

    <application
        android:name=".MyApplication"
        ...>
            
    </application>

</manifest>

演示 https://github.com/PhanVanLinh/AndroidDetectAppFromBackgroundToForeground

下面是我解决这个问题的方法。它的工作前提是,在活动转换之间使用时间参考最有可能提供足够的证据来证明应用程序是否“背景化”。

首先,我使用了一个android.app.Application实例(让我们称它为MyApplication),它有一个Timer,一个TimerTask,一个常量来表示从一个活动过渡到另一个活动可以合理地花费的最大毫秒数(我用了一个2s的值),一个布尔值来指示应用程序是否“在后台”:

public class MyApplication extends Application {

    private Timer mActivityTransitionTimer;
    private TimerTask mActivityTransitionTimerTask;
    public boolean wasInBackground;
    private final long MAX_ACTIVITY_TRANSITION_TIME_MS = 2000;
    ...

应用程序还提供了两种方法来启动和停止定时器/任务:

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

这个解决方案的最后一部分是从所有活动的onResume()和onPause()事件中添加对这些方法的调用,或者,最好是在所有具体活动继承的基础活动中:

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

所以在这种情况下,当用户只是在你的应用程序的活动之间导航,离开活动的onPause()启动定时器,但几乎立即进入的新活动取消定时器之前,它可以达到最大过渡时间。wasInBackground也是假的。

另一方面,当一个Activity从启动器来到前台,设备被唤醒,结束电话呼叫等,更有可能是定时器任务在此事件之前执行,因此wasInBackground被设置为true。

The principal problem is that you have to get an specific behavior when you start an activity from background. If you override your onPause() and onResume() methods, you'll have a close answer, but not the solution. The problem is that onPause() and onResume() methods are called even if you don't minimize your application, they can be called when you start an activity and later you press the back button to return to your activity. To eliminate that problem and to know really when your application comes from background, you must to get the running process and compare with your process:

private boolean isApplicationBroughtToBackground() {
    ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningTaskInfo> tasks = am.getRunningTasks(1);
    if (!tasks.isEmpty()) {
        ComponentName topActivity = tasks.get(0).topActivity;
        if (!topActivity.getPackageName().equals(getPackageName())) {
            return true;
        }
    }
    return false;
}

现在你必须声明一个布尔变量:

public boolean wasPaused = false;

并询问你的活动何时进入后台:

@Override
public void onPause(){
    super.onPause();
    if(isApplicationBroughtToBackground())
        wasPaused = true;
}

现在,当你的活动再次出现在屏幕上时,在onResume()方法中询问:

@Override
public void onResume(){
    super.onResume();
    if(wasPaused){
        lockScreen(true);
    }
    wasPaused = false;
}

就是这样。现在,当您的活动进入后台,稍后用户将其带到前台时,锁定屏幕将出现。

如果你想为你的应用程序的任何活动重复这个行为,你必须创建一个活动(可以是BaseActivity),放这个方法,你所有的活动都必须从BaseActivity继承。

我希望这对你有帮助。

问候!