코 틀린 + + 개조 + MVVM의 코 루틴 우아한 구현 네트워크 요청

코 틀린 + + 개조 + MVVM의 코 루틴 우아한 구현 네트워크 요청

머리말

최근 향 정말 좋은 정직, 코 틀린를 연습, 회사가 단지 나에게 새 프로젝트를 제공 할 계획이다, 그는 바로 코 틀린 프로젝트를 빌드하기위한 것. 그냥 완료 설정 전체 프레임 워크, 그래서 다른 사람이 네트워크 요청의 첫 번째 부분. 이 사용은 코 루틴 + 개조 + MVVM 모델로, 나는 여기의 특정 구현에 간단한 데모 모양의 직접적인을 사용할 수 있습니다. 제는 아이디어의 실현을 설명, 데모의 끝에서 텍스트로 직접 이동해야합니다.

프로젝트 구성

제 1-1 의존 필요 소개

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 장치 차단

코 루틴 + 뷰 모델 + LiveData 구현

위는 간단히 구현됩니다 만, 프로젝트가 더 포장 할 수있다에, 코 루틴을 교체 언급 사용의 용이성은 이전 MVVM은 뷰 모델 구성 요소 아키텍처 때문에 또한 새로 도입 된 안드로이드 LiveData 사용 봐 뷰 모델을 구현

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
 }
}

중간 데이터, 풀 타임 데이터 수집 저장소, 데이터를 얻기 위해 네트워크 요청을 시작하는 다음 코드에서 저장소보기로보기와 뷰 모델

 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) 내 시험을,

뷰 모델 봐 너무 간단합니다

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.
 }
}

발 결과 = 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

자신의 임무를 수행, 참조, 효과는 크다

예외 처리

그래서 지금 우리는 예외 핸들러는 요청이 실패 할 때, 프로젝트가 붕괴 없다 찾기 만 얻는다면, 이것은 우리가 티르 캐치 밖으로 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