NavUtils.navigateUpTo() не запускает никаких действий

У меня два занятия

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

----- Редактировать -----

Я понял, что несколько Служб Google сломаны таким же образом. Если вы перейдете, например, к контактам из поиска, нажмите вверх в AB, и вы окажетесь на главном экране вместо приложения контактов. (API19/ CM11)

9 ответов

Решение

Я думаю, что метод прослушивается. Я прочитал исходный код библиотеки поддержки, и этот метод проверил действие намерения. Он работает только тогда, когда ваше приложение было создано ранее. Как вы уже описали, если вы убьете его из предварительного просмотра приложений, метод 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);
 }

Мое решение проблемы ОП:

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 является корнем задачи (первоначальный запуск приложения или приложения ранее был прерван через диспетчер задач). Таким образом, я не теряю существующий задний стек, если активность была запущена по ссылке, когда задача приложения уже была на переднем плане.

Это выглядит как 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);
    }
}

Вы используете <activity-alias> как ваш ГЛАВНЫЙ / ЗАПУСК? Когда я меняю <activity-alias> для <activity>Навигация вверх работает правильно. По какой-то странной причине навигация не работает должным образом, когда задействованы псевдонимы.

Есть также странный сбой пользовательского интерфейса, который указывает, что в родном ActivityManager есть ошибка. После перехода к основной активности приложения с помощью navigateUpTo()нажмите кнопку возврата устройства. Текущее приложение выполнит анимацию выхода из активности, а затем сразу после этого следующее самое последнее приложение также выполнит анимацию выхода из активности. Это происходит, даже если вы запустили текущее приложение на главном экране Android. Если вы удалите все недавние приложения и повторите шаги "навигация вверх-назад", во время анимации выхода будет показано случайное (то есть неожиданное) приложение.

Другие приложения никогда не должны быть представлены в этих случаях. Кажется, что менеджер активности неправильно управляет стеком истории.

Оскорбительная ошибка может быть найдена в следующем методе, который появляется внизу этого 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;
+        }
+    }

Я нашел следующее работает для меня. Если действие было глубоко связано с другим действием или уведомлением, стек будет создан при переходе вверх, действия просто переносятся на передний план.

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"

в вашем манифесте

Если вы перейдете к своей активности из уведомления, вам придется создать стек при создании уведомления, зацените этот ответ: NavUtils.shouldUpRecreateTask не работает на JellyBean

Это не работает для меня тоже. Итак, я справляюсь так:

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

Моя основная активность помечена как однозадачность в манифесте Android

android:launchMode="singleTask"

Попробуй это:

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

Попробуйте следующий ответ, работающий для меня, в обоих случаях запустите действие из уведомления и запустите действие из родительского действия.

@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);
    }
}
Другие вопросы по тегам