我有两项活动
MainActivity
DeepLinkActivity
我将所有内容设置为使用NavUtils
进行导航,如建议here,here和here。
我想要实现的目标是:
DeepLinkActivity
MainActivity
只要我的应用程序在最近的应用程序中有任何任务,一切都很顺利。
然而,当我从最近的应用程序中移除我的应用程序时,它的行为如下:
DeepLinkActivity
我调试了代码,发现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);
}
}
-----编辑-----
我意识到一些Google Apps以同样的方式被破坏了。如果你跳,例如从搜索到联系人,按下AB,你会发现自己在主屏幕而不是联系人应用程序。 (API19 / CM11)
答案 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感知设备从外部启动,则会设置Uri
和Activity
。
@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);
}
}