Skip to content

Commit

Permalink
Add better instantiation and synchronization of scope
Browse files Browse the repository at this point in the history
  • Loading branch information
dcvz committed Apr 19, 2022
1 parent ef89a28 commit 4bb4c72
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 28 deletions.
5 changes: 3 additions & 2 deletions api/multiplatform-viewmodel.api
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ public final class com/doublesymmetry/viewmodel/BuildConfig {
public fun <init> ()V
}

public class com/doublesymmetry/viewmodel/ViewModel : androidx/lifecycle/ViewModel {
public abstract class com/doublesymmetry/viewmodel/ViewModel : androidx/lifecycle/ViewModel {
public fun <init> ()V
public final fun getScope ()Lkotlinx/coroutines/CoroutineScope;
public final fun getViewModelScope ()Lkotlinx/coroutines/CoroutineScope;
protected fun onCleared ()V
}

Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ package com.doublesymmetry.viewmodel
import kotlinx.coroutines.CoroutineScope

expect abstract class ViewModel() {
/**
* [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]
*/
val viewModelScope: CoroutineScope

/**
Expand Down
42 changes: 16 additions & 26 deletions src/darwinMain/kotlin/com/doublesymmetry/viewmodel/ViewModel.kt
Original file line number Diff line number Diff line change
@@ -1,39 +1,29 @@
package com.doublesymmetry.viewmodel

import kotlinx.atomicfu.locks.reentrantLock
import kotlinx.atomicfu.locks.withLock
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.*

actual abstract class ViewModel {
private val lock = reentrantLock()
private var backingScope: CoroutineScope? = null
private var hasCleared = false

actual val viewModelScope: CoroutineScope
get() {
return this.getScopeInstance() ?: this.createScopeInstance()
}

protected actual open fun onCleared() {}
actual val viewModelScope: CoroutineScope by lazy {
val result = CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)

fun clear() {
lock.withLock {
closeWithRuntimeException(backingScope)
backingScope = null
}
if (hasCleared)
closeWithRuntimeException(result)

onCleared()
return@lazy result
}

private fun getScopeInstance(): CoroutineScope? {
lock.withLock { return backingScope }
}
protected actual open fun onCleared() {}

private fun createScopeInstance(): CoroutineScope {
val scope = CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
backingScope = scope
return scope
/**
* Closes the [viewModelScope] and cancels all its coroutines.
* Should be called from main thread.
*/
fun clear() {
hasCleared = true
closeWithRuntimeException(viewModelScope)
onCleared()
}

companion object {
Expand Down

0 comments on commit 4bb4c72

Please sign in to comment.