46

我有两个活动

  • MainActivity
  • DeepLinkActivity

我设置了所有NavUtils用于导航的东西,就像这里这里这里所建议的那样。

我想要实现的是:

  1. DeepLinkActivity通过深层链接开始
  2. 向上按
  3. MainActivity

只要最近的应用程序中有我的应用程序的任何任务,一切都会很好地工作。

但是,当我从最近的应用程序中滑动我的应用程序时,它的行为如下:

  1. 从最近的应用程序中滑动我的应用程序
  2. DeepLinkActivity通过深层链接开始
  3. 向上按
  4. 我的应用程序关闭,就像按回时一样

我调试了代码,发现NavUtils.shouldUpRecreateTask()返回false. upIntent一切都设置为正常,就像我的Component设置一样。但是,NavUtils.navigateUpTo()行为就像调用finish(). 没有日志声明,什么都没有。

任何想法,如何解决?

AndroidManifest.xml

<activity
    android:name=".DeepLinkActivity"
    android:parentActivityName="my.package.MainActivity">
    <meta-data
        android:name="android.support.PARENT_ACTIVITY"
        android:value="my.package.MainActivity"/>
    <intent-filter>
        <!-- Some intent filter -->
    </intent-filter>
</activity>

DeepLinkActivity.java

@Override
public boolean onOptionsItemSelected(final MenuItem item) {
    switch (item.getItemId()) {
        case android.R.id.home:
            Intent upIntent = NavUtils.getParentActivityIntent(this);
            if (NavUtils.shouldUpRecreateTask(this, upIntent)) {
                // create new task
                TaskStackBuilder.create(this).addNextIntentWithParentStack(upIntent)
                        .startActivities();
            } else {
                // Stay in same task
                NavUtils.navigateUpTo(this, upIntent);
            }
            return true;
        default:
            return super.onOptionsItemSelected(item);
    }
}

- - - 编辑 - - -

我意识到一些谷歌应用程序以同样的方式被破坏。如果您从搜索跳转到联系人,请在 AB 中向上按,您会发现自己在主屏幕上,而不是联系人应用程序。(API19/cm11)

4

9 回答 9

58

我对 OPs 问题的解决方案:

public void navigateUp() {
    final Intent upIntent = NavUtils.getParentActivityIntent(this);
    if (NavUtils.shouldUpRecreateTask(this, upIntent) || isTaskRoot()) {
        Log.v(logTag, "Recreate back stack");
        TaskStackBuilder.create(this).addNextIntentWithParentStack(upIntent).startActivities();
    } else {
        NavUtils.navigateUpTo(this, upIntent);
    }
}

isTaskRoot()如果DeepLinkActivity是任务的根(应用程序的初始启动或应用程序先前已通过任务管理器终止),则将返回 true 。这样,当应用程序任务已经在前台时,如果通过链接启动活动,我就不会丢失现有的后台堆栈。

于 2015-07-10T21:35:56.073 回答
22

我认为这种方法是错误的。我已经阅读了支持库源代码,并且该方法检查了意图的操作。它仅在您的应用程序之前创建时才有效..正如您所描述的,如果您从应用程序预览中杀死它,shouldUp 方法将停止工作。

我已经使用我自己的“shouldUpRecreateTask”解决了这个问题。当我收到直接创建 Activity 的通知时(就像您的行为一样),我会从我的 BroadCastReceiver 发送意图内的自定义操作。然后,在我的方法中,我做下一件事:

private final boolean shouldUpRecreateTask(Activity from){
    String action = from.getIntent().getAction();
    return action != null && action.equals(com.xxxxxx.activities.Intent.FROM_NOTIFICATION);
}

…………………………………………………………………………………………

 if (shouldUpRecreateTask(this)) {
      TaskStackBuilder.create(this)
       .addNextIntentWithParentStack(upIntent)
       .startActivities();
 } else {
       fillUpIntentWithExtras(upIntent);
       NavUtils.navigateUpTo(this, upIntent);
 }
于 2013-12-17T20:27:04.497 回答
7

看起来NavUtils.shouldUpRecreateTask(this, upIntent)并没有为这种特殊情况做好准备。

我目前的解决方法是检查是否Activity深度链接并针对此类情况强制使用新的任务堆栈。

就我而言,我在EXTRAs 内部传递对象。但是,如果通过跟踪特定 URL 的链接或通过触摸 NFC 感知设备从外部启动,则会设置ACTIONand 。UriActivity

@Override
public boolean onOptionsItemSelected(final MenuItem item) {
    switch (item.getItemId()) {
        case android.R.id.home:
            Intent upIntent = NavUtils.getParentActivityIntent(this);
            if (NavUtils.shouldUpRecreateTask(this, upIntent)
                    || getIntent().getAction() != null) { // deep linked: force new stack
                // create new task
                TaskStackBuilder.create(this).addNextIntentWithParentStack(upIntent)
                        .startActivities();
            } else {
                // Stay in same task
                NavUtils.navigateUpTo(this, upIntent);
            }
            return true;
        default:
            return super.onOptionsItemSelected(item);
    }
}
于 2013-12-17T10:22:05.333 回答
4

你用一个<activity-alias>作为你的主要/发射器吗?当我将 更改为<activity-alias><activity>,向上导航可以正常工作。出于某种奇怪的原因,涉及别名时导航无法正常工作。

还有一个奇怪的 UI 故障,表明本机 ActivityManager 存在错误。使用 导航到应用程序的主要活动后navigateUpTo(),按设备的返回键。当前应用程序将执行活动退出动画,然后紧接着,下一个最近的应用程序也将执行活动退出动画。即使您从 Android 的主屏幕启动到当前应用程序,也会发生这种情况。如果您清除所有最近的应用程序并再次尝试导航向上然后返回步骤,则在退出动画期间将呈现一个随机(即意外)应用程序。

在这些情况下,永远不应显示其他应用程序。似乎活动管理器没有正确管理历史堆栈。

可以在此差异底部显示的以下方法中找到有问题的错误:

+    public boolean navigateUpTo(IBinder token, Intent destIntent, int resultCode,
+            Intent resultData) {
+        ComponentName dest = destIntent.getComponent();
+
+        synchronized (this) {
+            ActivityRecord srec = ActivityRecord.forToken(token);
+            ArrayList<ActivityRecord> history = srec.stack.mHistory;
+            final int start = history.indexOf(srec);
+            if (start < 0) {
+                // Current activity is not in history stack; do nothing.
+                return false;
+            }
+            int finishTo = start - 1;
+            ActivityRecord parent = null;
+            boolean foundParentInTask = false;
+            if (dest != null) {
+                TaskRecord tr = srec.task;
+                for (int i = start - 1; i >= 0; i--) {
+                    ActivityRecord r = history.get(i);
+                    if (tr != r.task) {
+                        // Couldn't find parent in the same task; stop at the one above this.
+                        // (Root of current task; in-app "home" behavior)
+                        // Always at least finish the current activity.
+                        finishTo = Math.min(start - 1, i + 1);
+                        parent = history.get(finishTo);
+                        break;
+                    } else if (r.info.packageName.equals(dest.getPackageName()) &&
+                            r.info.name.equals(dest.getClassName())) {
+                        finishTo = i;
+                        parent = r;
+                        foundParentInTask = true;
+                        break;
+                    }
+                }
+            }
+
+            if (mController != null) {
+                ActivityRecord next = mMainStack.topRunningActivityLocked(token, 0);
+                if (next != null) {
+                    // ask watcher if this is allowed
+                    boolean resumeOK = true;
+                    try {
+                        resumeOK = mController.activityResuming(next.packageName);
+                    } catch (RemoteException e) {
+                        mController = null;
+                    }
+
+                    if (!resumeOK) {
+                        return false;
+                    }
+                }
+            }
+            final long origId = Binder.clearCallingIdentity();
+            for (int i = start; i > finishTo; i--) {
+                ActivityRecord r = history.get(i);
+                mMainStack.requestFinishActivityLocked(r.appToken, resultCode, resultData,
+                        "navigate-up");
+                // Only return the supplied result for the first activity finished
+                resultCode = Activity.RESULT_CANCELED;
+                resultData = null;
+            }
+
+            if (parent != null && foundParentInTask) {
+                final int parentLaunchMode = parent.info.launchMode;
+                final int destIntentFlags = destIntent.getFlags();
+                if (parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE ||
+                        parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TASK ||
+                        parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TOP ||
+                        (destIntentFlags & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
+                    parent.deliverNewIntentLocked(srec.app.uid, destIntent);
+                } else {
+                    try {
+                        ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
+                                destIntent.getComponent(), 0, UserId.getCallingUserId());
+                        int res = mMainStack.startActivityLocked(srec.app.thread, destIntent,
+                                null, aInfo, parent.appToken, null,
+                                0, -1, parent.launchedFromUid, 0, null, true, null);
+                        foundParentInTask = res == ActivityManager.START_SUCCESS;
+                    } catch (RemoteException e) {
+                        foundParentInTask = false;
+                    }
+                    mMainStack.requestFinishActivityLocked(parent.appToken, resultCode,
+                            resultData, "navigate-up");
+                }
+            }
+            Binder.restoreCallingIdentity(origId);
+            return foundParentInTask;
+        }
+    }
于 2014-05-01T00:05:14.310 回答
3

我发现以下内容对我有用。如果 Activity 与另一个 Activity 或通知深度链接,则将在您向上导航时创建堆栈,否则,这些 Activity 只会被带到前面。

case android.R.id.home:
Intent upIntent = NavUtils.getParentActivityIntent(this);
upIntent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(upIntent);
finish();
return true;

前提是你有

android:parentActivityName="parent.activity"

在你的清单中

于 2014-02-19T22:33:34.997 回答
2

它对我也不起作用。所以我这样处理:

   @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == android.R.id.home) {

                Intent intent = new Intent(this, MainActivity.class);
                startActivity(intent);
                finish();
                return true;

        }
        return super.onOptionsItemSelected(item);
    }

我的 MainActivity 在 android manifest 中被标记为 singleTask

android:launchMode="singleTask"
于 2013-12-13T17:37:47.483 回答
2

如果您从通知中转到您的活动,则必须在创建通知时创建堆栈,请查看此答案:NavUtils.shouldUpRecreateTask 在 JellyBean 上失败

于 2014-01-08T10:35:50.713 回答
1

尝试这个 :

@Override
public boolean onOptionsItemSelected(final MenuItem item) {
    switch (item.getItemId()) {
        case android.R.id.home:
            Intent upIntent = NavUtils.getParentActivityIntent(this);
            if (NavUtils.shouldUpRecreateTask(this, upIntent)) {
                // create new task
                TaskStackBuilder.create(this).addNextIntentWithParentStack(upIntent)
                        .startActivities();
            } else {
                // Stay in same task
                NavUtils.navigateUpTo(this, upIntent);
            }
            break;
        default:
            return super.onOptionsItemSelected(item);
    }
    return true;
}
于 2013-12-12T04:50:44.923 回答
0

尝试以下对我有用的答案,无论是从通知开始活动还是从父活动开始活动。

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case android.R.id.home:
            Intent upIntent = NavUtils.getParentActivityIntent(this);
            if (NavUtils.shouldUpRecreateTask(this, upIntent) || isTaskRoot()) {
                TaskStackBuilder.create(this).addNextIntentWithParentStack(upIntent)
                        .startActivities();
            } else {
                finish();
            }
            return true;
        default:
            return super.onOptionsItemSelected(item);
    }
}
于 2018-08-10T13:06:25.967 回答