NavUtils.navigateUpTo()不会启动任何Activity

时间:2013-11-15 11:04:53

标签: android android-activity android-lifecycle

我有两项活动

  • MainActivity
  • DeepLinkActivity

我将所有内容设置为使用NavUtils进行导航,如建议hereherehere

我想要实现的目标是:

  1. 通过深层链接
  2. 开始DeepLinkActivity
  3. 按向上
  4. 转到MainActivity
  5. 只要我的应用程序在最近的应用程序中有任何任务,一切都很顺利。

    然而,当我从最近的应用程序中移除我的应用程序时,它的行为如下:

    1. 从最近的应用中移除我的应用
    2. 通过深层链接
    3. 开始DeepLinkActivity
    4. 按向上
    5. 我的应用关闭,就像按下
    6. 一样

      我调试了代码,发现NavUtils.shouldUpRecreateTask()返回falseupIntent已将所有内容设置为正常,例如我的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);
          }
      }
      

      -----编辑-----

      我意识到一些Google Apps以同样的方式被破坏了。如果你跳,例如从搜索到联系人,按下AB,你会发现自己在主屏幕而不是联系人应用程序。 (API19 / CM11)

9 个答案:

答案 0 :(得分:55)

我对OP问题的解决方案:

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);
    }
}
如果 DeepLinkActivity 是任务的根目录(先前通过任务管理器终止应用程序或应用程序的初始启动),

isTaskRoot()将返回true。这样,如果在应用程序任务已经位于前台时通过链接启动了活动,我就不会丢失现有的后台堆栈。

答案 1 :(得分:21)

我认为这种方法有问题。我已阅读支持库源代码,该方法检查intent的操作。它只适用于您之前创建的应用程序..如您所描述的,如果您从应用预览中删除它,则应该停止工作。

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

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

答案 2 :(得分:7)

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

我目前的解决方法是检查Activity是否已深度链接,并为此类情况强制执行新的任务堆栈。

在我的情况下,我在内部传递EXTRA s中的对象。 但是,如果ACTION是通过关注特定网址的链接或触摸NFC感知设备从外部启动,则会设置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);
    }
}

答案 3 :(得分:4)

您是否使用<activity-alias>作为MAIN / LAUNCHER?当我将<activity-alias>更改为<activity>时,向上导航功能正常。由于某些奇怪的原因,当涉及别名时导航无法正常工作。

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

在这些情况下,绝不应该提供其他应用程序。似乎活动管理器没有正确管理历史堆栈。

可以在以下方法中找到有问题的错误,该方法显示在this diff的底部:

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

答案 4 :(得分:3)

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

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"
你的清单中的

答案 5 :(得分: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清单中被标记为singleTask

android:launchMode="singleTask"

答案 6 :(得分:2)

如果您从通知中转到活动,则必须在创建通知时创建堆栈,请查看以下答案:NavUtils.shouldUpRecreateTask fails on JellyBean

答案 7 :(得分: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;
}

答案 8 :(得分: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);
    }
}