startActivityForResult
, onActivityResult
は廃止されます。これに関しては既に内部的には ActivityResultContract
等を使用するように置き換えられています。
用途
Intentを作成して起動( startActivityForResult
) -> 結果を受け取るもの( onActivityResult
)は今後これを使います。
使い方
ActivityやFragmentでとあるファイルをダウンロードして書き込みを行いたいとしたら、以下のようにします。
(今はScoped Storageの話は忘れてください)
private val launcher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { it : ActivityResultCallback -> if (it) { // TODO: ダウンロード -> 書き込み処理 } } fun download() { launcher.launch(Manifest.permission.READ_EXTERNAL_STORAGE) }
解説
ActivityResultContract
, ActivityResultLauncher
, ActivityResultCallback
の3つが登場します。
registerForActivityResult
で ActivityResultContract
と ActivityResultCallback
を指定します。
すると戻り値で ActivityResultLauncher
が取得できます。
ActivityResultLauncher
に対して launch
を行うと ActivityResultContracts
で指定された処理が行われ、 ActivityResultCallback
に結果がやってきます。
実装を見る
ActivityResultLauncher
はライブラリ側がよしなにやってくれるので今回は解説しません。
使用するだけなら ActivityResultContracts
と ActivityResultCallback
の実装だけわかっていれば大丈夫です。
ActivityResultCallback
ActivityResultContracts
は良く使われるであろうものは既に定義されています。 ActivityResultContracts.RequestPermission()
がそうです。
以下の3つ、最低2つを実装してあげるだけで大丈夫です。 parseResult
を見ると requestCode
という概念は存在しません。内部で使用されている ActivityResultRegistry
がよしなにやってくれるので requestCode
の管理から開放されます。
/** * Iで引数を指定する * Oで戻り値を指定する **/ public abstract class ActivityResultContract<I, O> { // Activity#setResult()で結果を返してくれるIntentを生成する public abstract @NonNull Intent createIntent( @NonNull Context context, @SuppressLint("UnknownNullness") I input ); // Intentを「O」にパースして返す @SuppressLint("UnknownNullness") public abstract O parseResult( int resultCode, @Nullable Intent intent ); // デフォルトでnullなので実装しなくても良い // ここで結果を返すとcreateIntentが呼ばれす、そのまま結果を返す。 public @Nullable SynchronousResult<O> getSynchronousResult( @NonNull Context context, @SuppressLint("UnknownNullness") I input ) { return null; } }
ActivityResultCallback
ActivityResultCallback
の結果の「O」が渡されるだけです。
public interface ActivityResultCallback<O> { void onActivityResult(@SuppressLint("UnknownNullness") O result); }
おまけ
使用する側でlaunchするわけですが、使用する側が使用するPermissionを意識するのって微妙じゃないですかね。
private val launcher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { it : ActivityResultCallback -> if (it) { // TODO: ダウンロード -> 書き込み処理 } } fun download() { launcher.launch(Manifest.permission.READ_EXTERNAL_STORAGE) }
というわけで、この様に書き換えてみました。普通に関数のように使用できます。
private val doDownload = object { private val launcher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { } operator fun invoke() { launcher.launch(Manifest.permission.READ_EXTERNAL_STORAGE) } }
Scoped Storage
等で特定OSバージョンで権限要らないなとなった場合は以下のように書けます。
private val doDownload = object { private val launcher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { if (it) { download() } else { // TODO: Error message } } operator fun invoke() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { download() } else { launcher.launch(Manifest.permission.READ_EXTERNAL_STORAGE) } } private fun download() { // TODO } }