如何修复应用程序在前后重新排序后转到后台

How to fix app go to background after reorder to front and back

我目前设法允许用户在两​​个不同的活动组之间切换(假设是 4 activity classes A/B 组和 X/Y 组)并通过 FLAG_ACTIVITY_REORDER_TO_FRONT 标志,但我注意到有一些奇怪的行为:

A ->(start activity)  X
X ->(reorder to front) A 
X , A ->(start) B ->(start) B2
A , B , B2 ->(reorder to front) X ->(start) Y
X , Y ->(reorder to front) A , B , B2 
X , Y , A , B <-(press back, app hide to background, B2 destroyed)  B2 
X , Y , A , B (click to foreground, B is here just fine)

如何防止应用程序在从 B2 返回时隐藏到后台?

我注意到它只发生在相同的 class(BB2 是相同的 class),如果我使用 BC,不会有这个问题,想知道是什么原因。

X ->(start) Y也是重现它的关键

我尝试使用应用程序级自定义实例列表来检测 B2onPause()isFinishing() 以及 startActivty()B,但是它将始终调用 BonCreate(),即使使用 FLAG_ACTIVITY_REORDER_TO_FRONT,这让我认为这不是正确的解决方案。正确的解决方案应该弄清楚如何防止应用程序隐藏到后台。

我误解了 FLAG_ACTIVITY_REORDER_TO_FRONT 标志。我以为 activity Y FLAG_ACTIVITY_REORDER_TO_FRONT activity C 会保留 parent,所以 C backpress 会去 B。不,不是这样的。 FLAG_ACTIVITY_REORDER_TO_FRONT 实际上使 C 转移到新的父 Y(即调用者),但由于父 Y 在 C 之上,因此 C backpress 将无法转到 Y 并使 minimized/hide 进入后台。

我也误解了我的代码创建了 2 个堆栈,但实际上它只是一个堆栈。我必须使用 FLAG_ACTIVITY_MULTIPLE_TASK 来使它成为多个堆栈。

我还需要在清单中定义正确的 launchMode

  • 启动器 activity A 和 X 必须定义为 singleInstance 显现。
  • 首页activity B和Y必须定义为singleTop 在清单中。
  • 其他活动 C 和 Z ...等必须定义为 singleTask 清单中。

Activity A 启动主页B 时必须设置flag Intent.FLAG_ACTIVITY_MULTIPLE_TASK。 当启动主页 Y.

时,X 也应该设置标志 Intent.FLAG_ACTIVITY_MULTIPLE_TASK

如果选项卡任务之前已经开始,loop 重新排序所有相同任务活动(存储在全局堆栈列表中)的有效标志是(moveTaskToBack(true); 单个行也可以,但不能回到前台):

intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
                            |Intent.FLAG_ACTIVITY_NEW_TASK
                            |Intent.FLAG_ACTIVITY_NO_ANIMATION); 

p/s:以上在Android5和6中不起作用,只会将单个顶部activity重新排序到前面,所以我需要添加android.permission.REORDER_TASKS在清单中执行此操作(更多信息,see this bug report):

ActivityManager tasksManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
for (final Activity a : YStackClasses) {
    if (IsBeforeAndroidNougat && (tasksManager != null)) {
        tasksManager.moveTaskToFront(a.getTaskId(), ActivityManager.MOVE_TASK_NO_USER_ACTION); 
    } else { 
        //Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
    }
}

如果选项卡任务从未启动,应在不设置任何特殊标志的情况下启动启动器activity。

为防止点击启动器因singleInstance而刷新,我还必须检查A和B,将其放在setContentViewfinish()/return;之前.

if (getIntent() != null && getIntent().hasCategory(Intent.CATEGORY_LAUNCHER)
    && getIntent().getAction() != null
    && getIntent().getAction().equals(Intent.ACTION_MAIN)) {
    //figure out latest activity and it's home activity, and reorder to front, and its entire task stack will focus on top.
}

Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK将不再自动清除所有活动以刷新应用程序,解决方案是ActivityCompat.finishAffinity(desiredTask's Top Act);,这将在不影响其他任务堆栈的情况下删除所有低于顶级活动的活动。

如果 class 是重复使用的,例如 C1、C2,那么我必须使用 startActivityForResult(intent, dummyResponse); 强制它在相同的 class 之上创建新的活动。但是当 A 启动 activity B 时不要这样做,因为 startActivityForResult 需要单任务并忽略 singleInstance launchMode,。并且不要在 onNewIntent() 中执行 ForResult ,这可能会导致页面在堆栈中生成两次(第二个页面不会立即生成,而是仅在返回时生成)。但是我可以在 onNewIntent() 的调用者中执行 ForResult 来生效。

我还需要这样做以使 backpress 在家中按预期工作 activity 以隐藏应用程序并能够返回到正确的任务页面。我不使用 TASK_ON_HOME(当启动主页 B class) 标志,因为它只是单一任务方向(即前景固定 activity 而不是取决于之前的顶级任务 activity):

@Override //B class
public void onBackPressed() {
    moveTaskToBack(true); //B task stack
    if (YStackClasses.size() > 0) { //Y task stack
        try {
            YStackClasses.get(0).moveTaskToBack(true);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    super.onBackPressed();
}

注意应用程序堆栈可能会缓存,即使由于崩溃而自动重新启动,所以有时您需要重新启动两次以使其回到初始堆栈状态进行测试,否则您会得到错误的结论。

就是这样,这个答案可能缺少代码细节,但是如果你遇到像我一样的"hide to background when backpress"问题,它应该会有所帮助,你可以知道有多难如果您在一个步骤中设置了错误的标志或在清单中的 activity 之一中定义了错误的 launchMode 以实现预期的行为。