協(xié)程內(nèi)存泄露-ViewModel
ViewModel KTX 中提供了 viewModelScope
,目的是為了減少協(xié)程內(nèi)存泄露。
如何使用
將 GlobalScope
替換為 viewModelScope
即可。
viewModelScope.launch(Dispatchers.Main) {
showLoadingLiveData.postValue(true)
//將文件轉(zhuǎn)化為ByteString
Logz.d("獲取ByteString")
val uploadBuffer = getUploadBuffer(uploadTempFile)
//......
uploadFile(uploadBuffer)
}
工作原理
常規(guī)情況使用協(xié)程需要手動(dòng)去停止對(duì)應(yīng)的協(xié)程,如果沒有正確的調(diào)用則會(huì)出現(xiàn)內(nèi)存泄露問題兽埃,而 ViewModel KTX 提供的viewModelScope
則自動(dòng)幫我們做了這件事柄错。
viewModelScope 源碼
val ViewModel.viewModelScope: CoroutineScope
get() {
val scope: CoroutineScope? = this.getTag(JOB_KEY)
if (scope != null) {
return scope
}
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()
}
}
如何取消協(xié)程
從源碼可以看到疫萤,內(nèi)部提供了一個(gè) CloseableCoroutineScope
毫捣,并且調(diào)用它的 close 方法即可將協(xié)程cancel蔓同。
那么關(guān)鍵我們需要關(guān)注什么時(shí)候調(diào)用這個(gè) close 方法。
如何做到主動(dòng)取消则北?
我們知道 ViewModel 當(dāng)被清除時(shí)會(huì)回調(diào) onClear() 方法,我們從這個(gè)方法中去找對(duì)應(yīng)取消協(xié)程相關(guān)的操作快骗。
下面是 ViewModel 的兩個(gè)方法的源碼名秀。onClear()是在 clear() 中調(diào)用的匕得,并且會(huì)調(diào)用 closeWithRuntimeException()
,在這里可以看到它會(huì)檢測(cè)當(dāng)前是 Closeable 類型的對(duì)應(yīng)則會(huì)主動(dòng)調(diào)用 close()考阱,回到上面提到的 CloseableCoroutineScope
這個(gè)類就實(shí)現(xiàn)了 Closeable
接口负间,因此我們就找到了主動(dòng)取消協(xié)程的地方了趾访。
//ViewModel.kt
@SuppressWarnings("WeakerAccess")
protected void onCleared() {
}
@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);
}
}
}
參考文檔
不用擔(dān)心引入庫(kù)導(dǎo)致包體積增大云头,這些擴(kuò)展庫(kù)的體積都很小
本文是筆者學(xué)習(xí)之后的總結(jié)科吭,方便日后查看學(xué)習(xí)谣殊,有任何不對(duì)的地方請(qǐng)指正。
記錄于 2020年6月19號(hào)