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


当前回答

考虑使用onUserLeaveHint。它只会在应用进入后台时被调用。onPause会有一些极端情况需要处理,因为它可以被用于其他原因;例如,如果用户在你的应用程序中打开另一个活动,比如你的设置页面,你的主活动的onPause方法将被调用,即使它们仍然在你的应用程序中;当你可以简单地使用onUserLeaveHint回调函数来做你所要求的事情时,跟踪正在进行的事情将导致错误。

当调用on UserLeaveHint时,你可以设置一个boolean inBackground标志为true。当onResume被调用时,如果inBackground标志被设置,只假设你回到前台。这是因为onResume也将被调用在你的主要活动,如果用户只是在你的设置菜单,从来没有离开应用程序。

Remember that if the user hits the home button while in your settings screen, onUserLeaveHint will be called in your settings activity, and when they return onResume will be called in your settings activity. If you only have this detection code in your main activity you will miss this use case. To have this code in all your activities without duplicating code, have an abstract activity class which extends Activity, and put your common code in it. Then each activity you have can extend this abstract activity.

例如:

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

其他回答

编辑2:我在下面写的东西实际上是行不通的。谷歌拒绝了一个包含对ActivityManager.getRunningTasks()调用的应用程序。从文档中可以明显看出,这个API仅用于调试和开发。一旦我有时间更新下面的GitHub项目,我就会更新这篇文章,使用一个使用计时器的新方案,几乎一样好。

编辑1:我已经写了一篇博客文章,并创建了一个简单的GitHub存储库,使这非常容易。

公认的和最高评价的答案都不是最好的方法。排名最高的答案是isApplicationBroughtToBackground()的实现,它不处理应用程序的主活动屈服于同一个应用程序中定义的活动,但它有不同的Java包的情况。我想到了一种方法,在这种情况下行得通。

在onPause()中调用它,它会告诉你你的应用程序是否因为另一个应用程序已经启动而进入后台,或者用户已经按下了home键。

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

我设法监控应用导航到后台和回前台通过实现一个BaseActivity,利用onResume, onPause和onStop活动回调的使用。这是我的实现。

override fun onResume() {
    super.onResume()
    if (AppActivityState.state == AppState.ON_LAUNCHED) {
        // We are in the first launch.
        onLaunched()
    } else {
        if (AppActivityState.state == AppState.ON_BACKGROUND) {
            // We came from background to foreground.
            AppActivityState.state = AppState.ON_FOREGROUND
            onForeground()
        } else {
            // We are just navigating through pages.
            AppActivityState.state = AppState.RESUMED
        }
    }
}

override fun onPause() {
    super.onPause()
    // If state is followed by onStop then it means we will going to background.
    AppActivityState.state = AppState.PAUSED
}

override fun onStop() {
    super.onStop()

    // App will go to background base on the 'pause' cue.
    if (AppActivityState.state == AppState.PAUSED) {
        AppActivityState.state = AppState.ON_BACKGROUND
        onBackground()
    }
}

在创建BaseActivity之后,你只需要将这个activity扩展到应用程序中的任何activity。

在这些类型的实现中,您可以准确地检测到以下内容: onBackground >应用程序将进入后台 onForeground >应用将返回前台 onLaunch >应用程序刚刚打开

我希望这对你有帮助:)

我所做的是确保所有应用程序内的活动启动startActivityForResult,然后检查onActivityResult是否在onResume之前被调用。如果不是,这意味着我们刚刚从应用程序之外的某个地方返回。

boolean onActivityResultCalledBeforeOnResume;

@Override
public void startActivity(Intent intent) {
    startActivityForResult(intent, 0);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
    super.onActivityResult(requestCode, resultCode, intent);
    onActivityResultCalledBeforeOnResume = true;
}

@Override
protected void onResume() {
    super.onResume();
    if (!onActivityResultCalledBeforeOnResume) {
        // here, app was brought to foreground
    }
    onActivityResultCalledBeforeOnResume = false;
}

编辑:新的体系结构组件带来了一些有希望的东西: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
    }
  }

}

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

但还是有希望的。

如果你的应用由多个活动和/或堆叠的活动组成,就像一个标签栏小部件,那么覆盖onPause()和onResume()将不起作用。例如,当启动一个新的活动,当前的活动将得到暂停之前,新的一个被创建。当完成一个活动(使用“后退”按钮)时,也同样适用。

我发现有两种方法似乎很有效。

第一个需要GET_TASKS权限,由一个简单的方法组成,通过比较包名来检查设备上运行最多的活动是否属于应用程序:

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

这种方法是在Droid-Fu(现在称为Ignition)框架中发现的。

我自己实现的第二个方法不需要GET_TASKS权限,这很好。相反,它的实现有点复杂。

在MainApplication类中,有一个变量用于跟踪应用程序中正在运行的活动的数量。在onResume()中为每个活动增加变量,在onPause()中减少变量。

当正在运行的activity数量达到0时,如果满足以下条件,应用程序将被放入后台:

正在暂停的活动没有结束(使用了“后退”按钮)。这可以通过使用activity.isFinishing()方法来完成。 没有启动一个新活动(相同的包名)。你可以重写startActivity()方法来设置一个变量来指示这一点,然后在onPostResume()中重置它,这是创建/恢复活动时运行的最后一个方法。

当您可以检测到应用程序已经退出到后台时,当它被带回前台时也很容易检测到。