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"

Ответ 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;
}