7285


A, B, C のアクティビティがあります。  
A は B の親、B は C の親になります。(AndroidManifest.xml に記述済み)

A -> B -> C のように遷移をした時に UP ボタンを押すと、B に遷移します。期待通りです。  
しかし A -> C のように遷移をした時に UP ボタンを押すと、A に遷移してしまいます。

コードは[Android のドキュメント](http://developer.android.com/training/implementing-
navigation/ancestral.html#NavigateUp)と同じようにしています。

[code]

    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case android.R.id.home:
            NavUtils.navigateUpFromSameTask(this);
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
    
[/code]

そこで、次のように、startActivity を利用する形に変更したら、期待通り B に遷移することが出来ました。

[code]

    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case android.R.id.home:
            Intent intent = NavUtils.getParentActivityIntent(this);
            startActivity(intent)
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
    
[/code]

navigateUpFromSameTask の挙動は「親のアクティビティに遷移する」だと思っていたのですが、そうでないケースがあるようです。

そこで質問なのですが、親のアクティビティに遷移したい場合、

  * 上記の startAcitivity を利用した方法で問題がないか
  * なぜ navigateUpFromSameTask が期待通りに動かないか

を知りたいです。


CC BY-SA 3.0
2015-03-01T13:18:18.530
7279
2015-03-01T14:31:44.803
205
question
android
navigateUpFromSameTask の挙動について


タスクの概念に関する理解が必要なように思います。

ざっくり説明すると、タスクというのは`Activity`のヒストリースタック、このケースで言えば、「A -> B ->
C」のことをタスクと呼称します。詳細な説明は公式ドキュメントの[Tasks and Back
Stack](http://developer.android.com/guide/components/tasks-and-back-
stack.html)を参照ください。

### Q.上記の startAcitivity を利用した方法で問題がないか

両者の挙動は全く異なるので、アプリケーションとしてどういう挙動を望むかによります。

`AndroidManifest.xml`に`Activity`の`launchMode`をどう指定したかによりますが、ほとんどの場合はデフォルトの`standard`かと思います。この場合、

[code]

    A -> B -> C
    
[/code]

の状態から、Bを`startAcitivity()`した場合、

[code]

    A -> B -> C -> B
    
[/code]

とスタックが積み重ねられます。一方、`navigateUpFromSameTask()`を利用した場合、

[code]

    A -> B
    
[/code]

の状態になります。（`CLEAR_TOP`フラグが付与されて、Bが呼び出されます。また、Cの`Activity`を`finish()`する点も異なります）

### Q.なぜ navigateUpFromSameTask が期待通りに動かないか

これについては[`navigateUpFromSameTask`のドキュメント](http://developer.android.com/reference/android/support/v4/app/NavUtils.html#navigateUpFromSameTask\(android.app.Activity\))に回答があります。

> Note: This method should only be used when sourceActivity and the
> corresponding parent are within the same task. If up navigation should cross
> tasks in some cases, see shouldUpRecreateTask(Activity, Intent).

このメソッドは親となる`Activity`と呼び出し元`Activity`が同一のタスクに存在するときのみ利用すべきと記載されています。つまりAPIの利用目的から外れているのです。

では、このときの挙動はどうなっているのでしょう？

実装はJBかそれ未満かで変わります。JB以降の場合、[`Activity#navigateUpTo()`](http://developer.android.com/reference/android/app/Activity.html#navigateUpTo\(android.content.Intent\))を呼び出しているだけなので、そちらのドキュメントより、

> If the indicated activity does not appear in the history stack, this will
> finish each activity in this task until the root activity of the task is
> reached, resulting in an "in-app home" behavior.

親`Activity`がヒストリースタックに存在しない場合、タスクのルートまで遡るとあります。このため、

[code]

    A
    
[/code]

の状態になるのです。（JB未満では挙動が異なり、恐らく期待通りBが起動すると思います）

### 本来の利用想定ケース

例えば以下のような、

[code]

    A -> B -> C
    A -> B -> D -> C
    A -> B -> E -> C
    A -> B -> D -> E -> C
    
[/code]

BからCへの遷移が複数パターンある場合、どのような遷移パターンであっても、`navigateUpFromSameTask()`を使うことで、

[code]

    A -> B
    
[/code]

の状態になります。C自身と、中途の`Activity`がクリアされているのがポイントです。複雑な階層構造を持っていても、確実に親のポイントまで戻せるのが`navigateUpFromSameTask()`の便利な点なのですが、単純な親子構造を実現したい場合には存外不便です。


CC BY-SA 3.0
2015-03-01T14:09:01.510
7285
2015-03-01T14:31:44.803
2020-06-17T08:14:45.997
-1
5337
7279
answer