31
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

startActivityForResult / requestPermissions が deprecated になる話

Posted at

はじめに

影響範囲が大きそうなのでまとめておきます。
※本記事は 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に以下を追加します。

build.gradle
implementation "androidx.activity:activity-ktx:1.2.0-alpha04"
implementation "androidx.fragment:fragment-ktx:1.3.0-alpha03"

シンプルな使い方

シンプルにActivityの結果だけを受け取りたい場合は以下のように書けます。

MainActivity
// 普段どおり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)

registerForActivityResultActivityResultContracts.StartActivityForResult() を引数として渡すことによって、 ActivityResult 型の結果をコールバックで受け取ることが出来ます。

ActivityResult には resultCode ( onActivityResult の resultCode と同様)と、 data (Intent) というプロパティがあるので、そちらから結果を処理します。

呼び出し先のActivityは以前と変わりません。

SecondActivity
intent.putExtra("result_key", "result")
// 結果をセット
setResult(Actiivty.RESULT_OK, intent)
finish()

カスタムの ActivityResultContracts

ActivityResultContracts を継承した class を定義することで、カスタムの ActivityResultContracts を利用することもできます。

ActivityResultContracts 継承クラスの作成

今回はobjectで定義しました。

ResultContracts
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 へ置き換えることを念頭に入れて作る必要があると思われます。

本記事を書くにあたり、以下の記事を参考にさせて頂きました。

31
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
31
17

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?