Wofkflow Dispatcherとは
GitHub - eaglesakura/workflow-dispatcher
- Androidアプリ開発でよくある
画面やActivityやプロセスを跨ぐ可能性のある非同期処理
をなるべく簡単に扱うためのライブラリ - Annotation Processorを使って、定形処理を出力する
- startActivityForResult/onActivityResultの分岐を書く必要がなくなる
- Runtime Permissionの処理を書く必要が無くなる
- DialogFragmentのコールバックに気を使う必要が無くなる
一時的な情報をメンバ変数ではなく引数として扱うことができる
- 個人的にコレが重要
- Activity再生成(回転とかLowMemoryとか)しても大丈夫
- 初めてAnnotation Processorを実装した
- 以前は否定的だったけど、十分にマシンスペックが上がったので心の中で解禁
似たライブラリ
使うとどう書けるのか
- 普通に書けるライブラリはいろいろあるが、例えば
onActivityResult
に戻ってきたときに一時変数の値を使いたい場合は結構気を使う - このサンプルでは、ブラウザを開いた時刻を保存しておき、onActivityResultでToast表示に使っている
- AndroidではActivityやFragmentが再生成される(新しいインスタンスが作られる)場合があるため、save/resotreには気を使う
- 別Activityで開く -> Runtime Permissionで権限を得る -> ダイアログでYES/NOを質問するとかを順番にやると一時変数が結構増えていく
- ライブラリが自動的に一時情報を保存・レストアして、コールバック時に渡してくれるのでActivityとかFragmentをクリーンに保てる
Before
class ExampleBeforeActivity : AppCompatActivity() { private var tempDialogStartDate: Date? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) if(savedInstanceState == null) { tempDialogStartDate = startDate startActivityForResult( Intent( Intent.ACTION_VIEW, Uri.parse("https://google.com") ), REQUEST_SHOW_BROWSER ) } } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putSerializable("startDate", tempDialogStartDate) } override fun onRestoreInstanceState(savedInstanceState: Bundle) { super.onRestoreInstanceState(savedInstanceState) tempDialogStartDate = savedInstanceState.getSerializable("startDate") as? Date } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { when (requestCode) { REQUEST_SHOW_BROWSER -> { // read temporary data, clear temporary val startDate = tempDialogStartDate!! tempDialogStartDate = null // show toast Toast.makeText(this, "done workflow, startDate='$startDate'", Toast.LENGTH_SHORT) .show() } else -> super.onActivityResult(requestCode, resultCode, data) } } companion object { const val REQUEST_SHOW_BROWSER = 0x0011 } }
After
class ExampleAfterActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) if(savedInstanceState == null) { // show browser // ExampleAfterActivity.showWebsite() is generated by kapt(workflow-dispatcher-processor). // auto save/restore/clear `startDate` state. showWebsite( Intent(Intent.ACTION_VIEW,Uri.parse("https://google.com")), startDate = Date() ) } } @OnActivityResultFlow("showWebsite") fun onShowWebsiteResult(result: ActivityResult, startDate: Date) { // show toast Toast.makeText(this, "startDate='$startDate'", Toast.LENGTH_SHORT).show() } }
どうやって実現しているのか
- startActivityとかするタイミングで、ライブラリが WorkflowProviderFragment をchildFragmentManagerに差し込む
- WorkflowProviderFragmentの中でstartActivityとかpermissionとか処理をして、結果をコールバックしている
- 一時データの保存もWorkflowProviderFragmentの中に持っているViewModelに持たせているので、呼び出し側を汚染しない
保存できるステート
- 生成されるコードを見るとわかるが、内部的にはBundleに突っ込んでいるので、Bundleに保存可能な値であれば
@OnActivityResultFlow
の引数として受け取ることができる - ステートが不要な場合は
result: ActivityResult
とか第1引数だけでメソッドを宣言すれば良い
使い続けられるのか?
- Jetpack Navigation ライブラリが同じ機能をサポートしたらコイツは役目を終える
- 公式、ガンバ・・・