本博客 demo 见:demo。
平常用手机的时候经常碰到这种情况,用首屏广告举个栗子~很多应用都会有首屏广告 activity A,假设此应用是 app C,如果此时要使用别的应用,就会使得 app C 在后台运行。可是当 activity A 的广告结束后自动跳转 activity B 的时候 app C 总是会自动跳出来切换到前台展示,挡住了我们正在使用的应用,体验非常不好。
这是 android 4.4 后修改的新特性。理想的体验应该是如果应用在后台启动 activity B,那 activity B 也应该同样保持在后台。也就是启动的 activity B 应该保持和启动前时应用的前后台状态一致,才不会影响用户的使用。
有 2 种方案:
1. 在当前 activity A 里处理
在跳转 activity B 前判断应用 C 是否在后台,如果应用 C 在后台,那么就不跳转,并标记变量 ifStartSecondActivity = true,等到应用 C 被切换到前台的时候,因为还没有跳转,所以相当于 activity A 重新在前台展示的时候,在 onResume() 里判断变量 ifStartSecondActivity == true 则执行 startActivity() 跳转至 B;如果应用 C 在前台,正常跳转即可。
判断应用是否在前台:
public boolean isAppOnForeground() {
ActivityManager activityManager = (ActivityManager) getApplicationContext()
.getSystemService(Context.ACTIVITY_SERVICE);
String packageName = getApplicationContext().getPackageName();
List<RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
if (appProcesses == null)
return false;
for (RunningAppProcessInfo appProcess : appProcesses) {
// The name of the process that this object is associated with.
if (appProcess.processName.equals(packageName)
&& appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
return true;
}
}
return false;
}
注:这样当切换应用至前台时,跳转 B 的时候比较生硬突然,体验不是很好,因为这时其实先显示的还是 A,只是会立马跳转 B,会有一瞬间的闪动,衔接不是很好,如果对时间要求不高,可以加一个延时再跳转。
如果不想延时,希望倒计时结束后打开应用 C ,直接展示的是 activity B,可以使用下面的方法 2。
2. 在需要跳转的 activity B 里处理
这里使用到 activity 的 moveTaskToBack(boolean nonRoot) 方法来使应用切至后台。
官方文档:
/**
* Move the task containing this activity to the back of the activity
* stack. The activity's order within the task is unchanged.
*
* 将包含此 activity 的 task 移到活动堆栈的后面。该 task 里的 activity 的顺序不会变。
*
* @param nonRoot If false then this only works if the activity is the root
* of a task; if true it will work for any activity in
* a task.
*
* false:这只会在 activity 是 task 的第一个的时候起作用。
* true:对 task 里的所有 activity 都起作用。
*
* @return If the task was moved (or it was already at the
* back) true is returned, else false.
*
* 如果 task 被移动(或者它已经在后台),则返回 true,否则返回 false。
*/
public boolean moveTaskToBack(boolean nonRoot) {
try {
return ActivityManagerNative.getDefault().moveActivityTaskToBack(
mToken, nonRoot);
} catch (RemoteException e) {
// Empty
}
return false;
}
其中,判断是否是根 task,可以用 isTaskRoot() 来判断。
/**
* Return whether this activity is the root of a task. The root is the
* first activity in a task.
*
* @return True if this is the root activity, else false.
*/
public boolean isTaskRoot() {
try {
return ActivityManagerNative.getDefault()
.getTaskForActivity(mToken, true) >= 0;
} catch (RemoteException e) {
return false;
}
}
若翻译有误,请指正(^_^)
在 activity B 里调用 moveTaskToBack 后,应用会被切至后台运行。
编写代码打印 log 测试:
activity A 里有个倒计时,倒计时结束后会调用 startActivity() 启动 activity B,我们在 activity B 的 onCreate() 里添加 moveTaskToBack()。
activity B 的生命周期如下:
(1)应用在前台打开 activity A ,启动倒计时,在倒计时结束前手动将应用切换至后台。
03-29 19:49:59.756 8312-8312/com.app D/moveTaskToBack: onCreate
03-29 19:49:59.843 8312-8312/com.app D/moveTaskToBack: moveTaskToBack
03-29 19:49:59.939 8312-8312/com.app D/moveTaskToBack: onStart
03-29 19:49:59.944 8312-8312/com.app D/moveTaskToBack: onResume
03-29 19:49:59.951 8312-8312/com.app D/moveTaskToBack: onPause
03-29 19:49:59.984 8312-8312/com.app D/moveTaskToBack: onStop
activity A 在后台倒计时结束后会启动 activity B。
可见 activity B 会执行 onPause() 和 onStop() 自动切换至后台,没有 finish 掉。
(2)倒计时结束后手动将应用切换至前台,会直接展示 activity B。
03-29 19:56:27.842 8312-8312/com.app D/moveTaskToBack: onRestart
03-29 19:56:27.850 8312-8312/com.app D/moveTaskToBack: onStart
03-29 19:56:27.852 8312-8312/com.app D/moveTaskToBack: onResume
activity B 不会再执行 onCreate()。
(3)按返回键返回 activity A。
03-29 19:57:34.397 8312-8312/com.app D/moveTaskToBack: onPause
03-29 19:57:44.432 8312-8312/com.app D/moveTaskToBack: onStop
03-29 19:57:44.433 8312-8312/com.app D/moveTaskToBack: onDestroy
注:不过此方法仍然有个问题,当倒计时进行中时打开多任务界面(Recents screen),倒计时结束的跳转,activity B 的 moveTaskToBack() 不仅会将应用切至后台,还会关闭多任务界面。
观察多任务界面打开、关闭时 activity 的生命周期,发现打开多任务界面时,会执行 onPause() - onStop(),关闭多任务界面时,会执行 onRestart() - onStart() - onResume()。我的猜想是多任务界面和 activity A 在同一个任务栈里面,所以一起切换至后台了。
注:这里是判断的应用是否在前台,而不是当前 activity A 是否在前台(参见博客 Start Acitivity in background on Android 4.4 KitKat)。因为如果此时别的 activity 有 dialog 显示,activity A 被部分遮挡会执行 onPause(),导致 activity B 会执行 moveTaskToBack,出现的效果就是 app 闪退到后台了。可以把他的变量 paused 换成 isVisible,放在 onStart() 和 onStop() 进行赋值判断 activity A 是否可见。
参考:Start Acitivity in background on Android 4.4 KitKat
转载:https://blog.csdn.net/u013719138/article/details/79743895