web-dev-qa-db-ja.com

NavUtils.navigateUpTo()がアクティビティを開始しない

2つのアクティビティがあります

  • MainActivity
  • DeepLinkActivity

NavUtilsを使用して、アドバイス herehere および here のようにナビゲートするためにすべてを設定しました。

私が達成したいのは:

  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 Appsが同じように壊れていることに気付きました。あなたがジャンプした場合検索から連絡先に移動するには、ABを押します。連絡先アプリの代わりにホーム画面が表示されます。 (API19/cm11)

46
flx

その方法にはバグがあると思います。サポートライブラリのソースコードを読み、そのメソッドでインテントのアクションを確認しました。アプリが以前に作成された場合にのみ機能します。前述のように、アプリのプレビューからアプリを強制終了すると、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);
 }
22
noni

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を返します(アプリケーションの初期起動またはアプリケーションが以前にタスクマネージャーを通じて終了されていた場合)。このようにして、アプリケーションタスクがすでにフォアグラウンドにあるときにリンクを介してアクティビティが起動された場合、既存のバックスタックを失うことはありません。

56
Sokolov

NavUtils.shouldUpRecreateTask(this, upIntent)は、この特殊なケースに対応していないようです。

現在の回避策は、Activityがディープリンクされているかどうかを確認し、このような場合に新しいタスクスタックを強制することです。

私の場合、EXTRAsのオブジェクトを内部的に渡します。ただし、ACTIONUriは、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);
    }
}
7
flx

_<activity-alias>_をメイン/ランチャーとして使用していますか? _<activity-alias>_を_<activity>_に変更すると、上方向のナビゲーションが正しく機能します。奇妙な理由で、エイリアスが含まれているとナビゲーションが正しく機能しません。

ネイティブのActivityManagerにバグがあることを示す奇妙なUIグリッチもあります。 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
James Wald

私は次のことがうまくいきました。アクティビティが別のアクティビティまたは通知からディープリンクされている場合、他の方法でナビゲートするとスタックが作成され、アクティビティは前面に表示されます。

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"

あなたのマニフェスト

3
David

通知からアクティビティに移動する場合は、通知を作成するときにスタックを作成する必要があります。この回答を確認してください: NavUtils.shouldUpRecreateTask fail on JellyBean

2
Ciprian

私にとってもうまくいきません。だから私はこのように扱います:

   @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"
2
vandzi

これを試して :

@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;
}
1
SweetWisher ツ

通知からアクティビティを開始する場合と、親アクティビティからアクティビティを開始する場合の両方で、次の回答を試してみてください。

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