머리말
최근 향 정말 좋은 정직, 코 틀린를 연습, 회사가 단지 나에게 새 프로젝트를 제공 할 계획이다, 그는 바로 코 틀린 프로젝트를 빌드하기위한 것. 그냥 완료 설정 전체 프레임 워크, 그래서 다른 사람이 네트워크 요청의 첫 번째 부분. 이 사용은 코 루틴 + 개조 + 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
}
}
발문
단지가 구현 프로세스의 일부를 설명보다도, 우리는 기본적으로 대부분의 요구를 충족, 특정 참조 데모를 사용했다 관심이 작은 파트너, 당신은 데모 참조를 다운로드 할 수있는 경우, 그때 쉽게 매우 만족 칭찬을 지적, 좋은 느낌. 잘되지 내용은 부적절한 사용이있을 수있다, 나는 당신이 오빠 잘못된 장소, 매우 감사를 가리킬 수 있기를 바랍니다.