Androidで協程を正しく使うにはどうすればいいですか?
9361 ワード
前言
Google IOがAndroid 1級開発言語に
今日お話ししたいのは
協程に関する文章はたくさん読んだことがありますが、まとめてみると、次のようなものにすぎません.
1つ目はMediumの人気記事の翻訳ですが、実は私も翻訳しました.
Androidで使用協程(一):Getting The Background
Androidで使用協程(二):Getting started
Androidでの使用協程(三):Real Work
正直に言うと、この3つの文章は確かに私の協力に対する理解を深めた.
第2類は公式文書の翻訳で、私は少なくとも5つの翻訳バージョンを見たことがありますが、やはり公式サイトの文書を見たほうがいいと思います.もし英語が本当に骨が折れるなら、Kotlin中国語ステーションの翻訳と照らして読むことができます.
公式文書を長い間読んでいたが、私はほとんど
Androidでのコラボレーションの使用
GlobalScope
一般的なアプリケーションシーンでは、ネットワークリクエスト、データ処理など、時間のかかるタスクを非同期で行うことを望んでいます.現在のページを離れるときも、進行中の非同期タスクをキャンセルすることを望んでいます.この2点は,まさに使用協程において注意すべき点である.
Global scope is used to launch top-level coroutines which are operating on the whole application lifetime and are not cancelled prematurely. Another use of the global scope is operators running in Dispatchers.Unconfined, which don’t have any job associated with them.
Application code usually should use an application-defined CoroutineScope. Using async or launch on the instance of GlobalScope is highly discouraged.
一般に、Global scopeは、アプリケーションのライフサイクル全体で実行され、早期にキャンセルされないトップレベルのコラボレーションを開始するために使用されます.プログラムコードは、通常、カスタムのコモンシップドメインを使用する必要があります.GlobalScopeを直接使用するasyncまたはlaunch法は強く推奨されない.
GlobalScopeによって作成されるコンシステントには親コンシステントはありません.GlobalScopeは通常、ライフサイクルコンポーネントにバインドされません.手動で管理しない限り、実際の開発のニーズを満たすことは難しい.だから、GlobalScopeはできるだけ使わないでください.
MainScope
公式ドキュメントでは、カスタマイズされたコラボレーションドメインを使用することについて説明しています.もちろん、Kotlinは適切なコラボレーションドメイン
この定義を覚えておくと、後のViewModelのコンセンサス使用でもこの書き方を参考にします.
私たちのActivityに独自のコラボレーションドメインを実現します.
拡張関数
最後に、
ViewModelScope
MVVMアーキテクチャを使用している場合は、Activityに論理コードを書くことはありません.コラボレーションを開始することは言うまでもありません.この時、ほとんどの仕事はView Modelに任せます.では、どのようにしてViewModelでコラボレーションドメインを定義しますか?上の
ここの
これらのロジックを処理するために
そして、何もする必要はなく、コパスドメイン
コードを見てみればわかるはずですが、よく知っているセットです.
ここまで書くと、
LiveData
Kotlinは同様にLiveDataに直接協程を使用する能力を与えた.次の依存関係を追加します.
非同期で実行する必要がある保留関数を
ここには何の表示呼び出しもないようですが、liveDataコードブロックは何で実行されているのでしょうか.ライブデータが
したがって、
LifecycleScope
LifeCycleが
少なくとも特定のライフサイクルの後に保留関数を実行することを指定でき、Viewレイヤの負担をさらに軽減できます.
まとめ
以上、Androidでのコラボレーションの合理的な使用方法について簡単に説明しました.サンプルコードはGithubにアップロードされています.
文章の先発微信公衆番号:
もっと最新のオリジナル文章、コードをスキャンして私に注目してください!
Google IOがAndroid 1級開発言語に
Kotlin
を正式に発表したのを覚えていますか?Google IO 2017
です.今から2年が経ち、Android開発者の立場から見ると、Kotlinの生態環境はますますよくなり、関連するオープンソースプロジェクトや学習資料も豊富になり、身の回りでKotlinを使用したり試したりしたい友达も増えています.長年にわたって金を掘ってきた私も、Kotlinのラベルの下の文章がだんだん多くなってきたことを明らかに感じることができます(実はまだ少ないのがかわいそうです).今年のGoogle IOもKotlin First
のスローガンを発表し、多くの新しいAPIと機能特性がKotlinサポートを優先的に提供する.だから、今日になって、アンドロイド開発者がKotlinを勉強しない理由が見つからない.今日お話ししたいのは
Kotlin Coroutine
です.Kotlinリリース当初からコンセンサスがあったが、2018年のKotlinConf大会までJetBrainがKotlin 1.3 RCをリリースして安定版コンセンサスをもたらした.安定版のコラボレーションが1年以上発表されても、十分なユーザーはいないようですが、少なくとも私から見ればそうです.私が協程を学ぶ各段階の中で、問題に直面しても助けを求める場所は珍しく、技術群に投げつけると基本的に石が海に沈む.基本的には英語のドキュメントで問題を解決するしかありません.協程に関する文章はたくさん読んだことがありますが、まとめてみると、次のようなものにすぎません.
1つ目はMediumの人気記事の翻訳ですが、実は私も翻訳しました.
Androidで使用協程(一):Getting The Background
Androidで使用協程(二):Getting started
Androidでの使用協程(三):Real Work
正直に言うと、この3つの文章は確かに私の協力に対する理解を深めた.
第2類は公式文書の翻訳で、私は少なくとも5つの翻訳バージョンを見たことがありますが、やはり公式サイトの文書を見たほうがいいと思います.もし英語が本当に骨が折れるなら、Kotlin中国語ステーションの翻訳と照らして読むことができます.
公式文書を長い間読んでいたが、私はほとんど
GlobalScope
しか知らなかった.確かに、公式文書では基本的にGlobalScopeでサンプルコードを書いています.だから一部の開発者は、私自身も含めて、自分のコードを書くときも直接GlobalScopeになりました.偶然の機会にこのような問題が大きいことに気づいた.Androidでは一般的にGlobalScopeを直接使うことはお勧めしません.では、Androidではどのように協程を正しく使うべきでしょうか.もう少し細分化して、Activityで直接使うにはどうすればいいですか?ViewModel、LiveData、LifeCycleなどにどう合わせて使うのでしょうか?私は簡単なサンプルコードでAndroidの協力使用を説明します.あなたも手について叩くことができます.Androidでのコラボレーションの使用
GlobalScope
一般的なアプリケーションシーンでは、ネットワークリクエスト、データ処理など、時間のかかるタスクを非同期で行うことを望んでいます.現在のページを離れるときも、進行中の非同期タスクをキャンセルすることを望んでいます.この2点は,まさに使用協程において注意すべき点である.
GlobalScope
を直接使用することをお勧めしない以上、まずそれを使用するとどのような効果があるかを試してみましょう.private fun launchFromGlobalScope() {
GlobalScope.launch(Dispatchers.Main) {
val deferred = async(Dispatchers.IO) {
// network request
delay(3000)
"Get it"
}
globalScope.text = deferred.await()
Toast.makeText(applicationContext, "GlobalScope", Toast.LENGTH_SHORT).show()
}
}
launchFromGlobalScope()
メソッドでは、GlobalScope.launch()
を介して直接コモンシップを開始し、delay(3000)
はネットワーク要求をシミュレートし、3秒後にToastプロンプトがポップアップします.使用上は何の問題もなく、正常にToastをイジェクトできます.しかし、この方法を実行すると、すぐに戻るキーを押して前のページに戻ると、Toastがポップアップされます.実際に開発中にネットワークを介してページの更新を要求すると,ユーザがこのページにいなくなった場合,これ以上要求する必要はなく,リソースを浪費するだけである.GlobalScopeは明らかにこの特性に合致していない.Kotlinドキュメントでは、以下のように詳細に説明されています.Global scope is used to launch top-level coroutines which are operating on the whole application lifetime and are not cancelled prematurely. Another use of the global scope is operators running in Dispatchers.Unconfined, which don’t have any job associated with them.
Application code usually should use an application-defined CoroutineScope. Using async or launch on the instance of GlobalScope is highly discouraged.
一般に、Global scopeは、アプリケーションのライフサイクル全体で実行され、早期にキャンセルされないトップレベルのコラボレーションを開始するために使用されます.プログラムコードは、通常、カスタムのコモンシップドメインを使用する必要があります.GlobalScopeを直接使用するasyncまたはlaunch法は強く推奨されない.
GlobalScopeによって作成されるコンシステントには親コンシステントはありません.GlobalScopeは通常、ライフサイクルコンポーネントにバインドされません.手動で管理しない限り、実際の開発のニーズを満たすことは難しい.だから、GlobalScopeはできるだけ使わないでください.
MainScope
公式ドキュメントでは、カスタマイズされたコラボレーションドメインを使用することについて説明しています.もちろん、Kotlinは適切なコラボレーションドメイン
MainScope
を提供しています.MainScopeの定義を見てみましょう.public fun MainScope(): CoroutineScope = ContextScope(SupervisorJob() + Dispatchers.Main)
この定義を覚えておくと、後のViewModelのコンセンサス使用でもこの書き方を参考にします.
私たちのActivityに独自のコラボレーションドメインを実現します.
class BasicCorotineActivity : AppCompatActivity(), CoroutineScope by MainScope() {}
拡張関数
launch()
を使用すると、主スレッドで直接コパスを開始できます.サンプルコードは次のとおりです.private fun launchFromMainScope() {
launch {
val deferred = async(Dispatchers.IO) {
// network request
delay(3000)
"Get it"
}
mainScope.text = deferred.await()
Toast.makeText(applicationContext, "MainScope", Toast.LENGTH_SHORT).show()
}
}
最後に、
onDestroy()
でコンシステントをキャンセルし、関数cancel()
を拡張することによって実現することを忘れないでください.override fun onDestroy() {
super.onDestroy()
cancel()
}
launchFromMainScope()
の方法を試してみましょう.これはあなたのニーズに完全に合っていることに気づきます.実際の開発ではMainScopeをBaseActivityに統合できるので,テンプレートコードを繰り返し書く必要はない.ViewModelScope
MVVMアーキテクチャを使用している場合は、Activityに論理コードを書くことはありません.コラボレーションを開始することは言うまでもありません.この時、ほとんどの仕事はView Modelに任せます.では、どのようにしてViewModelでコラボレーションドメインを定義しますか?上の
MainScope()
の定義を覚えていますか?そうです.引っ越して直接使えばいいです.class ViewModelOne : ViewModel() {
private val viewModelJob = SupervisorJob()
private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
val mMessage: MutableLiveData = MutableLiveData()
fun getMessage(message: String) {
uiScope.launch {
val deferred = async(Dispatchers.IO) {
delay(2000)
"post $message"
}
mMessage.value = deferred.await()
}
}
override fun onCleared() {
super.onCleared()
viewModelJob.cancel()
}
}
ここの
uiScope
は実はMainScope
に等しい.呼び出しgetMessage()
メソッドは、以前のlaunchFromMainScope()
と同じ効果で、ViewModelのonCleared()
コールでコンシステントをキャンセルしたことを覚えています.これらのロジックを処理するために
BaseViewModel
を定義し、テンプレートコードを繰り返し書くことを避けることができます.しかしKotlinはあなたに同じことをさせて、もっと少ないコードを書いて、viewmodel-ktx
に来ました.ktxを見ると、コードを簡略化するために使われていることがわかります.次の依存関係を導入します.implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0-alpha03"
そして、何もする必要はなく、コパスドメイン
viewModelScope
をそのまま使えばよい.viewModelScope
は、次のように定義されたViewModelの拡張属性です.val ViewModel.viewModelScope: CoroutineScope
get() {
val scope: CoroutineScope? = this.getTag(JOB_KEY)
if (scope != null) {
return scope
}
return setTagIfAbsent(JOB_KEY,
CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main))
}
コードを見てみればわかるはずですが、よく知っているセットです.
ViewModel.onCleared()
が呼び出されると、viewModelScope
は、アクティブドメイン内のすべてのコヒーレンスを自動的にキャンセルする.使用例は次のとおりです.fun getMessageByViewModel() {
viewModelScope.launch {
val deferred = async(Dispatchers.IO) { getMessage("ViewModel Ktx") }
mMessage.value = deferred.await()
}
}
ここまで書くと、
viewModelScope
は需要を満たす最も簡単な書き方です.実際、全編を書き終えて、viewModelScope
は依然として私が思っている最高の選択です.LiveData
Kotlinは同様にLiveDataに直接協程を使用する能力を与えた.次の依存関係を追加します.
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-alpha03"
非同期で実行する必要がある保留関数を
liveData {}
コードブロックに直接呼び出し、emit()
関数を呼び出して処理結果を送信する.サンプルコードは次のとおりです.val mResult: LiveData = liveData {
val string = getMessage("LiveData Ktx")
emit(string)
}
ここには何の表示呼び出しもないようですが、liveDataコードブロックは何で実行されているのでしょうか.ライブデータが
active
状態に入ると、liveData{ }
が自動的に実行されます.LiveDataがinactive
状態になると、構成可能なtimeoutが経過すると自動的にキャンセルされます.それが完了する前にキャンセルされた場合、LiveDataが再びactive
になったときに再実行されます.前回の実行が正常に終了した場合、再実行は行われません.つまり自動キャンセルされたliveData{ }
のみが運転を再開できるということです.その他の理由(例えばCancelationException
)によるキャンセルも再実行されません.したがって、
livedata-ktx
の使用には一定の制限があります.ユーザがアクティブにリフレッシュする必要があるシーンでは、満足できません.完全なライフサイクルでは、一度正常に実行されると、これ以上トリガーできません.この言葉は正しいかどうか分かりませんが、個人的にはそう理解しています.したがって、viewmodel-ktx
の適用性はより広く、制御性もより良い.LifecycleScope
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0-alpha03"
lifecycle-runtime-ktx
は、各LifeCycle
オブジェクトに対して、拡張属性によってコモンアクティブドメインlifecycleScope
を定義する.lifecycle.coroutineScope
またはlifecycleOwner.lifecycleScope
でアクセスできます.サンプルコードは次のとおりです.fun getMessageByLifeCycle(lifecycleOwner: LifecycleOwner) {
lifecycleOwner.lifecycleScope.launch {
val deferred = async(Dispatchers.IO) { getMessage("LifeCycle Ktx") }
mMessage.value = deferred.await()
}
}
LifeCycleが
onDestroy()
にコールバックすると、コモンアクティブドメインlifecycleScope
は自動的にキャンセルされます.Activity/Fragment
などのライフサイクルコンポーネントでは便利に使用できますが、MVVMではあまりViewレイヤで論理処理を行うことはありません.viewModelScopeは基本的にView Modelのニーズを満たすことができ、lifecycleScope
も少し味気ないように見えます.しかし、彼には特別な使い方があります.suspend fun Lifecycle.whenCreated()
suspend fun Lifecycle.whenStarted()
suspend fun Lifecycle.whenResumed()
suspend fun LifecycleOwner.whenCreated()
suspend fun LifecycleOwner.whenStarted()
suspend fun LifecycleOwner.whenResumed()
少なくとも特定のライフサイクルの後に保留関数を実行することを指定でき、Viewレイヤの負担をさらに軽減できます.
まとめ
以上、Androidでのコラボレーションの合理的な使用方法について簡単に説明しました.サンプルコードはGithubにアップロードされています.
MVVM +
の実戦種目については、私のオープンソース種目wanandroidを見てもいいし、貴重な意見も期待できます.文章の先発微信公衆番号:
、Java、Androidのオリジナル知識の共有に専念し、LeetCodeの問題解.もっと最新のオリジナル文章、コードをスキャンして私に注目してください!