한 글 은 Kotlin 의 협 정 을 철저히 이해 하 였 다.

14518 단어 kotlin협정
배경 생 성
비동기 스 레 드 를 해결 하기 위 한 리 턴 지옥

//      
api.login(phone,psd).enquene(new Callback<User>(){
 public void onSuccess(User user){
 api.submitAddress(address).enquene(new Callback<Result>(){
 public void onSuccess(Result result){
 ...
 }
 });
 }
});

//     
val user=api.login(phone,psd)
api.submitAddress(address)
...
협 정 이 뭐 예요?
본질 적 으로 협 정 은 경량급 의 스 레 드 이다.
협정 키워드

val job = GlobalScope.launch {
 delay(1000)
 println("World World!")
}
CorotuneScope(역할 범위)
GlobeScope,lifecycle Scope,view ModelScope 및 기타 사용자 정의 CoroutineScope 를 포함 하여 협회 코드 블록 이 실행 하 는 스 레 드,수명 주기 등 을 제어 합 니 다.
GlobeScope:전역 범위,자동 으로 실행 이 끝나 지 않 습 니 다.
lifecycle Scope:라 이 프 사이클 범위,activity 등 라 이 프 사이클 이 있 는 구성 요소 에 사용 되 며,DESTROYED 시 자동 으로 종 료 됩 니 다.추가 도입 이 필요 합 니 다.
viewModelScope:viewModel 범위,ViewModel 에 사용 되 며,ViewModel 이 회수 되 었 을 때 자동 으로 끝 납 니 다.추가 도입 이 필요 합 니 다.
작업(작업)
협 정의 계량 단 위 는 한 번 의 작업 임무 에 해당 합 니 다.launch 방법 은 기본적으로 새로운 Job 으로 돌아 갑 니 다.
suspend(걸 기)
방법 에 작용 하 는 것 은 이 방법 을 대표 하 는 시간 소모 임무 이다.예 를 들 어 위의 delay 방법 이다.

public suspend fun delay(timeMillis: Long) {
 ...
}
협정의 도입
주 프레임 워 크($coroutinesversion 은 1.3.9 와 같은 최신 버 전 으로 바 뀌 었 습 니 다)

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
lifecycle Scope(선택 가능,버 전 2.2.0)

implementation 'androidx.activity:activity-ktx:$lifecycle_scope_version'
viewModelScope(선택 가능,버 전 2.3.0-beta 01)

implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$coroutines_viewmodel_version"
간단히 사용 하 다
먼저 간단 한 예 를 들다

lifecycleScope.launch { 
 delay(2000)
 tvTest.text="Test"
}
위의 이 예 에서 실 현 된 기능 은 2 초 를 기다 린 다음 에 id 가 tvtest 인 TextView 컨트롤 의 text 값 을 Test 로 수정 하 는 것 입 니 다.
사용자 정의 지연 반환 방법
kotlin 에 서 는 지연 이 있어 야 결 과 를 되 돌 릴 수 있 는 방법 에 대해 서 는 suspend 로 표시 해 야 합 니 다.

lifecycleScope.launch {
 val text=getText()
 tvTest.text = text
}

suspend fun getText():String{
 delay(2000)
 return "getText"
}
다른 스 레 드 에 서 는 Continuation 을 사용 하여 스 레 드 전환 이 필요 합 니 다.suspendCancellable Coroutine 또는 suspendCoroutine 패키지(전 자 는 취소 할 수 있 고 후자 의 확장 에 해당 합 니 다)를 사용 하여 it.resume()을 성공 적 으로 호출 하 였 으 며,it.resume With Exception()을 호출 하지 못 했 습 니 다.

suspend fun getTextInOtherThread() = suspendCancellableCoroutine<String> {
 thread {
 Thread.sleep(2000)
 it.resume("getText")
 }
}
이상 포획
협정 안의 실 패 는 모두 이상 포획 을 통 해 특수 상황 을 통일 적 으로 처리 할 수 있다

lifecycleScope.launch {
 try {
 val text=getText()
 tvTest.text = text
 } catch (e:Exception){
 e.printStackTrace()
 }
}
취소 기능
다음은 두 개의 job 를 실 행 했 습 니 다.첫 번 째 는 원본 입 니 다.두 번 째 는 1 초 후에 첫 번 째 job 를 취소 하면 tvText 의 텍스트 가 바 뀌 지 않 습 니 다.

val job = lifecycleScope.launch {
 try {
 val text=getText()
 tvTest.text = text
 } catch (e:Exception){
 e.printStackTrace()
 }
}
lifecycleScope.launch {
 delay(1000)
 job.cancel()
}
설정 시간 초과
이것 은 시스템 이 자동 취소 기능 을 패키지 한 것 과 같 습 니 다.대응 함수 withTimeout

lifecycleScope.launch {
 try {
 withTimeout(1000) {
  val text = getText()
  tvTest.text = text
 }
 } catch (e:Exception){
 e.printStackTrace()
 }
}
반환 값 을 가 져 온 Job
launch 와 유사 한 async 방법 이 있 습 니 다.Deferred 대상 을 되 돌려 줍 니 다.Job 의 확장 클래스 에 속 합 니 다.Deferred 는 되 돌아 오 는 결 과 를 얻 을 수 있 습 니 다.구체 적 으로 다음 과 같이 사용 합 니 다.

lifecycleScope.launch {
 val one= async {
 delay(1000)
 return@async 1
 }
 val two= async {
 delay(2000)
 return@async 2
 }
 Log.i("scope test",(one.await()+two.await()).toString())
}
고급 진급
사용자 정의 CorotuneScope
코 루 티 네 스 코 프 소스 부터 볼 게 요.

public interface CoroutineScope {
 public val coroutineContext: CoroutineContext
}
CoroutineScope 에는 주로 coroutineContext 대상 이 포함 되 어 있 습 니 다.coroutineContext 만 실현 하 는 get 방법 을 사용자 정의 해 야 합 니 다.

class TestScope() : CoroutineScope {
 override val coroutineContext: CoroutineContext
  get() = TODO("Not yet implemented")
}
coroutine Context 를 만 들 려 면 먼저 Coroutine Context 가 무엇 인지 알 고 Coroutine Context 소스 코드 를 봐 야 합 니 다.

/**
 * Persistent context for the coroutine. It is an indexed set of [Element] instances.
 * An indexed set is a mix between a set and a map.
 * Every element in this set has a unique [Key].
 */
public interface CoroutineContext {
 public operator fun <E : Element> get(key: Key<E>): E?
 public fun <R> fold(initial: R, operation: (R, Element) -> R): R
 public operator fun plus(context: CoroutineContext): CoroutineContext = 
  ...
 public fun minusKey(key: Key<*>): CoroutineContext
 
 public interface Key<E : Element>
 public interface Element : CoroutineContext {
  ...
 }
}
설명 을 통 해 우 리 는 그 본질 이 Element 를 포함 하 는 집합 이라는 것 을 알 고 있 습 니 다.set 와 map 집합 처럼 가 져 오기(get),접 기(fold,추가 와 교체 의 조합),상쇄(minusKey,제거),대상 조합(plus,예 를 들 어 val coroutineContext=coroutineContext 1+coroutineContext 2)을 실현 하지 않 았 습 니 다.
그것 의 주요 내용 은 Element 이 고 Element 의 실현 은
Job 퀘 스 트
Continuation Interceptor 차단기
  • AbstractCoroutineContextElement
  • CoroutineExceptionHandler
  • ThreadContextElement
  • DownstreamExceptionElement
  • ....
  • 많은 곳 에서 Element 를 실현 하 는 것 을 볼 수 있 는데 그 주요 목적 은 범위 제한 과 이상 한 처리 이다.여기 서 우 리 는 먼저 두 가지 중요 한 Element 를 알 아 보 겠 습 니 다.하 나 는 Job 이 고 하 나 는 CorotineDispatcher 입 니 다.
    Job
  • Job:하위 Job 이 취소 되면 부모 job 와 다른 하위 job 가 취 소 됩 니 다.부모 job 취소,모든 하위 job 취소.
    SupervisorJob:부모 job 취소,모든 하위 job 취소
  • CoroutineDispatcher
    Dispatchers.Main:메 인 스 레 드 실행Dispatchers.IO:IO 스 레 드 실행라 이 프 사이클 스 코 프 와 유사 한 사용자 정의 TestScope 를 모 의 합 니 다.
    
    class TestScope() : CoroutineScope {
     override val coroutineContext: CoroutineContext
      get() = SupervisorJob() +Dispatchers.Main
    }
    
    여기 서 우 리 는 전체 프로 세 스 라인 SupervisorJob()과 구체 적 인 실행 환경 Dispatchers.Main(Android 메 인 스 레 드)을 정의 합 니 다.activity 의 lifecy cleScope 를 교체 하려 면 activity 에서 인 스 턴 스 를 만들어 야 합 니 다.
    
    val testScope=TestScope()
    
    그리고 activity 소각 시 모든 job 를 취소 합 니 다.
    
    override fun onDestroy() {
     testScope.cancel()
     super.onDestroy()
    }
    
    다른 사용 방식 은 lifecycle Scope 와 같 습 니 다.
    
    testScope.launch{
     val text = getText()
     tvTest.text = text
    }
    
    Job 을 깊이 이해 하 다
    CorotineScope 에는 주 Job 이 포함 되 어 있 습 니 다.그 후에 호출 된 launch 또는 다른 방법 으로 만 든 job 는 모두 CorotineScope 의 하위 Job 에 속 합 니 다.모든 job 는 자신 만 의 상태 가 있 습 니 다.그 중에서 isActive,isComplete,isCanceled,그리고 일부 기초 작업 start(),cancel(),join()을 포함 하고 구체 적 인 전환 절 차 는 다음 과 같 습 니 다.

    우 리 는 먼저 job 를 만 드 는 것 부터 시작 합 니 다.launch 를 호출 할 때 기본적으로 세 개의 인자 가 있 습 니 다.
  • context:CorotineContext 의 대상 은 기본적으로 CorotineStart.DEFAULT 이 며,CorotineScope 의 context 와 접 습 니 다
  • start:CorotineStart 의 대상 은 기본적으로 CorotineStart.DEFAULT 이 고 대표 가 즉시 실행 하 는 동시에 CorotineStart.LAZY 도 있 습 니 다.대표 가 즉시 실행 되 지 않 으 면 job 의 start()를 호출해 야 실행 할 수 있 습 니 다
  • 
    val job2= lifecycleScope.launch(start = CoroutineStart.LAZY) {
     delay(2000)
     Log.i("scope test","lazy")
    }
    job2.start()
    
    이 모드 를 사용 하여 생 성 할 때 기본 값 은 new 상태 입 니 다.이때 isActive,isComplete,isCanceled 는 모두 false 입 니 다.start 를 호출 한 후에 active 상태 로 전환 합 니 다.그 중에서 isActive 만 true 이 고 작업 이 완료 되면 Completing 상태 에 들 어 갑 니 다.이 때 는 하위 job 가 완 료 될 때 까지 기다 리 는 상태 에서 isActive 만 true 입 니 다.모든 하위 job 도 완성 되면 Complete 상태 에 들 어가 고 isComplete 만 true 입 니 다.active 또는 Completing 상태 에서 취소 또는 이상 이 발생 하면 Cancelling 상태 에 들 어 갑 니 다.부모 job 와 다른 하위 job 를 취소 하려 면 취소 가 완 료 될 때 까지 기 다 립 니 다.이때 isCanceled 만 true 이 고 취소 가 완료 되면 Canceled 상태 에 들 어 갑 니 다.isCanceled 와 isComplete 는 모두 true 입 니 다.
    State
    isActive
    isCompleted
    isCancelled
    New
    FALSE
    FALSE
    FALSE
    Active
    TRUE
    FALSE
    FALSE
    Completing
    TRUE
    FALSE
    FALSE
    Cancelling
    FALSE
    FALSE
    TRUE
    Cancelled
    FALSE
    TRUE
    TRUE
    Completed
    FALSE
    TRUE
    FALSE
    서로 다른 job 상호작용 은 join()과 cancelAndJoin()을 사용 해 야 합 니 다.
    join():현재 job 를 다른 협동 작업 에 추가 합 니 다
  • cancel AndJoin():작업 을 취소 하고 추가 한 후에 취소 합 니 다
  • 
    val job1= GlobleScope.launch(start = CoroutineStart.LAZY) {
     delay(2000)
     Log.i("scope test","job1")
    }
    lifecycleScope.launch {
     job1.join()
     delay(2000)
     Log.i("scope test","job2")
    }
    
    suspend 깊이 이해
    suspend 는 kotlin 에 추 가 된 방법 수식어 로 서 최종 실현 은 자바 입 니 다.우 리 는 먼저 그들의 차이 점 을 봅 니 다.
    
    suspend fun test1(){}
    fun test2(){}
    
    대응 자바 코드
    
    public final Object test1(@NotNull Continuation $completion) {
     return Unit.INSTANCE;
    }
    public final void test2() {
    }
    
    대응 바이트 코드
    
    public final test1(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
     ...
     L0
     LINENUMBER 6 L0
     GETSTATIC kotlin/Unit.INSTANCE : Lkotlin/Unit;
     ARETURN
     L1
     LOCALVARIABLE this Lcom/lieni/android_c/ui/test/TestActivity; L0 L1 0
     LOCALVARIABLE $completion Lkotlin/coroutines/Continuation; L0 L1 1
     MAXSTACK = 1
     MAXLOCALS = 2
    
    public final test2()V
     L0
     LINENUMBER 9 L0
     RETURN
     L1
     LOCALVARIABLE this Lcom/lieni/android_c/ui/test/TestActivity; L0 L1 0
     MAXSTACK = 0
     MAXLOCALS = 1
    
    
    이 를 통 해 알 수 있 듯 이 suspend 를 추가 하 는 방법 은 사실 일반적인 방법 과 마찬가지 로 들 어 올 때 Continuation 대상 이 하나 더 생 겨 서 Unit.INSTANCE 대상 으로 돌 아 왔 을 뿐이다.
    
    public interface Continuation<in T> {
      public val context: CoroutineContext
      public fun resumeWith(result: Result<T>)
    }
    
    Continuation 의 구체 적 인 실현 은 BaseContinuation Impl 에서
    
    internal abstract class BaseContinuationImpl(...) : Continuation<Any?>, CoroutineStackFrame, Serializable {
      public final override fun resumeWith(result: Result<Any?>) {
        ...
        while (true) {
          ...
          with(current) {
           	val outcome = invokeSuspend(param)
            ...
            releaseIntercepted() 
            if (completion is BaseContinuationImpl) {
              ...
            } else {
              ...
              return
            }
          }
        }
      }
      ...
    }
    
    resume With 를 호출 할 때 invoke Suspend(param)와 releaseIntercepted()를 호출 하여 최상 위 completion 이 실 행 될 때 까지 순환 을 실행 하고 협 정 된 interceptor 를 방출 합 니 다.
    최종 석방 은 Continuation Impl 에서 이 루어 집 니 다.
    
    internal abstract class ContinuationImpl(...) : BaseContinuationImpl(completion) {
      ...
      protected override fun releaseIntercepted() {
        val intercepted = intercepted
        if (intercepted != null && intercepted !== this) {
          context[ContinuationInterceptor]!!.releaseInterceptedContinuation(intercepted)
        }
        this.intercepted = CompletedContinuation 
      }
    }
    
    이 를 통 해 알 수 있 듯 이 방출 은 최종 적 으로 CoroutineContext 에서 Continuation Interceptor 의 Element 를 통 해 이 루어 집 니 다.
    일시 정지 도 마찬가지다.
    
    public suspend inline fun <T> suspendCoroutine(crossinline block: (Continuation<T>) -> Unit): T =
      suspendCoroutineUninterceptedOrReturn { c: Continuation<T> ->
        val safe = SafeContinuation(c.intercepted())
        ...
      }
    
    기본적으로 Continuation 의 intercepted()방법 을 호출 합 니 다.
    
    internal abstract class ContinuationImpl(...) : BaseContinuationImpl(completion) {
      ...
      public fun intercepted(): Continuation<Any?> =intercepted
          ?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
            .also { intercepted = it }
    }
    
    일시 정지 가 최종 적 으로 CorotineContext 에서 Continuation Interceptor 를 위 한 Element 를 통 해 이 루어 지 는 것 을 볼 수 있 습 니 다.
    프로 세 스 요약(스 레 드 전환)
    새로운 Continuation 을 만 듭 니 다
  • coroutineScope 의 context 를 호출 하 는 Continuation Interceptor 의 intercept Continuation 방법 으로 부모 작업 을 중단 합 니 다
  • 하위 작업 수행(스 레 드 를 지정 하면 새 스 레 드 에서 실행 하고 Continuation 대상 에 전송)
  • 실행 이 끝 난 후 사용자 가 Continuation 의 resume 또는 resume With 를 호출 하여 결 과 를 되 돌려 줍 니 다
  • coroutineScope 의 context 를 호출 한 Continuation Interceptor 의 releaseIntercepted Continuation 방법 으로 부모 작업 을 복원 합 니 다
  • 차단 과 비 차단
    CoroutineScope 는 기본적으로 현재 스 레 드 를 막 지 않 습 니 다.차단 이 필요 하 다 면 runBlocking 을 사용 할 수 있 습 니 다.메 인 스 레 드 에서 아래 코드 를 실행 하면 2s 화이트 스크린 이 나타 납 니 다.
    
    runBlocking { 
      delay(2000)
      Log.i("scope test","runBlocking is completed")
    }
    
    차단 원리:runBlocking 을 실행 하면 기본적으로 BlockingCorotine 을 만 들 고 BlockingCorotine 에 서 는 현재 Job 이 isComplete 상태 일 때 까지 순환 을 실행 합 니 다.
    
    public fun <T> runBlocking(...): T {
      ...
      val coroutine = BlockingCoroutine<T>(newContext, currentThread, eventLoop)
      coroutine.start(CoroutineStart.DEFAULT, coroutine, block)
      return coroutine.joinBlocking()
    }
    
    
    private class BlockingCoroutine<T>(...) : AbstractCoroutine<T>(parentContext, true) {
      ...
      fun joinBlocking(): T {
       ...
       while (true) {
        ...
        if (isCompleted) break
        ...
       }  
       ...
      }
    }
    
    총결산
    여기 서 Kotlin 중 협 정 을 철저히 이해 하 는 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 Kotlin 협 정 내용 은 우리 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 조회 하 시기 바 랍 니 다.앞으로 많은 응원 바 랍 니 다!

    좋은 웹페이지 즐겨찾기