Kotlin + + +レトロフィットMVVMのコルーチンエレガントな実装のネットワーク要求

Kotlin + + +レトロフィットMVVMのコルーチンエレガントな実装のネットワーク要求

序文

最近、お香と本当にいい正直に言うと、Kotlinを練習して、会社はちょうど私に新しいプロジェクトを与えることを計画し、彼が直接Kotlinのプロジェクトをビルドするためのもの。ただ、完成されたセットアップ、全体のフレームワークなので、他の人にこのネットワーク要求の最初の部分。この使用は、コルーチン+改造+ MVVMモデルであると、私はここでの具体的な実装では、簡単なデモを見ての直接の使用を持っています。記事では、デモの最後にテキストに直接ジャンプする必要があり、アイデアの実現を説明します。

プロジェクトの構成

まず、最初に依存必要な導入

implementation 'android.arch.lifecycle:extensions:1.1.1'
 //协程
 implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1'
 implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1'
 //retrofit + okHttp3
 implementation 'com.squareup.retrofit2:retrofit:2.4.0'
 implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
 implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'

アイデアの実現

単純なネットワークの要求にかかわらず、これらのデザインパターンの、最初の、どのような手順の基本的な実現の改造は確認する必要があります

1.改造を作成します。

~~~
 val retrofit = Retrofit.Builder()
 .baseUrl(RetrofitClient.BASE_URL)
 .addConverterFactory(GsonConverterFactory.create())
 .addCallAdapterFactory(CoroutineCallAdapterFactory())
 .build()
~~~

2.サービス・インターフェースを作成します。

~~~
 interface RequestService {
 @GET("wxarticle/chapters/json")
 fun getDatas() : Call<DataBean>
 }
~~~

3.要求を開始します

~~~
 val service = retrofit.create(RequestService::class.java)
 service.getDatas().enqueue(object : Callback<DataBean> {
 override fun onFailure(call: retrofit2.Call<DataBean>, t: Throwable) {
 TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
 }
 override fun onResponse(call: retrofit2.Call<DataBean>, response: Response<DataBean>) {
 TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
 }
 })
~~~

この実施形態は、改造の唯一の単純な要求を記述したパッケージは、実質的にされた後、実際のプロジェクトが使用されますが、また、コードの可読性を向上させる結合を低減する、人気のポイントの各部分のみその職務を置くことをしますオーケー仕事をして、それから私達は達成するためにその職務を遂行するために一つ一つの周りに移動します

達成コルーチン

コルーチンを達成するための方法に続いて、上記のリクエスト

1. RetrofitClientを作成します。

object为了使RetrofitClient 只能有一个实例
~~~
 object RetrofitClient {
 val BASE_URL = "https://wanandroid.com/"
 val reqApi by lazy {
 val retrofit = Retrofit.Builder()
 .baseUrl(BASE_URL)
 .addConverterFactory(GsonConverterFactory.create())
 .addCallAdapterFactory(CoroutineCallAdapterFactory())
 .build()
 return@lazy retrofit.create(RequestService::class.java)
 }
 }
~~~

2.サービス・インターフェース・クラスを作成します。

~~~
interface RequestService {
 @GET("wxarticle/chapters/json")
 fun getDatas() : Deferred<DataBean>
}
~~~

フォローアップ我々はコルーチンに使用するので、ので、ここでは、繰延コールを置き換えられます

3.要求を開始します

~~~
 GlobalScope.launch(Dispatchers.Main) {
 withContext(Dispatchers.IO){
 val dataBean = RetrofitClient.reqApi.getDatas().await()
 }
 //更新ui
 }
~~~

上記の唯一の彼のアプリケーション、特定の会場や詳細については、公式ドキュメントについてはこちら、コルーチンを使用しています。コルーチンネットワーク要求、スケジューリング及びIOユニット、メインスレッドは、恐れることなくブロックします

コルーチン+のViewModel + LiveData実装

上記見えるので、ビューモデルコンポーネントアーキテクチャはまた、新たに導入されたとAndroid LiveDataを使用し、さらに包装することができる以前MVVM述べ使いやすさを単に実現されるが、コルーチンに置換され、プロジェクト内ViewModelにを実装

class ScrollingViewModel : ViewModel() {
 private val TAG = ScrollingViewModel::class.java.simpleName
 private val datas: MutableLiveData<DataBean> by lazy { MutableLiveData<DataBean>().also { loadDatas() } }
 private val repository = ArticleRepository()
 fun getActicle(): LiveData<DataBean> {
 return datas
 }
 private fun loadDatas() {
 GlobalScope.launch(Dispatchers.Main) {
 getData()
 }
 // Do an asynchronous operation to fetch users.
 }
 private suspend fun getData() {
 val result = withContext(Dispatchers.IO){
// delay(10000)
 repository.getDatas()
 }
 datas.value = result
 }
}

中間のデータ、フルタイムのデータ収集リポジトリ、データを取得するネットワーク要求を開始するために、次のコードでのリポジトリの外観として表示し、ViewModelに

 class ArticleRepository {
 suspend fun getDatas(): DataBean {
 return RetrofitClient.reqApi.getDatas().await()
 }
 }

次のコード活動に

 private fun initData() {
 model.getActicle().observe(this, Observer{
 //获取到数据
 toolbar.setBackgroundColor(Color.RED)
 })
 }

後続の最適化

1.メモリリークの問題解決

結び目とビューの大物たちが、メモリリークの問題は、最適化されているかもしれGlobalScopeを使用します。コルーチンを要求している場合ビューモデルの破壊は、それが破壊されない場合、プロセスは、メモリリークをコルーチンを要求するので、ビューモデルは、以下のコードを参照して、さらにタスクコルーチンの終了場合、内部onCleared。ため

 open class BaseViewModel : ViewModel(), LifecycleObserver{
 private val viewModelJob = SupervisorJob()
 private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
 //运行在UI线程的协程
 fun launchUI( block: suspend CoroutineScope.() -> Unit) {
 try {
 uiScope.launch(Dispatchers.Main) {
 block()
 }
 }catch (e:Exception){
 e.printStackTrace()
 }
 }
 override fun onCleared() {
 super.onCleared()
 viewModelJob.cancel()
 }
}

もちろん、最良の方法はviewModelScopeを使用することですが、私はこのパッケージを導入したときに、文句を言うでしょう、なぜならないより緊急解決するという、最近忙しいの、フォローアップの質問は、私が修正していきます時間を持っているだけでなく、あなたが兄を助けることを願ってポインティング

2.要求コードの最適化

前回の要求コードを見てください

private suspend fun getData() {
 val result = withContext(Dispatchers.IO){
// delay(10000)
 repository.getDatas()
 }
 datas.value = result
 }

毎回これwithContext()、実際、少し不便を感じを書き、する必要性が、どのように彼にその方法の要求にシールを与えることを一瞬考えたこと?コードは以下の通りです

open class BaseRepository {
 suspend fun <T : Any> request(call: suspend () -> ResponseData<T>): ResponseData<T> {
 return withContext(Dispatchers.IO){ call.invoke()}
 }
}

各要求は、単に次のラインの参照に要求を実行するようにBaseRepositoryにより、特別なリクエストメソッド内書き込ま

class ArticleRepository : BaseRepository() {
 suspend fun getDatas(): ResponseData<List<Data>> {
 return request {
 delay(10000)
 Log.i(ScrollingViewModel::class.java.simpleName,"loadDatas1 run in ${Thread.currentThread().name}")
 RetrofitClient.reqApi.getDatas().await() }
 }
}

注:この遅延(10000)ちょうど私のテスト、その現在のコルーチン睡眠を意味し、自分のプロジェクトに新しい追加発芽防止し、またはについて話をする必要性

ViewModelに見て、あまりにも簡単です

class ScrollingViewModel : BaseViewModel() {
 private val TAG = ScrollingViewModel::class.java.simpleName
 private val datas: MutableLiveData<List<Data>> by lazy { MutableLiveData<List<Data>>().also { loadDatas() } }
 private val repository = ArticleRepository()
 fun getActicle(): LiveData<List<Data>> {
 return datas
 }
 private fun loadDatas() {
 launchUI {
 Log.i(TAG,"loadDatas1 run in ${Thread.currentThread().name}")
 val result = repository.getDatas()
 Log.i(TAG,"loadDatas3 run in ${Thread.currentThread().name}")
 datas.value = result.data
 }
 // Do an asynchronous operation to fetch users.
 }
}

要求部に注意してください、ちょうど2つの文章、valの結果を開始する要求= repository.getDatas()の割り当ては、私たちのLiveDataのためである、そして、何の同期コードがない感じに見える、これはコルーチンの魅力である、検証するために私たちの要求は、メインスレッドをブロックしません、私はログを印刷します

06-19 12:26:35.736 13648-13648/huaan.com.mvvmdemo I/ScrollingViewModel: loadDatas start run in main
06-19 12:26:45.743 13648-13684/huaan.com.mvvmdemo I/ScrollingViewModel: request run in DefaultDispatcher-worker-1
06-19 12:26:46.227 13648-13648/huaan.com.mvvmdemo I/ScrollingViewModel: loadDatas end run in main

参照してください、その職務を遂行、効果が大きいです

例外処理

だから今、私たちも例外ハンドラは、要求が失敗したときに、プロジェクトが崩壊し、存在しない見つけるためにのみ取得し、これは我々がTyrでキャッチが出Dingyidingの一組のみに対処するためのより良い方法を考えていないため、良いに、結果を望むものではありません、および下記を参照してください。

open class BaseViewModel : ViewModel(), LifecycleObserver{
 private val viewModelJob = SupervisorJob()
 private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
 private val error by lazy { MutableLiveData<Exception>() }
 private val finally by lazy { MutableLiveData<Int>() }
 //运行在UI线程的协程
 fun launchUI( block: suspend CoroutineScope.() -> Unit) {
 uiScope.launch(Dispatchers.Main) {
 try {
 block()
 }catch (e:Exception){
 error.value = e
 }finally {
 finally.value = 200
 }
 }
 }
 override fun onCleared() {
 super.onCleared()
 viewModelJob.cancel()
 }
 /**
 * 请求失败,出现异常
 */
 fun getError(): LiveData<Exception> {
 return error
 }
 /**
 * 请求完成,在此处做一些关闭操作
 */
 fun getFinally(): LiveData<Int> {
 return finally
 }
}

エピローグ

唯一の実装プロセスの一部を説明の上、我々は、特定の参照のデモを使用していた基本的にほとんどのニーズを満たす、興味小さなパートナーならば、あなたはデモの参照をダウンロードすることができ、私は良い感じ、その後、簡単に非常に満足して賞賛を指します。罰金ではない学習するには、不適切な使用があるかもしれない、私は非常に感謝し、あなたが兄間違った場所を指すことができます願っています。

おすすめ

転載: blog.51cto.com/14332859/2428845