이전 기사 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는 LifecycleEventObserver 및 CoroutineScope 인터페이스를 모두 구현하는 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에서 mBagOfTags 가 clear( ) 호출 방법
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가 보유한 모든 데이터를 지우고 해제합니다. 물론 이때 코루틴이 해제 및 취소됩니다.