はじめに
影響範囲が大きそうなのでまとめておきます。
※本記事は Medium にも投稿済みです。 Qiita の方が見る人が多そうなのでこちらにも投稿しました。
androidx.fragment:fragment-ktx:1.3.0-alpha04
から、以下のメソッドが deprecated
になります。
- startActivityForResult
- onActivityResult
- requestPermissions
- onRequestPermissionsResult
代わりに 新しく追加された Activity Result APIs を使えとのアナウンスがあります。
Getting a result from an activity
While the underlying startActivityForResult() and onActivityResult() APIs are available on the Activity class on all API levels, it is strongly recommended to use the Activity Result APIs introduced in AndroidX Activity 1.2.0-alpha02 and Fragment 1.3.0-alpha02.
どうやら、ActivityResultContract
の 「registerForActivityResult
」 を使えば良いようです。
そこで、registerForActivityResult
の使い方をまとめました。
リポジトリ
以下にサンプルリポジトリがあります。
https://github.com/nanaten/Activity-Contract-Example
使い方
build.gradle
build.gradleに以下を追加します。
implementation "androidx.activity:activity-ktx:1.2.0-alpha04"
implementation "androidx.fragment:fragment-ktx:1.3.0-alpha03"
シンプルな使い方
シンプルにActivityの結果だけを受け取りたい場合は以下のように書けます。
// 普段どおりIntentを生成する
val intent = Intent(this, SecondActivity::class.java)
// registerForActivityResult を定義する
val launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
// 呼び出し先のActivityを閉じた時に呼び出されるコールバック
if(result.resultCode == Activity.RESULT_OK) {
// RESULT_OK時の処理
val intent = result.data
intent?.getStringExtra("result_key")
}
}
// Activityを起動
launcher.launch(intent)
registerForActivityResult
に ActivityResultContracts.StartActivityForResult()
を引数として渡すことによって、 ActivityResult
型の結果をコールバックで受け取ることが出来ます。
ActivityResult
には resultCode ( onActivityResult
の resultCode と同様)と、 data (Intent) というプロパティがあるので、そちらから結果を処理します。
呼び出し先のActivityは以前と変わりません。
intent.putExtra("result_key", "result")
// 結果をセット
setResult(Actiivty.RESULT_OK, intent)
finish()
カスタムの ActivityResultContracts
ActivityResultContracts
を継承した class を定義することで、カスタムの ActivityResultContracts
を利用することもできます。
ActivityResultContracts 継承クラスの作成
今回はobjectで定義しました。
object ResultContracts: ActivityResultContract<Int, String?>() {
// Activity起動時の処理
override fun createIntent(context: Context, input: Int): Intent {
val intent = Intent(context, SecondActivity::class.java)
intent.putExtra("select_id", input)
return intent
}
// 結果受け取り時の処理
override fun parseResult(resultCode: Int, intent: Intent?): String? {
if(resultCode == Activity.RESULT_OK) {
return intent?.getStringExtra("result_key")
}
return null
}
}
class に ActivityResultContract<{Inputの型}, {Outputの型}>
の形で継承させます。
作成したクラスには、Activityを起動する時に呼ばれる createIntent()
とActivityから結果を受け取る時に呼ばれる parseResult()
をoverrideする必要があります。
createIntent()
には registerForActivityResult.launch()
が呼び出された時の処理を、 parseResult()
には Activityから結果を受け取った時の処理を書きます。
あとは、作成した class を registerForActivityResult
に渡してあげます。
val launcher = registerForActivityResult(ResultContracts) { result ->
// 受け取った結果を処理する
text_view.text = result
}
launcher.launch(0) // InputにInt型を定義したのでInt型の引数を渡す
どんな時に使う?
上記ではInputとOutputで定義した型をそのまま渡しているため恩恵はありませんが、Activityから受け取った値を加工してから渡したい場合などに便利です。
object ResultContracts: ActivityResultContract<Int, User?>() { // Outputの型をUser型に修正
// Activity起動時の処理
...
// 結果受け取り時の処理
override fun parseResult(resultCode: Int, intent: Intent?): User? {
if(resultCode == Activity.RESULT_OK) {
// Activityから文字列を受け取る
val text = intent?.getStringExtra("result_key")
// 受け取った値を独自クラスに詰め替えて返す
val user = User(name = text)
return user
}
return null
}
}
val launcher = registerForActivityResult(ResultContracts) { result: User? ->
// OutputをUser?型で定義したのでUser?型が返ってくる
text_view.text = result?.name
}
launcher.launch(0) // Inputは変えていないのでInt型のまま
requestPermissions
requestPermissions も ActivityResultContracts を利用して代替可能です。
ActivityResultContracts.RequestPermission
という関数が用意されているので、 registerForActivityResult
に引数として渡してあげます。
val requestPermission = registerForActivityResult(ActivityResultContracts.RequestPermission()) { result ->
if(result) {
// リクエスト許可時の処理
Toast.makeText(this, "Permission Accepted.", Toast.LENGTH_SHORT).show()
}
else {
// リクエスト拒否時の処理
Toast.makeText(this, "Permission Denied.", Toast.LENGTH_SHORT).show()
}
}
// アクセスしたい権限を引数に渡す
requestPermission.launch(Manifest.permission.ACCESS_FINE_LOCATION)
コールバックの引数(result)はBoolean型です。権限が許可された場合 true が、拒否された場合は false が渡されてきます。
AndroidManifest.xmlに要求する権限を記載する必要があるのはいつも通りです。
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
注意点
すたぜろさんのこちらの記事の注意点は registerForActivityResult
の場合にも当てはまると思われます。 prepareCall
-> registerForActivityResult
に置き換えて読んでください。
(このあたりは自分で検証出来てません。すみません)
おわりに
Activityへの値の受け渡しは長い間 startActivityForResult
が使われてきましたが、ここにきて大きな変更が加わりました。
これから新規作成するアプリは、将来的に registerForActivityResult
へ置き換えることを念頭に入れて作る必要があると思われます。
本記事を書くにあたり、以下の記事を参考にさせて頂きました。