kotlin的協(xié)程封裝了線程的API库物,這個線程框架可以讓我們很方便得編寫異步代碼。
雖然協(xié)程已經(jīng)很方便了侨核,但是如果再配合Google提供的架構(gòu)組件的KTX擴展一起使用,那就更方便了灌灾。
1. 添加KTX依賴
//將 Kotlin 協(xié)程與架構(gòu)組件一起使用
//ViewModelScope
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
//LifecycleScope
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0'
//liveData
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
2. viewModelScope
2.1 用老方式在ViewModel中使用協(xié)程
在使用ViewModelScope之前搓译,先來回顧一下以前在ViewModel中使用協(xié)程的方式。自己管理CoroutineScope紧卒,在不需要的時候(一般是在onCleared())進行取消侥衬。否則,可能造成資源浪費跑芳、內(nèi)存泄露等問題。
class JetpackCoroutineViewModel : ViewModel() {
//在這個ViewModel中使用協(xié)程時,需要使用這個job來方便控制取消
private val viewModelJob = SupervisorJob()
//指定協(xié)程在哪里執(zhí)行,并且可以由viewModelJob很方便地取消uiScope
private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
fun launchDataByOldWay() {
uiScope.launch {
//在后臺執(zhí)行
val result = getNetData()
//修改UI
log(result)
}
}
override fun onCleared() {
super.onCleared()
viewModelJob.cancel()
}
//將耗時任務(wù)切到IO線程去執(zhí)行
private suspend fun getNetData() = withContext(Dispatchers.IO) {
//模擬網(wǎng)絡(luò)耗時
delay(1000)
//模擬返回結(jié)果
"{}"
}
}
看起來有很多的樣板代碼直颅,而且在不需要的時候取消協(xié)程很容易忘博个。
2.2 新方式在ViewModel中使用協(xié)程
正是在這種情況下,Google為我們創(chuàng)造了ViewModelScope功偿,它通過向ViewModel類添加擴展屬性來方便我們使用協(xié)程盆佣,而且在ViewModel被銷毀時會自動取消其子協(xié)程。
class JetpackCoroutineViewModel : ViewModel() {
fun launchData() {
viewModelScope.launch {
//在后臺執(zhí)行
val result = getNetData()
//修改UI
log(result)
}
}
//將耗時任務(wù)切到IO線程去執(zhí)行
private suspend fun getNetData() = withContext(Dispatchers.IO) {
//模擬網(wǎng)絡(luò)耗時
delay(1000)
//模擬返回結(jié)果
"{}"
}
}
所有CoroutineScope的初始化和取消都已經(jīng)為我們完成了械荷,只需要在代碼里面使用viewModelScope
即可開啟一個新協(xié)程共耍,而且還不用擔心忘記取消的問題。
下面我們來看看Google是怎么實現(xiàn)的吨瞎。
2.3 viewModelScope的底層實現(xiàn)
點進去看看源碼痹兜,知根知底,萬一后面遇到什么奇怪的bug颤诀,在知道原理的情況下字旭,才能更快的想到解決辦法。
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]
*/
public val ViewModel.viewModelScope: CoroutineScope
get() {
//先從緩存中取值崖叫,有就直接返回
val scope: CoroutineScope? = this.getTag(JOB_KEY)
if (scope != null) {
return scope
}
//沒有緩存就新建一個CloseableCoroutineScope
return setTagIfAbsent(
JOB_KEY,
CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
)
}
internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope {
override val coroutineContext: CoroutineContext = context
override fun close() {
coroutineContext.cancel()
}
}
源碼中首先是介紹了viewModelScope是什么遗淳,它其實是一個ViewModel的擴展屬性,它的實際類型是CloseableCoroutineScope心傀。這名字看起來就是一個可以取消的協(xié)程屈暗,果不其然,它實現(xiàn)了Closeable并在close方法中進行了取消脂男。
每次在使用viewModelScope的時候养叛,會先從緩存中取,如果沒有才去新建一個CloseableCoroutineScope疆液。需要注意的是一铅,CloseableCoroutineScope的執(zhí)行是在主線程中執(zhí)行的。
我們現(xiàn)在需要知道的是緩存是怎么存儲和取出的堕油。
//ViewModel.java
// Can't use ConcurrentHashMap, because it can lose values on old apis (see b/37042460)
@Nullable
private final Map<String, Object> mBagOfTags = new HashMap<>();
/**
* Returns the tag associated with this viewmodel and the specified key.
*/
@SuppressWarnings({"TypeParameterUnusedInFormals", "unchecked"})
<T> T getTag(String key) {
if (mBagOfTags == null) {
return null;
}
synchronized (mBagOfTags) {
return (T) mBagOfTags.get(key);
}
}
/**
* Sets a tag associated with this viewmodel and a key.
* If the given {@code newValue} is {@link Closeable},
* it will be closed once {@link #clear()}.
* <p>
* If a value was already set for the given key, this calls do nothing and
* returns currently associated value, the given {@code newValue} would be ignored
* <p>
* If the ViewModel was already cleared then close() would be called on the returned object if
* it implements {@link Closeable}. The same object may receive multiple close calls, so method
* should be idempotent.
*/
@SuppressWarnings("unchecked")
<T> T setTagIfAbsent(String key, T newValue) {
T previous;
synchronized (mBagOfTags) {
previous = (T) mBagOfTags.get(key);
if (previous == null) {
mBagOfTags.put(key, newValue);
}
}
T result = previous == null ? newValue : previous;
if (mCleared) {
// It is possible that we'll call close() multiple times on the same object, but
// Closeable interface requires close method to be idempotent:
// "if the stream is already closed then invoking this method has no effect." (c)
closeWithRuntimeException(result);
}
return result;
}
現(xiàn)在我們知道了潘飘,原來是存在了ViewModel的mBagOfTags中肮之,它是一個HashMap。
知道了怎么存的卜录,那么它是在什么時候用的呢戈擒?
@MainThread
final void clear() {
mCleared = true;
// Since clear() is final, this method is still called on mock objects
// and in those cases, mBagOfTags is null. It'll always be empty though
// because setTagIfAbsent and getTag are not final so we can skip
// clearing it
if (mBagOfTags != null) {
synchronized (mBagOfTags) {
for (Object value : mBagOfTags.values()) {
// see comment for the similar call in setTagIfAbsent
closeWithRuntimeException(value);
}
}
}
onCleared();
}
private static void closeWithRuntimeException(Object obj) {
if (obj instanceof Closeable) {
try {
((Closeable) obj).close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
我在ViewModel中搜索了一下mBagOfTags,發(fā)現(xiàn)有一個clear方法艰毒,在里面將mBagOfTags遍歷一遍筐高,然后將所有value是Closeable的全部close。在上面的源碼中丑瞧,第一次使用viewModelScope的時候柑土,會創(chuàng)建一個CloseableCoroutineScope,它實現(xiàn)了Closeable接口绊汹,并實現(xiàn)了close方法稽屏,剛好用來做取消操作。
看到這里西乖,我們知道了:viewModelScope構(gòu)建的協(xié)程是在ViewModel的clear方法回調(diào)時取消協(xié)程的狐榔。
而且,clear方法里面居然還有我們熟悉的onCleared方法調(diào)用获雕。而onCleared我們知道是干什么的薄腻,當這個ViewModel不再使用時會回調(diào)這個方法,一般我們需要在此方法中做一些收尾工作届案,如取消觀察者訂閱庵楷、關(guān)閉資源之類的。
那么萝玷,大膽猜測一下嫁乘,這個clear()方法應(yīng)該也是在ViewModel要結(jié)束生命的時候調(diào)用的。
搜索了一下球碉,發(fā)現(xiàn)clear方法是在ViewModelStore里面調(diào)用的蜓斧。
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.put(key, viewModel);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
}
final ViewModel get(String key) {
return mMap.get(key);
}
Set<String> keys() {
return new HashSet<>(mMap.keySet());
}
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
ViewModelStore是一個容器,用于盛放ViewModel睁冬。在ViewModelStore的clear方法中調(diào)用了該ViewModelStore中所有ViewModel的clear方法挎春。那么ViewModelStore的clear是在哪里調(diào)用的?我跟著追蹤豆拨,發(fā)現(xiàn)是在ComponentActivity的構(gòu)造方法中直奋。
public ComponentActivity() {
Lifecycle lifecycle = getLifecycle();
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
if (!isChangingConfigurations()) {
getViewModelStore().clear();
}
}
}
});
}
在Activity的生命周期走到onDestroy的時候調(diào)用ViewModelStore的clear做收尾工作。但是施禾,注意一下脚线,這個調(diào)用有個前提,此次走onDestroy不是因為配置更改才會去調(diào)用clear方法弥搞。
好的邮绿,到此為止渠旁,咱們理通了viewModelScope的協(xié)程是怎么做到自動取消的(ViewModel的mBagOfTags),以及是在什么時候進行取消的(ViewModel的clear()時)船逮。
3. lifecycleScope
對于Lifecycle顾腊,Google貼心地提供了LifecycleScope,我們可以直接通過launch來創(chuàng)建Coroutine挖胃。
3.1 使用
舉個簡單例子杂靶,比如在Activity的onCreate里面,每隔100毫秒更新一下TextView的文字。
lifecycleScope.launch {
repeat(100000) {
delay(100)
tvText.text = "$it"
}
}
因為LifeCycle是可以感知組件的生命周期的酱鸭,所以Activity一旦onDestroy了吗垮,相應(yīng)的上面這個lifecycleScope。launch閉包的調(diào)用也會取消凹髓。
另外抱既,lifecycleScope還貼心地提供了launchWhenCreated、launchWhenStarted扁誓、launchWhenResumed方法,這些方法的閉包里面有協(xié)程的作用域蝗敢,它們分別是在CREATED、STARTED足删、RESUMED時被執(zhí)行寿谴。
//方式1
lifecycleScope.launchWhenStarted {
repeat(100000) {
delay(100)
tvText.text = "$it"
}
}
//方式2
lifecycleScope.launch {
whenStarted {
repeat(100000) {
delay(100)
tvText.text = "$it"
}
}
}
不管是直接調(diào)用launchWhenStarted還是在launch中調(diào)用whenStarted都能達到同樣的效果。
3.2 LifecycleScope的底層實現(xiàn)
先來看下lifecycleScope.launch是怎么做到的
/**
* [CoroutineScope] tied to this [LifecycleOwner]'s [Lifecycle].
*
* This scope will be cancelled when the [Lifecycle] is destroyed.
*
* This scope is bound to
* [Dispatchers.Main.immediate][kotlinx.coroutines.MainCoroutineDispatcher.immediate].
*/
val LifecycleOwner.lifecycleScope: LifecycleCoroutineScope
get() = lifecycle.coroutineScope
好家伙失受,又是擴展屬性讶泰。這次擴展的是LifecycleOwner,返回了一個LifecycleCoroutineScope拂到。每次在get的時候痪署,是返回的lifecycle.coroutineScope,看看這個是啥兄旬。
/**
* [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]
*/
val Lifecycle.coroutineScope: LifecycleCoroutineScope
get() {
while (true) {
val existing = mInternalScopeRef.get() as LifecycleCoroutineScopeImpl?
if (existing != null) {
return existing
}
val newScope = LifecycleCoroutineScopeImpl(
this,
SupervisorJob() + Dispatchers.Main.immediate
)
if (mInternalScopeRef.compareAndSet(null, newScope)) {
newScope.register()
return newScope
}
}
}
Lifecycle的coroutineScope也是擴展屬性狼犯,它是一個LifecycleCoroutineScope。從注釋可以看到领铐,在Lifecycle被銷毀之后悯森,這個協(xié)程會跟著取消。這里首先會從mInternalScopeRef中取之前存入的緩存绪撵,如果沒有再生成一個LifecycleCoroutineScopeImpl放進去瓢姻,并調(diào)用LifecycleCoroutineScopeImpl的register函數(shù)。這里的mInternalScopeRef是Lifecycle類里面的一個屬性: AtomicReference<Object> mInternalScopeRef = new AtomicReference<>();
(AtomicReference可以讓一個對象保證原子性)音诈。這里使用AtomicReference當然是為了線程安全幻碱。
既然生成的是LifecycleCoroutineScopeImpl绎狭,那么就先來看看這個東西是什么
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() {
//啟了個協(xié)程,當前Lifecycle的state大于等于INITIALIZED收班,就注冊一下Lifecycle的觀察者坟岔,觀察生命周期
launch(Dispatchers.Main.immediate) {
if (lifecycle.currentState >= Lifecycle.State.INITIALIZED) {
lifecycle.addObserver(this@LifecycleCoroutineScopeImpl)
} else {
coroutineContext.cancel()
}
}
}
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
//觀察到當前生命周期小于等于DESTROYED,那么就移除當前這個觀察者并且取消協(xié)程
if (lifecycle.currentState <= Lifecycle.State.DESTROYED) {
lifecycle.removeObserver(this)
coroutineContext.cancel()
}
}
}
在上面的代碼中摔桦,有2個重要的函數(shù):register和onStateChanged社付。register函數(shù)是在初始化LifecycleCoroutineScopeImpl的時候調(diào)用的,先在register函數(shù)中添加一個觀察者用于觀察生命周期的變化邻耕,然后在onStateChanged函數(shù)中判斷生命周期到DESTROYED時就移除觀察者并且取消協(xié)程鸥咖。
有個小細節(jié),為啥register函數(shù)中能直接啟協(xié)程兄世?是因為LifecycleCoroutineScopeImpl繼承了LifecycleCoroutineScope,啼辣,而LifecycleCoroutineScope實現(xiàn)了CoroutineScope接口(其實是在LifecycleCoroutineScopeImpl中實現(xiàn)的)。
public abstract class LifecycleCoroutineScope internal constructor() : CoroutineScope {
internal abstract val lifecycle: Lifecycle
......
}
現(xiàn)在我們流程理清楚了御滩,lifecycleScope使用時會構(gòu)建一個協(xié)程鸥拧,同時會觀察組件的生命周期,在適當?shù)臅r機(DESTROYED)取消協(xié)程削解。
在上面的實例我們見過一段代碼:
//方式1
lifecycleScope.launchWhenStarted {
repeat(100000) {
delay(100)
tvText.text = "$it"
}
}
//方式2
lifecycleScope.launch {
whenStarted {
repeat(100000) {
delay(100)
tvText.text = "$it"
}
}
}
可以直接通過lifecycleScope提供的launchWhenCreated富弦、launchWhenStarted、launchWhenResumed在相應(yīng)的生命周期時執(zhí)行協(xié)程氛驮。
點進去看一下
abstract class LifecycleCoroutineScope internal constructor() : CoroutineScope {
internal abstract val lifecycle: Lifecycle
/**
* Launches and runs the given block when the [Lifecycle] controlling this
* [LifecycleCoroutineScope] is at least in [Lifecycle.State.CREATED] state.
*
* The returned [Job] will be cancelled when the [Lifecycle] is destroyed.
* @see Lifecycle.whenCreated
* @see Lifecycle.coroutineScope
*/
fun launchWhenCreated(block: suspend CoroutineScope.() -> Unit): Job = launch {
lifecycle.whenCreated(block)
}
/**
* Launches and runs the given block when the [Lifecycle] controlling this
* [LifecycleCoroutineScope] is at least in [Lifecycle.State.STARTED] state.
*
* The returned [Job] will be cancelled when the [Lifecycle] is destroyed.
* @see Lifecycle.whenStarted
* @see Lifecycle.coroutineScope
*/
fun launchWhenStarted(block: suspend CoroutineScope.() -> Unit): Job = launch {
lifecycle.whenStarted(block)
}
/**
* Launches and runs the given block when the [Lifecycle] controlling this
* [LifecycleCoroutineScope] is at least in [Lifecycle.State.RESUMED] state.
*
* The returned [Job] will be cancelled when the [Lifecycle] is destroyed.
* @see Lifecycle.whenResumed
* @see Lifecycle.coroutineScope
*/
fun launchWhenResumed(block: suspend CoroutineScope.() -> Unit): Job = launch {
lifecycle.whenResumed(block)
}
}
原來這些函數(shù)就是LifecycleOwner的擴展屬性lifecycleScope所返回的LifecycleCoroutineScope類里面的函數(shù)腕柜。這幾個函數(shù)里面啥也沒干,直接調(diào)用了lifecycle對應(yīng)的函數(shù)
/**
* Runs the given block when the [Lifecycle] is at least in [Lifecycle.State.CREATED] state.
*
* @see Lifecycle.whenStateAtLeast for details
*/
suspend fun <T> Lifecycle.whenCreated(block: suspend CoroutineScope.() -> T): T {
return whenStateAtLeast(Lifecycle.State.CREATED, block)
}
/**
* Runs the given block when the [Lifecycle] is at least in [Lifecycle.State.STARTED] state.
*
* @see Lifecycle.whenStateAtLeast for details
*/
suspend fun <T> Lifecycle.whenStarted(block: suspend CoroutineScope.() -> T): T {
return whenStateAtLeast(Lifecycle.State.STARTED, block)
}
/**
* Runs the given block when the [Lifecycle] is at least in [Lifecycle.State.RESUMED] state.
*
* @see Lifecycle.whenStateAtLeast for details
*/
suspend fun <T> Lifecycle.whenResumed(block: suspend CoroutineScope.() -> T): T {
return whenStateAtLeast(Lifecycle.State.RESUMED, block)
}
這幾個函數(shù)原來是suspend函數(shù)矫废,并且是擴展Lifecycle的函數(shù)盏缤。它們最終都調(diào)用到了whenStateAtLeast函數(shù),并傳入了執(zhí)行協(xié)程的最小的生命周期狀態(tài)標志(minState)蓖扑。
suspend fun <T> Lifecycle.whenStateAtLeast(
minState: Lifecycle.State,
block: suspend CoroutineScope.() -> T
) = withContext(Dispatchers.Main.immediate) {
val job = coroutineContext[Job] ?: error("when[State] methods should have a parent job")
val dispatcher = PausingDispatcher()
val controller =
LifecycleController(this@whenStateAtLeast, minState, dispatcher.dispatchQueue, job)
try {
//執(zhí)行協(xié)程
withContext(dispatcher, block)
} finally {
//收尾工作 移除生命周期觀察
controller.finish()
}
}
@MainThread
internal class LifecycleController(
private val lifecycle: Lifecycle,
private val minState: Lifecycle.State,
private val dispatchQueue: DispatchQueue,
parentJob: Job
) {
private val observer = LifecycleEventObserver { source, _ ->
if (source.lifecycle.currentState == Lifecycle.State.DESTROYED) {
//DESTROYED->取消協(xié)程
handleDestroy(parentJob)
} else if (source.lifecycle.currentState < minState) {
dispatchQueue.pause()
} else {
//執(zhí)行
dispatchQueue.resume()
}
}
init {
// If Lifecycle is already destroyed (e.g. developer leaked the lifecycle), we won't get
// an event callback so we need to check for it before registering
// see: b/128749497 for details.
if (lifecycle.currentState == Lifecycle.State.DESTROYED) {
handleDestroy(parentJob)
} else {
//觀察生命周期變化
lifecycle.addObserver(observer)
}
}
@Suppress("NOTHING_TO_INLINE") // avoid unnecessary method
private inline fun handleDestroy(parentJob: Job) {
parentJob.cancel()
finish()
}
/**
* Removes the observer and also marks the [DispatchQueue] as finished so that any remaining
* runnables can be executed.
*/
@MainThread
fun finish() {
//移除生命周期觀察者
lifecycle.removeObserver(observer)
//標記已完成 并執(zhí)行剩下的可執(zhí)行的Runnable
dispatchQueue.finish()
}
}
whenStateAtLeast也是一個Lifecycle的擴展函數(shù)唉铜,核心邏輯是在LifecycleController中添加了LifecycleObserver來監(jiān)聽生命周期狀態(tài),通過狀態(tài)來決定是暫停執(zhí)行還是恢復執(zhí)行赵誓,或者是取消執(zhí)行打毛。當執(zhí)行完成之后,也就是finally那里俩功,從執(zhí)行LifecycleController的finish進行收尾工作:移除生命周期監(jiān)聽幻枉,開始執(zhí)行余下的任務(wù)。
執(zhí)行完成一次诡蜓,就會移除生命周期觀察者熬甫,相當于我們寫到launchWhenResumed之類的函數(shù)里面的閉包只會被執(zhí)行一次。執(zhí)行完成之后蔓罚,即使再經(jīng)過onPause->onResume也不會再次執(zhí)行椿肩。
4. liveData
在我們平時使用LiveData的過程中瞻颂,可能會涉及到這種場景:去請求網(wǎng)絡(luò)拿結(jié)果,然后通過LiveData將數(shù)據(jù)轉(zhuǎn)出去郑象,在Activity里面收到通知倒谷,然后更新UI强经。非常常見的場景变姨,這種情況下纽匙,我們可以通過官方的liveData構(gòu)造器函數(shù)來簡化上面的場景代碼。
4.1 使用
val netData: LiveData<String> = liveData {
//觀察的時候在生命周期內(nèi),則會馬上執(zhí)行
val data = getNetData()
emit(data)
}
//將耗時任務(wù)切到IO線程去執(zhí)行
private suspend fun getNetData() = withContext(Dispatchers.IO) {
//模擬網(wǎng)絡(luò)耗時
delay(5000)
//模擬返回結(jié)果
"{}"
}
在上面的例子中击奶,getNetData()是一個suspend函數(shù)辈双。使用LiveData構(gòu)造器函數(shù)異步調(diào)用getNetData(),然后使用emit()提交結(jié)果柜砾。在Activity那邊如果觀察了這個netData湃望,并且處于活動狀態(tài),那么就會收到結(jié)果痰驱。我們知道证芭,suspend函數(shù)需要在協(xié)程作用域中調(diào)用,所以liveData的閉包里面也是有協(xié)程作用域的担映。
有個小細節(jié)檩帐,如果組件在觀察此netData時剛好處于活動狀態(tài),那么liveData閉包里面的代碼會立刻執(zhí)行另萤。
除了上面這種用法,還可以在liveData里面發(fā)出多個值诅挑。
val netData2: LiveData<String> = liveData {
delay(3000)
val source = MutableLiveData<String>().apply {
value = "11111"
}
val disposableHandle = emitSource(source)
delay(3000)
disposableHandle.dispose()
val source2 = MutableLiveData<String>().apply {
value = "22222"
}
val disposableHandle2 = emitSource(source2)
}
需要注意的是四敞,后一個調(diào)用emitSource的時候需要把前一個emitSource的返回值調(diào)用一下dispose函數(shù),切斷拔妥。
4.2 liveData的底層實現(xiàn)
老規(guī)矩忿危,Ctrl+鼠標左鍵 點進去看源碼
@UseExperimental(ExperimentalTypeInference::class)
fun <T> liveData(
context: CoroutineContext = EmptyCoroutineContext,
timeoutInMs: Long = DEFAULT_TIMEOUT,
@BuilderInference block: suspend LiveDataScope<T>.() -> Unit
): LiveData<T> = CoroutineLiveData(context, timeoutInMs, block)
//咱們在liveData后面的閉包里面寫的代碼就是傳給了這里的block,它是一個suspend函數(shù)没龙,有LiveDataScope的上下文
首先铺厨,映入眼簾的是liveData函數(shù)居然是一個全局函數(shù),這意味著你可以在任何地方使用它硬纤,而不局限于Activity或者ViewModel里面解滓。
其次,liveData函數(shù)返回的是一個CoroutineLiveData對象筝家?居然返回的是一個對象洼裤,沒有在這里執(zhí)行任何代碼。那我的代碼是在哪里執(zhí)行的溪王?
這就得看CoroutineLiveData類的代碼了
internal class CoroutineLiveData<T>(
context: CoroutineContext = EmptyCoroutineContext,
timeoutInMs: Long = DEFAULT_TIMEOUT,
block: Block<T>
) : MediatorLiveData<T>() {
private var blockRunner: BlockRunner<T>?
private var emittedSource: EmittedSource? = null
init {
// use an intermediate supervisor job so that if we cancel individual block runs due to losing
// observers, it won't cancel the given context as we only cancel w/ the intention of possibly
// relaunching using the same parent context.
val supervisorJob = SupervisorJob(context[Job])
// The scope for this LiveData where we launch every block Job.
// We default to Main dispatcher but developer can override it.
// The supervisor job is added last to isolate block runs.
val scope = CoroutineScope(Dispatchers.Main.immediate + context + supervisorJob)
blockRunner = BlockRunner(
liveData = this,
block = block,
timeoutInMs = timeoutInMs,
scope = scope
) {
blockRunner = null
}
}
internal suspend fun emitSource(source: LiveData<T>): DisposableHandle {
clearSource()
val newSource = addDisposableSource(source)
emittedSource = newSource
return newSource
}
internal suspend fun clearSource() {
emittedSource?.disposeNow()
emittedSource = null
}
override fun onActive() {
super.onActive()
blockRunner?.maybeRun()
}
override fun onInactive() {
super.onInactive()
blockRunner?.cancel()
}
}
里面代碼比較少腮鞍,主要就是繼承了MediatorLiveData值骇,然后在onActive的時候執(zhí)行BlockRunner的maybeRun函數(shù)。BlockRunner的maybeRun里面執(zhí)行的實際上就是我們在liveData里面寫的代碼塊移国,而onActive方法實際上是從LiveData那里繼承過來的吱瘩,當有一個處于活躍狀態(tài)的觀察者監(jiān)聽LiveData時會被調(diào)用。
這就解釋得通了迹缀,我上面的案例中是在Activity的onCreate(處于活躍狀態(tài))里面觀察了netData使碾,所以liveData里面的代碼會被立刻執(zhí)行。
//typealias 類型別名
//在下面的BlockRunner中會使用到這個裹芝,這個東西用于承載我們在liveData后面閉包里面的代碼
internal typealias Block<T> = suspend LiveDataScope<T>.() -> Unit
/**
* Handles running a block at most once to completion.
*/
internal class BlockRunner<T>(
private val liveData: CoroutineLiveData<T>,
private val block: Block<T>,
private val timeoutInMs: Long,
private val scope: CoroutineScope,
private val onDone: () -> Unit
) {
@MainThread
fun maybeRun() {
...
//這里的scope是CoroutineScope(Dispatchers.Main.immediate + context + supervisorJob)
runningJob = scope.launch {
val liveDataScope = LiveDataScopeImpl(liveData, coroutineContext)
//這里的block執(zhí)行的是我們在liveData里面寫的代碼部逮,在執(zhí)行block時將liveDataScope實例傳入,liveDataScope上下文有了
block(liveDataScope)
//完成
onDone()
}
}
...
}
internal class LiveDataScopeImpl<T>(
internal var target: CoroutineLiveData<T>,
context: CoroutineContext
) : LiveDataScope<T> {
...
// use `liveData` provided context + main dispatcher to communicate with the target
// LiveData. This gives us main thread safety as well as cancellation cooperation
private val coroutineContext = context + Dispatchers.Main.immediate
//因為在執(zhí)行l(wèi)iveData閉包的時候有LiveDataScopeImpl上下文嫂易,所以可以使用emit函數(shù)
override suspend fun emit(value: T) = withContext(coroutineContext) {
target.clearSource()
//給target這個LiveData設(shè)置value兄朋,而liveData返回的就是這個target,在組件中觀察此target怜械,就會收到這里的value數(shù)據(jù)
target.value = value
}
}
在BlockRunner的maybeRun函數(shù)的里面啟了個協(xié)程颅和,這個scope是在CoroutineLiveData里面初始化的:CoroutineScope(Dispatchers.Main.immediate + context + supervisorJob)
,接著就是在這個scope里面執(zhí)行我們在liveData后面閉包里面寫的代碼缕允,并且有LiveDataScopeImpl的上下文峡扩。有了LiveDataScopeImpl的上下文,那么我們就可以使用LiveDataScopeImpl里面的emit方法障本,而emit方法其實很簡單教届,就是將一個數(shù)據(jù)交給一個LiveData對象,而這個LiveData就是liveData{}
返回的那個驾霜。此時因為LiveData的數(shù)據(jù)發(fā)生了變化案训,如果有組件觀察了該LiveData并且該組件處于活動狀態(tài),那么該組件就會收到數(shù)據(jù)發(fā)生變化的回調(diào)粪糙。
整個過程大致流程就是在liveData{}
里面構(gòu)建了一個協(xié)程强霎,返回了一個LiveData,然后我們寫的閉包里面的代碼實際上是在一個協(xié)程里面執(zhí)行的蓉冈,我們調(diào)用emit方法時就是在更新這個LiveData里面的value城舞。既然是返回的LiveData,那么天然就與組件生命周期關(guān)聯(lián)上了寞酿,只有在組件處于活動狀態(tài)才能得到結(jié)果家夺,而且也避免了一些內(nèi)存泄露的問題。
5. 小結(jié)
不得不說伐弹,官方提供的東西就是方便秦踪,可以極大地方便我們使用協(xié)程。在使用協(xié)程且還沒有與架構(gòu)組件一起使用的小伙伴兒趕快用起來。美滋滋~