NavUtils.navigateUpTo() не запускает никаких действий
У меня есть два действия
-
MainActivity
-
DeepLinkActivity
Я настроил все, чтобы использовать NavUtils
для навигации, как рекомендуется здесь, здесь и здесь.
Чего я хочу достичь:
- Запустите
DeepLinkActivity
с помощью глубокой ссылки
- Нажать вверх
- Перейдите к
MainActivity
Все работает хорошо, пока в моих приложениях есть какая-либо задача моего приложения.
Однако, когда я удаляю свое приложение из последних приложений, он ведет себя следующим образом:
- Удалите мое приложение из последних приложений.
- Запустите
DeepLinkActivity
с помощью глубокой ссылки
- Нажать вверх
- Приложение закрывается, например, при нажатии
Я отлаживал код и выяснял, что NavUtils.shouldUpRecreateTask()
возвращает false
.
upIntent
имеет все нормальное значение, например my 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)
Ответы
Ответ 1
Я думаю, что этот метод прослушивается. Я прочитал исходный код библиотеки поддержки, и этот метод проверяет действие намерения. Он работает только тогда, когда ваше приложение было создано ранее. Как вы описали, если вы его убьете из предварительного просмотра приложений, метод shouldUp перестает работать.
Я исправил это, используя мою собственную "shouldUpRecreateTask". Когда я получаю уведомление, которое создает непосредственно действие (например, ваше поведение), я отправляю из своего 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);
}
Ответ 2
Мое решение проблемы с 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()
вернет true, если DeepLinkActivity - это корень задачи (первоначальный запуск приложения или приложения был ранее завершен с помощью диспетчера задач). Таким образом, я не теряю существующий задний стек, если активность была запущена через ссылку, когда задача приложения уже была на переднем плане.
Ответ 3
Похоже, что NavUtils.shouldUpRecreateTask(this, upIntent)
не подготовлен для этого частного случая.
Мое текущее обходное правило проверяет, если Activity
был глубоко связан и принудительно создал новый стек задач для таких случаев.
В моем случае я обхожу объекты внутри EXTRA
внутренне.
Но ACTION
и Uri
устанавливаются, если Activity
запускается извне, следуя ссылке на определенный URL-адрес или касаясь устройств с поддержкой NFC.
@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);
}
}
Ответ 4
Используете ли вы <activity-alias>
как ваш MAIN/LAUNCHER? Когда я меняю <activity-alias>
на <activity>
, работа над навигацией работает правильно. По какой-то нечетной причине навигация не работает должным образом, когда используются псевдонимы.
Существует также странный UI-глюк, который указывает, что у native ActivityManager есть ошибка. После перехода к основному действию приложения с помощью navigateUpTo()
нажмите клавишу возврата устройства. В текущем приложении будет выполняться анимация выхода из процесса, а затем сразу же, следующее самое последнее приложение также выполнит анимацию выхода из действия. Это происходит, даже если вы запустили текущее приложение с экрана Android Home. Если вы очистите все последние приложения и снова попытаетесь выполнить шаги "вверх-вниз-назад", во время анимации выхода будет показано случайное (то есть неожиданное) приложение.
Другие приложения никогда не должны быть представлены в этих случаях. Кажется, что менеджер активности не правильно управляет стеком истории.
Оскорбительная ошибка может быть найдена в следующем методе, который появляется внизу этот 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;
+ }
+ }
Ответ 5
Я нашел, что для меня это работало. Если активность была глубоко связана с другим действием или уведомлением, стек будет создан при перемещении в противном случае, действия просто будут перенесены на передний план.
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"
в вашем манифесте
Ответ 6
Это тоже не работает для меня. Поэтому я обрабатываю этот способ:
@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);
}
My MainActivity помечен как singleTask в манифесте android
android:launchMode="singleTask"
Ответ 7
Если вы перейдете к своей деятельности из уведомления, вам нужно будет создать стек при создании уведомления, посмотрите этот ответ: NavUtils.shouldUpRecreateTask завершится с ошибкой на JellyBean
Ответ 8
Попробуйте следующее:
@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;
}