アクティビティの概要

Activity クラスは Android アプリの重要なコンポーネントであり、アクティビティの開始方法と組み立て方法はプラットフォームのアプリケーション モデルを構成する際の基本的な要素です。main メソッドで起動するアプリをプログラミングする際の枠組みとは異なり、Android システムでは、ライフサイクルのそれぞれの段階に対応する特定のコールバック メソッドを呼び出すことにより、Activity インスタンス内のコードが開始されます。

このドキュメントでは、アクティビティのコンセプトを紹介し、それを使いこなす方法について簡単に説明します。アプリの設計に関するおすすめの方法については、 アプリ アーキテクチャのガイドをご覧ください。

アクティビティのコンセプト

モバイルアプリのエクスペリエンスはパソコンと異なり、ユーザーの操作が必ずしも同じ場所で開始されるとは限りません。ユーザーの操作が定められた状態で開始されることはほとんどありません。たとえば、ホーム画面からメールアプリを開くと、メールの一覧が表示されることがあります。一方、ソーシャルメディアアプリを使用していて、そのアプリからメールアプリを起動した場合は、メールアプリのメール作成画面に直接移動できます。

Activity クラスは、このパラダイムを容易にするように設計されています。1 つのアプリが別のアプリを呼び出すとき、呼び出し元のアプリは、別のアプリ全体をひとまとめとして呼び出すのではなく、その中のアクティビティを呼び出します。そうすることで、アクティビティが、アプリがユーザーとやり取りする際のエントリ ポイントとして機能します。なお、アクティビティの実装は サブクラスとして Activity クラスに行います。

アクティビティにより、アプリが UI を描画するウィンドウが用意されます。通常、このウィンドウは画面全体に表示されますが、画面よりも小さく、他のウィンドウの上に重なることもあります。

通常、アプリ内の 1 つのアクティビティがメイン アクティビティに指定されます。これは、 ユーザーがアプリを起動したときに最初に表示される画面です。最新の Compose アプリでは、ビュー階層を所有するのではなく、 単一アクティビティ アーキテクチャでコンポーザブルをホストするため、このアクティビティのみが必要です。アプリに画面用の複数のアクティビティがあるのではなく、アクティビティ内のコンポーザブルが複数のナビゲーション デスティネーションをホストします。

アクティビティをアプリで使用するには、アプリのマニフェストにその情報を登録する必要があります。また、アクティビティのライフサイクルを把握しておくことをおすすめします。このドキュメントの残りの部分では、これらのトピックについて説明します。

マニフェストの設定

アプリでアクティビティを使用できるようにするには、マニフェストでアクティビティとその属性を宣言する必要があります。

アクティビティを宣言する

アクティビティを宣言するには、マニフェスト ファイルを開き、<activity> 要素を<application>要素の子として追加します。例:

<manifest ... >
  <application ... >
      <activity android:name=".ExampleActivity" />
      ...
  </application ... >
  ...
</manifest >

この要素に必須の属性は、アクティビティのクラス名を指定する android:name だけです。ラベル、アイコン、UI テーマなどのアクティビティの特性を定義する属性を追加することもできます。これらを含めた属性の詳細については、<activity>要素のリファレンス ドキュメントをご覧ください。

インテント フィルタを宣言する

インテント フィルタは、Android プラットフォームの非常に強力な機能です。明示的なリクエストだけでなく、暗黙的なリクエストに基づいてアクティビティを起動する機能を提供します。たとえば、明示的なリクエストでは、「Gmail アプリでのメール送信アクティビティの開始」をシステムに指示します。一方、暗黙的なリクエストでは、「この作業を行える Activity でのメール送信画面の開始」をシステムに指示します。インテント フィルタは、システム UI がタスクの実行に使用するアプリの選択をユーザーに求める際に利用されます。

この機能を利用するには、<activity> 要素で <intent-filter> 属性を宣言します。この要素の定義には、<action> 要素と、必要に応じて <category>要素や <data> 要素が含まれます。これらの要素の組み合わせで、アクティビティが応答するインテントのタイプを指定します。たとえば、次のコード スニペットは、テキストデータとメールを送信するアクティビティを設定し、そのためのリクエストを他のアクティビティから受信する方法を示しています。

<activity android:name=".ExampleActivity" android:icon="@drawable/app_icon">
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="text/plain" />
    </intent-filter>
   <intent-filter>
        <action android:name="android.intent.action.SENDTO" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="mailto" />
    </intent-filter>
</activity>

この例では、<action> 要素で、このアクティビティがデータを送信することを指定しています。<category> 要素を DEFAULT として宣言すると、アクティビティ が開始リクエストを受信できるようになります。<data>要素は、アクティビティが送信できる データの種類を指定します。次のコード スニペットは、上記のアクティビティを呼び出してメールを作成する方法を示しています。

fun composeEmail(addresses: Array<String>, subject: String) {
    val intent = Intent(Intent.ACTION_SENDTO).apply {
        data = Uri.parse("mailto:") // Only email apps handle this.
        putExtra(Intent.EXTRA_EMAIL, addresses)
        putExtra(Intent.EXTRA_SUBJECT, subject)
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}

アプリを自己完結させ、他のアプリによるアクティビティの有効化を許可しない場合は、他のインテント フィルタは必要ありません。他のアプリケーションに使用させたくないアクティビティにはインテント フィルタを設定しないでください。このようなアクティビティは明示的なインテントを使用して開始できます。アクティビティがインテントにどのように応答するかの詳細については、インテントと インテントフィルタをご覧ください。

受信インテントを処理する

次の例は、複数のインテント タイプ(単一のテキスト共有、単一の画像、複数の画像配列)を処理しながらアクティビティのライフサイクルを管理するパターンを示しています。これらのさまざまな入力を一元化された handleIntent 関数を介してルーティングすることで、ACTION_SEND アクションと ACTION_SEND_MULTIPLE アクションの両方が正しく解析され、リアクティブ UI の更新のために ViewModel に委任されるようにします。

class ExampleActivity : ComponentActivity() {
  private val viewModel: MyViewModel by viewModels()

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    handleIntent(intent)
    setContent {
      ComposeApp(viewModel)
    }
  }

  override fun onNewIntent(intent: Intent) {
    super.onNewIntent(intent)
    setIntent(intent)
    handleIntent(intent)
  }

  private fun handleIntent(intent: Intent?) {
    when (intent?.action) {
      Intent.ACTION_SEND -> {
        if ("text/plain" == intent.type) {
          intent.getStringExtra(Intent.EXTRA_TEXT)?.let {
            viewModel.handleText(it) // Update UI to reflect text being shared
          }
        } else if (intent.type?.startsWith("image/") == true) {
          (intent.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java))?.let {
            viewModel.handleImage(it) // Update UI to reflect image being shared
          }
        }
      }

      Intent.ACTION_SEND_MULTIPLE -> {
          if (intent.type?.startsWith("image/") == true) {
              intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM, Uri::class.java)?.let {
                  viewModel.handleMultipleImages(it) // Update UI to reflect multiple images being shared
              }
          } else {
              // Handle other types
          }
      }

      else -> {
          // Handle other intents
      }
    }
  }
}

権限を宣言する

マニフェストの <activity> タグを使用して、特定のアクティビティを開始できるアプリを制御できます。マニフェストで、親アクティビティと子アクティビティに同じ権限が指定されていない場合、親アクティビティは子アクティビティを起動できません。 <uses-permission>要素を親アクティビティに宣言した場合は、それぞれの子アクティビティ にも一致する<uses-permission>要素が必要です。

たとえば、SocialApp という名前の仮想アプリを使用してソーシャル メディアで投稿を共有するには、SocialApp を呼び出すアプリに必要な権限を SocialApp 自体にも定義する必要があります。

<manifest>
<activity android:name="...."
   android:permission="com.google.socialapp.permission.SHARE_POST"

/>

次に、SocialApp の呼び出しを許可するには、アプリの権限セットを SocialApp のマニフェストに設定されている権限セットと一致させる必要があります。

<manifest>
   <uses-permission android:name="com.google.socialapp.permission.SHARE_POST" />
</manifest>

権限と一般的なセキュリティの詳細については、セキュリティ チェックリストをご覧ください。

アクティビティのライフサイクルの管理

アクティビティのライフサイクルには、さまざまな状態があり、一連のコールバックを使用することで、状態間の遷移を処理します。以降のセクションでは、これらのコールバックについて説明します。Compose アプリでは、これらのコールバックに直接フックすることをおすすめしません。代わりに、Lifecycle API を使用して状態の変化を監視します。詳細については、Compose とライフサイクルを統合する をご覧ください。

onCreate

このコールバックは必ず実装してください。システムによってアクティビティが作成される際に呼び出されるコールバックです。実装の際は、アクティビティの基本的なコンポーネントを初期化する必要があります。たとえば、アプリでビューを作成し、そこに一覧表示するデータをバインドするなどです。

Compose アプリでは、次の例に示すように、このコールバックを使用して setContent を使用してホスト コンポーザブルを設定します。

class MyActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Text(text = stringResource(id = R.string.greeting))
        }
    }
}

onCreate が終了すると、次のコールバックは常に onStart になります。

onStart

onCreate が終了すると、アクティビティは開始状態になり、アクティビティがユーザーに表示されます。このコールバックには、アクティビティがフォアグラウンドになり、操作可能になるための最後の準備が含まれます。

onResume

アクティビティがユーザーとやり取りを始める直前に、このコールバックが呼び出されます。この時点で、アクティビティはアクティビティ スタックの最上位にあり、すべてのユーザー入力を取得します。アプリのコア機能のほとんどは onResumeメソッドに実装されます。

onPause コールバックは常に onResume の後に呼び出されます。

onPause

アクティビティがフォーカスを失って一時停止状態になると、onPause が呼び出されます。この状態は、たとえばユーザーが [戻る] ボタンや [最近] ボタンをタップした場合に発生します。システムがアクティビティの onPause を呼び出すときは、厳密にはアクティビティが部分的に表示された状態であることを意味しますが、ほとんどの場合、ユーザーがアクティビティから離れ、すぐに停止あるいは再開状態に入ることを示します。

一時停止状態のアクティビティでも、ユーザーが UI の更新を見込んでいるような状況では、引き続き UI が更新されることがあります。このようなアクティビティの例としては、ナビゲーション マップの画面や再生中のメディア プレーヤーなどがあります。このようなアクティビティの場合は、フォーカスが失なわれたとしても、ユーザーは UI の更新が引き続き行われるものと想定します。

`onPause` は、アプリケーション データやユーザーデータの保存、ネットワーク呼び出し、データベース トランザクションの実行には使用しないでくださいonPauseデータの保存について詳しくは、一時的な UI の状態の保存と復元をご覧ください。

onPause の実行が終了すると、次のコールバックは onStop または onResume のどちらかです。どちらになるかは、アクティビティが一時停止状態になった後で何が起きたかに依存します。

onStop

アクティビティがユーザーに表示されなくなった場合、onStop が呼び出されます。発生要因としては、アクティビティが破棄されている、新しいアクティビティが開始されている、既存のアクティビティが再開状態に入って停止したアクティビティに重複する、などが挙げられます。このような場合、停止したアクティビティは一切表示されなくなります。

次に呼び出されるコールバックは、onRestart、アクティビティでのユーザーによる操作が再開している場合は 、またはonDestroy、このアクティビティが完全に終了している場合は になります。

onRestart

停止状態のアクティビティが再起動しようとすると、このコールバックが呼び出されます。onRestart により、アクティビティの状態が停止した時点の状態に復元されます。

このコールバックの後は必ず onStart が呼び出されます。

onDestroy

アクティビティが破棄される前に、このコールバックが呼び出されます。

このコールバックは、アクティビティが受け取る最後のコールバックです。onDestroy は 通常、アクティビティもしくはアクティビティを含むプロセスが破棄される際に、アクティビティのリソースがすべて解放されるように実装されます。

このセクションでは、このトピックの概要についてのみ説明します。アクティビティのライフサイクルとそのコールバックの取り扱いの詳細については、アクティビティのライフサイクルをご覧ください。