Kotlin의 코루틴 lifecycleScope 및 viewModelScope 소스 코드(2)

이전 기사 Kotlin의 코루틴 사용 (1)   Activity, Fragment 및 ViewModelScope는 기사 끝에 소개됩니다.

해당 자동 바인딩 수명 주기 코루틴 열기 메서드, lifecycleScope  viewModelScope

이 기사에서는 사용자가 바인딩을 취소할 필요 없이 이 두 제품이 라이프 사이클에 바인딩될 수 있는 이유는 무엇입니까?

==================================================== ========================

Activity 및 Fragment에 해당하는  lifecycleScope 

==================================================== ========================

먼저 활동을 시작하는 방법을 살펴보겠습니다.

class CoroutinesActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_coroutines)
        // 通过 lifecycleScope 开启
        lifecycleScope.launch(Dispatchers.IO) {
        }
    }
}

Fragment 열기 방법 다시보기

class CoroutinesFragment : Fragment() {
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // viewLifecycleOwner.lifecycleScope
        viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
        }
    }
}

각각의 lifecycleScopes를 클릭하여 살펴보세요. 실제로 모두 androidx.lifecycle:lifecycle-runtime-ktx 패키지 아래의 LifecycleOwner에 해당하는 lifecycleScopes입니다.

coroutineScope는 lifeCycle을 통해 획득한 것을 알 수 있는데 lifeCycle은 어떻게 coroutineScope를 획득하고 coroutineScope는 어떻게 life cycle을 묶는가?

/**
 * [CoroutineScope] tied to this [Lifecycle].
 *
 * This scope will be cancelled when the [Lifecycle] is destroyed.
 *
 * This scope is bound to
 * [Dispatchers.Main.immediate][kotlinx.coroutines.MainCoroutineDispatcher.immediate]
 */
public val Lifecycle.coroutineScope: LifecycleCoroutineScope
    get() {
        while (true) {
            //注释1 先判断是否已经有 coroutineScope ,有则直接返回
            val existing = mInternalScopeRef.get() as LifecycleCoroutineScopeImpl?
            if (existing != null) {
                return existing
            }
            //注释2 没有创建一个 coroutineScope 
            val newScope = LifecycleCoroutineScopeImpl(
                this,
                SupervisorJob() + Dispatchers.Main.immediate
            )
            //注释3 保存好 coroutineScope 
            if (mInternalScopeRef.compareAndSet(null, newScope)) {
                //注释4 注册生命周期回调,绑定生命周期
                newScope.register()
                return newScope
            }
        }
    }

//省略部分带码...

internal class LifecycleCoroutineScopeImpl(
    override val lifecycle: Lifecycle,
    override val coroutineContext: CoroutineContext
) : LifecycleCoroutineScope(), LifecycleEventObserver {
    init {
        // in case we are initialized on a non-main thread, make a best effort check before
        // we return the scope. This is not sync but if developer is launching on a non-main
        // dispatcher, they cannot be 100% sure anyways.
        if (lifecycle.currentState == Lifecycle.State.DESTROYED) {
            coroutineContext.cancel()
        }
    }

    fun register() {
        launch(Dispatchers.Main.immediate) {
            if (lifecycle.currentState >= Lifecycle.State.INITIALIZED) {
                //注释4 注册生命周期回调,绑定生命周期
                lifecycle.addObserver(this@LifecycleCoroutineScopeImpl)
            } else {
                coroutineContext.cancel()
            }
        }
    }

    override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
        if (lifecycle.currentState <= Lifecycle.State.DESTROYED) {
            //注释5 当生命周期是 destroy 时,取消生命周期回调监听,取消协程
            lifecycle.removeObserver(this)
            coroutineContext.cancel()
        }
    }
}

위의 소스 코드에 주석 1.2.3.4.5를 추가하는 것은 이미 명백하며 전체 프로세스는 다음과 같습니다.

1: Activity와 Fragment는 모두 LifecycleOwner를 통해 LifecycleScope를 통해 획득됩니다.

2: 이 coroutineScope는 LifecycleEventObserverCoroutineScope 인터페이스를 모두 구현하는 LifecycleCoroutineScopeImpl에 의해 캡슐화됩니다 .

3: 그래서 ( CoroutineScope 를 통해 ) 코루틴을 생성할 때, ( LifecycleEventObserver 를 통해 ) 수명 주기를 모니터링하고, 수명 주기가 파괴되도록 실행되면 모니터링을 취소하고 코루틴을 취소합니다.

==================================================== ========================

ViewModel에 해당하는  viewModelScope 

==================================================== ========================

ViewModel을 여는 방법을 살펴보겠습니다. viewModelScope는  ViewModel의 자체 속성 입니다 . 직접 호출하면 됩니다.

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch

class MyViewModel : ViewModel() {

    fun test() {
        // 开启协程
        viewModelScope.launch {

        }
    }
}

viewModelScope   구현 방법 보기

package androidx.lifecycle

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import java.io.Closeable
import kotlin.coroutines.CoroutineContext

private const val JOB_KEY = "androidx.lifecycle.ViewModelCoroutineScope.JOB_KEY"

/**
 * [CoroutineScope] tied to this [ViewModel].
 * This scope will be canceled when ViewModel will be cleared, i.e [ViewModel.onCleared] is called
 *
 * This scope is bound to
 * [Dispatchers.Main.immediate][kotlinx.coroutines.MainCoroutineDispatcher.immediate]
 */
val ViewModel.viewModelScope: CoroutineScope
        get() {
            /*
            *    注释1 每个ViewModel持有一个CoroutineScope
            *    初次获取创建并保存,保存通过Map 保存 
            *    再次获取 直接返回
            *    getTag() 和 setTagIfAbsent() 都是通过Map读写,有兴趣的可以进去细看
            */ 
            val scope: CoroutineScope? = this.getTag(JOB_KEY)
            if (scope != null) {
                return scope
            }
            return setTagIfAbsent(
                    JOB_KEY,
                    CloseableCoroutineScope(SupervisorJob() 
                    + Dispatchers.Main.immediate))
        }


/**
*注释 2
*CloseableCoroutineScope 实现 CoroutineScope 和 Closeable 
* 
*CoroutineScope 实现协程功能
*Closeable  实现关闭/取消协程功能
*
*/ 
internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope {
    override val coroutineContext: CoroutineContext = context

    override fun close() {
        //注释3: 取消协程
        coroutineContext.cancel()
    }
}

참고 1.2.3 참조....... ViewModel이 코루틴을 열고 저장하는 방법을 알고 있지만 참고 3은 호출될 때 코루틴을 취소합니다. 언제 호출해야 하는지 알고 싶다면 위의 참고 1을 입력해야 합니다. 무시된 두 메서드 getTag()setTagIfAbsent()는 mBagOfTags 라는 Map 에 해당합니다 .

 위의 두 메서드를 보면 close() 메서드가 언제 호출되는지 알 수 없습니다. 이 mBagOfTags가 언제 어디서  호출되는지 확인해야 합니다. 이 ViewModel.java에서 mBagOfTagsclear( ) 호출 방법

public abstract class ViewModel {
   
    @Nullable
    private final Map<String, Object> mBagOfTags = new HashMap<>();

    @MainThread
    final void clear() {
        mCleared = true;
        // 注释1 清除 mBagOfTags 里面所有元素
        if (mBagOfTags != null) {
            synchronized (mBagOfTags) {
                for (Object value : mBagOfTags.values()) {
                    // 注释2 close每一个元素
                    closeWithRuntimeException(value);
                }
            }
        }
        onCleared();
    }


    private static void closeWithRuntimeException(Object obj) {
        if (obj instanceof Closeable) {
            try {
                // 注释3 close每一个元素
                ((Closeable) obj).close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

참고 2와 3은 close()가 있는 곳입니다. 그런데 코루틴을 닫을 곳은 찾았는데 clear()를 언제 호출할까요?

clear()가 참조하는 위치를 살펴보겠습니다.

ViewModelStore의 clear()에 의해 호출되는 것을 알 수 있다.

Discovery는 Activity 및 Fragment에 의해 호출됩니다. 작성자가 소스 코드를 단계별로 찾을 때 Activity 및 Fragment destroy에서 각각 해당 메서드를 트리거하여 해당 ViewModel의 mBagOfTags가 보유한 데이터를 지우고 해제합니다.

이 부분은 이 글의 내용이 아니기 때문에 작성자가 코드 추적을 게시하지 않습니다. 관심있는 학생들은 살펴 볼 수 있습니다.

여기에서 나는 당신에게 상기시키고 싶습니다. 소스 코드를 볼 때 모퉁이를 돌지 말고 일반적인 논리적 사고를 보고 절반은 추측하고 절반은 읽으십시오. 코드의 모든 단계를 이해하지 마십시오. 소스 코드에 갇히지 않도록 합니다.

【요약하다】

 이를 다시 요약하면 ViewModel은 해당 ViewModelScope를 저장합니다. 이 ViewModel은 처음 획득할 때 mBagOfTags 맵에 저장되고 다시 획득할 때 이 mBagOfTags에서 제거됩니다. ViewModel 호스트 본체(Activity 또는 Fragment)가 소멸되면 Activity 또는 Fragment는 ViewModelStore를 통해 ViewModel의 mBagOfTags가 보유한 모든 데이터를 지우고 해제합니다. 물론 이때 코루틴이 해제 및 취소됩니다.

추천

출처blog.csdn.net/Leo_Liang_jie/article/details/120263726