最近の画面

[最近] 画面は、最近アクセスしたアクティビティタスクを一覧表示するシステムレベルの UI です。オーバービュー画面、最近のタスクリスト、最近のアプリとも呼ばれます。ユーザーはリスト内を移動して、再開するタスクを選択したり、スワイプでリストからタスクを削除したりできます。Android 5.0(API レベル 21)では、ドキュメント中心モデルが導入されています。このモデルでは、異なるドキュメントを含んだ同じアクティビティの複数のインスタンスが [最近] 画面にタスクとして表示されます。たとえば、Google ドライブには Google ドキュメントごとにタスクがあります。各ドキュメントは、[最近] 画面にタスクとして表示されます。

図 1. 3 つの Google ドライブ ドキュメントをそれぞれ別のタスクとして表示している [最近] 画面

もう 1 つの一般的な例は、ユーザーがブラウザを使用しているときに、[共有] > [Gmail] をタップする場合です。Gmail アプリの [作成] 画面が表示されます。[最近] ボタンをタップすると、Chrome と Gmail が別々のタスクとして実行されているのがわかります。以前のバージョンの Android では、すべてのアクティビティが 1 つのタスクとして表示され、[戻る] ボタンが唯一のナビゲーションの手段でした。図 2 は、Android 5.0 以降とそれより前のプラットフォームでの [最近] 画面の表示例です。左側が Android 5.0 以降の画面、右側がそれより前のバージョンの画面です。

Android 5.0 より前と以前の [最近] 画面

図 2. Android 5.0 以降(左)と Android 5.0 より前のバージョン(右)の [最近] 画面

通常、タスクとアクティビティの [最近] 画面での表示方法は、システムが定義した方法に従い、この動作を変更する必要はありません。ただし、[最近] 画面でアクティビティを表示する方法とタイミングについては、アプリで定めることができます。ActivityManager.AppTask クラスを使用するとタスクの管理ができ、Intent クラスを使用すると [最近] 画面にアクティビティを追加するタイミング、あるいはそこから削除するタイミングを指定できます。また、 <activity> 属性を使用するとマニフェストで動作を設定できます。

[最近] 画面へのタスクの追加

Intent クラスのフラグを使用すると、[最近] 画面でドキュメントを開いたり再度開いたりするタイミングと方法を自由に制御できます。<activity> 属性を使用する際に、常に新しいタスクでドキュメントを開くか、既存のタスクを再利用するかを選択できます。

インテント フラグを使用したタスクの追加

アクティビティの新しいドキュメントを作成するとき、startActivity() メソッドを呼び出して、アクティビティを起動するインテントを渡します。論理的な区切りを挿入して、[最近] 画面でアクティビティが新しいタスクとして扱われるようにするには、アクティビティを起動する IntentaddFlags() メソッドに FLAG_ACTIVITY_NEW_DOCUMENT フラグを渡します。

注: FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET フラグは Android 5.0(API レベル 21)で非推奨となり、代わりに FLAG_ACTIVITY_NEW_DOCUMENT が推奨となりました。

新しいドキュメントを作成するときに FLAG_ACTIVITY_MULTIPLE_TASK フラグを設定すると、ターゲット アクティビティをルートとする新しいタスクが常に作成されます。この設定により、同じドキュメントを複数のタスクで開くことができます。次のコードは、これをメイン アクティビティで行う例です。

Kotlin

    fun createNewDocument(view: View) {
        val newDocumentIntent = newDocumentIntent()
        if (useMultipleTasks) {
            newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
        }
        startActivity(newDocumentIntent)
    }

    private fun newDocumentIntent(): Intent =
            Intent(this, NewDocumentActivity::class.java).apply {
                addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT or
                        android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS)
                putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, documentCounter++)
            }

    

Java

DocumentCentricActivity.java

    public void createNewDocument(View view) {
          final Intent newDocumentIntent = newDocumentIntent();
          if (useMultipleTasks) {
              newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
          }
          startActivity(newDocumentIntent);
      }

      private Intent newDocumentIntent() {
          boolean useMultipleTasks = checkbox.isChecked();
          final Intent newDocumentIntent = new Intent(this, NewDocumentActivity.class);
          newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
          newDocumentIntent.putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, documentCounter++);
          return newDocumentIntent;
      }

    }
    

注: FLAG_ACTIVITY_NEW_DOCUMENT フラグを付けて起動するアクティビティには、マニフェストで android:launchMode="standard" 属性値(デフォルト値)を設定する必要があります。

メイン アクティビティで新しいアクティビティが起動されると、システムは、インテント コンポーネント名とそのアクティビティのインテント データが一致するインテントを持つタスクを、既存のタスク内で検索します。タスクが見つからないか、インテントに FLAG_ACTIVITY_MULTIPLE_TASK フラグが含まれていた場合、アクティビティをルートとして新しいタスクが作成されます。タスクが見つかった場合は、そのタスクがフォアグランドに移動し、新しいインテントが onNewIntent() に渡されます。次の例のように、新しいアクティビティがインテントを取得し、[最近] 画面に新しいドキュメントが作成されます。

Kotlin

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_new_document)
        documentCount = intent
                .getIntExtra(DocumentCentricActivity.KEY_EXTRA_NEW_DOCUMENT_COUNTER, 0)
        documentCounterTextView = findViewById(R.id.hello_new_document_text_view)
        setDocumentCounterText(R.string.hello_new_document_counter)
    }

    override fun onNewIntent(newIntent: Intent) {
        super.onNewIntent(newIntent)
        /* If FLAG_ACTIVITY_MULTIPLE_TASK has not been used, this Activity
        will be reused. */
        setDocumentCounterText(R.string.reusing_document_counter)
    }
    

Java

NewDocumentActivity.java

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_new_document);
        documentCount = getIntent()
                .getIntExtra(DocumentCentricActivity.KEY_EXTRA_NEW_DOCUMENT_COUNTER, 0);
        documentCounterTextView = (TextView) findViewById(
                R.id.hello_new_document_text_view);
        setDocumentCounterText(R.string.hello_new_document_counter);
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        /* If FLAG_ACTIVITY_MULTIPLE_TASK has not been used, this activity
        is reused to create a new document.
         */
        setDocumentCounterText(R.string.reusing_document_counter);
    }
    

activity 属性を使用したタスクの追加

アクティビティでは、マニフェストで、<activity> 属性、android:documentLaunchMode を使用することにより、常に新しいタスクを開始するように指定することもできます。この属性には 4 つの値があり、ユーザーがアプリケーションでドキュメントを開くときに次のような効果があります。

"intoExisting"
アクティビティがドキュメント用に既存のタスクを再利用します。これは、FLAG_ACTIVITY_MULTIPLE_TASK フラグを設定せずに FLAG_ACTIVITY_NEW_DOCUMENT フラグを設定した場合と同じです。上記のインテント フラグを使用したタスクの追加をご覧ください。
"always"
すでにドキュメントが開いている場合でも、アクティビティがドキュメント用に新しいタスクを作成します。この値の使用は、FLAG_ACTIVITY_NEW_DOCUMENT フラグと FLAG_ACTIVITY_MULTIPLE_TASK フラグの設定と同じです。
"none"
アクティビティは、ドキュメント用に新しいタスクを作成しません。[最近] 画面では、アクティビティがデフォルトとして扱われます。つまり、ユーザーが最後に実行したアクティビティから再開するアプリに対してタスクが 1 つ表示されます。
"never"
アクティビティは、ドキュメント用に新しいタスクを作成しません。この値を設定すると、FLAG_ACTIVITY_NEW_DOCUMENT フラグと FLAG_ACTIVITY_MULTIPLE_TASK フラグの動作がオーバーライドされます。いずれかがインテントに設定されている場合、[最近] 画面では、ユーザーが最後に実行したアクティビティから再開するアプリに対してタスクが 1 つ表示されます。

注: nonenever 以外の値の場合、launchMode="standard" を指定してアクティビティを定義する必要があります。この属性が指定されていない場合、documentLaunchMode="none" が使用されます。

タスクの削除

デフォルトでは、ドキュメント タスクはアクティビティの終了時に [最近] 画面から自動的に削除されます。この動作は、ActivityManager.AppTask クラス、Intent フラグ、または <activity> 属性を使ってオーバーライドできます。

<activity> 属性の android:excludeFromRecentstrue に設定することで、いつでも [最近] 画面からタスクを完全に除外できます。

<activity> 属性の android:maxRecents に整数値を設定すると、[最近] 画面に含めることができるタスク数の上限を指定できます。デフォルトは 16 です。タスク数が上限に達すると、直近で最も使用されていない(LRU)タスクが [最近] 画面から削除されます。android:maxRecents には 50 以下を指定します(メモリの少ないデバイスの場合は 25)。1 未満は無効です。

AppTask クラスを使用したタスクの削除

[最近] 画面に新しいタスクを作成するアクティビティでは、finishAndRemoveTask() メソッドを呼び出すことにより、タスクを削除して、関連するアクティビティをすべて終了させるタイミングを指定できます。

Kotlin

    fun onRemoveFromOverview(view: View) {
        // It is good pratice to remove a document from the overview stack if not needed anymore.
        finishAndRemoveTask()
    }
    

Java

NewDocumentActivity.java

    public void onRemoveFromRecents(View view) {
        // The document is no longer needed; remove its task.
        finishAndRemoveTask();
    }
    

注: 以下で説明するように、finishAndRemoveTask() メソッドを使用すると、FLAG_ACTIVITY_RETAIN_IN_RECENTS タグの使用がオーバーライドされます。

完了したタスクの保持

アクティビティが終了した後もタスクを [最近] 画面に保持したい場合、そのアクティビティを起動するインテントの addFlags() メソッドに FLAG_ACTIVITY_RETAIN_IN_RECENTS フラグを渡します。

Kotlin

    private fun newDocumentIntent() =
            Intent(this, NewDocumentActivity::class.java).apply {
                addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT or
                        android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS)
                putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, getAndIncrement())
            }
    

Java

DocumentCentricActivity.java

    private Intent newDocumentIntent() {
        final Intent newDocumentIntent = new Intent(this, NewDocumentActivity.class);
        newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT |
          android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS);
        newDocumentIntent.putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, getAndIncrement());
        return newDocumentIntent;
    }
    

同じ効果を得るには、<activity> 属性の android:autoRemoveFromRecentsfalse に設定します。デフォルト値は、ドキュメント アクティビティの場合は true、通常のアクティビティの場合は false です。上で説明したように、この属性を使用すると FLAG_ACTIVITY_RETAIN_IN_RECENTS フラグがオーバーライドされます。

他のサンプルコード

ドキュメント中心のアプリのサンプルアプリをダウンロードするには、DocumentCentricRecents サンプルAndroid DocumentCentricRelinquishIdentity サンプルをご覧ください。