Android 應用在後臺時,跳轉 Activity 會自動切換應用至前臺
本部落格 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