Last active
October 9, 2021 13:17
-
-
Save MRezaNasirloo/883f3d5767891b39eb6a576e1b2f9441 to your computer and use it in GitHub Desktop.
Share ViewModels across LifecycleOwners
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.mrezanasirloo.ganjeh | |
import android.util.SparseArray | |
import androidx.activity.ComponentActivity | |
import androidx.annotation.MainThread | |
import androidx.fragment.app.Fragment | |
import androidx.fragment.app.createViewModelLazy | |
import androidx.lifecycle.LifecycleOwner | |
import androidx.lifecycle.ViewModel | |
import androidx.lifecycle.ViewModelLazy | |
import androidx.lifecycle.ViewModelProvider | |
import androidx.lifecycle.ViewModelStore | |
/** | |
* @author: [email protected] | |
* | |
* Ganjeh lets you share a [ViewModel] across multiple [LifecycleOwner]s and | |
* destroy the [ViewModel] when the last owner is destroyed | |
*/ | |
object Ganjeh { | |
private val stores = SparseArray<GanjehViewModelStore>() | |
fun get(graphId: String, owner: LifecycleOwner): ViewModelStore { | |
val key = graphId.hashCode() | |
return (stores[key] ?: put(graphId, GanjehViewModelStore { | |
stores.delete(key) | |
})).apply { | |
add(owner, owner.javaClass.canonicalName.toString() + "_" + graphId) | |
} | |
} | |
private fun put(graphId: String, store: GanjehViewModelStore): GanjehViewModelStore { | |
return store.apply { stores.put(graphId.hashCode(), this) } | |
} | |
} | |
@MainThread | |
inline fun <reified VM : ViewModel> Fragment.ganjehViewModels( | |
graphId: String, | |
noinline factoryProducer: (() -> ViewModelProvider.Factory)? = null | |
): Lazy<VM> { | |
return createViewModelLazy( | |
VM::class, | |
{ Ganjeh.get(graphId, this) }, | |
factoryProducer | |
) | |
} | |
@MainThread | |
inline fun <reified VM : ViewModel> Fragment.ganjehViewModels( | |
noinline graphIdProvider: () -> String, | |
noinline factoryProducer: (() -> ViewModelProvider.Factory)? = null | |
): Lazy<VM> { | |
return createViewModelLazy( | |
VM::class, | |
{ Ganjeh.get(graphIdProvider(), this) }, | |
factoryProducer | |
) | |
} | |
@MainThread | |
inline fun <reified VM : ViewModel> Fragment.ganjehViewModels( | |
noinline factoryProducer: (() -> ViewModelProvider.Factory)? = null | |
): Lazy<VM> { | |
return createViewModelLazy( | |
VM::class, | |
{ Ganjeh.get(VM::class.java.canonicalName.toString(), this) }, | |
factoryProducer | |
) | |
} | |
@MainThread | |
inline fun <reified VM : ViewModel> ComponentActivity.ganjehViewModels( | |
noinline graphIdProvider: () -> String = { VM::class.java.canonicalName.toString() }, | |
noinline factoryProducer: (() -> ViewModelProvider.Factory)? = null | |
): Lazy<VM> { | |
val factoryPromise = factoryProducer ?: { | |
val application = application ?: throw IllegalArgumentException( | |
"ViewModel can be accessed only when Activity is attached" | |
) | |
ViewModelProvider.AndroidViewModelFactory.getInstance(application) | |
} | |
return ViewModelLazy(VM::class, { Ganjeh.get(graphIdProvider(), this) }, factoryPromise) | |
} | |
@MainThread | |
inline fun <reified VM : ViewModel> ComponentActivity.ganjehViewModels( | |
graphId: String = VM::class.java.canonicalName.toString(), | |
noinline factoryProducer: (() -> ViewModelProvider.Factory)? = null | |
): Lazy<VM> { | |
return ganjehViewModels({ graphId }, factoryProducer) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.mrezanasirloo.ganjeh | |
import android.app.Activity | |
import androidx.fragment.app.Fragment | |
import androidx.lifecycle.Lifecycle | |
import androidx.lifecycle.LifecycleEventObserver | |
import androidx.lifecycle.LifecycleOwner | |
import androidx.lifecycle.ViewModel | |
import androidx.lifecycle.ViewModelStore | |
/** | |
* @author: [email protected] | |
* | |
* [GanjehViewModelStore] keeps a [ViewModel] until its last [LifecycleOwner] is destroyed | |
*/ | |
internal class GanjehViewModelStore( | |
private val onCleared: () -> Unit | |
) : ViewModelStore() { | |
private val owners = HashSet<String>(2) | |
fun add(owner: LifecycleOwner, ownerId: String) { | |
owners.add(ownerId) | |
owner.lifecycle.addObserver(LifecycleEventObserver { _, event -> | |
if (event == Lifecycle.Event.ON_DESTROY && !owner.isChangingConfigurations()) { | |
remove(ownerId) | |
} | |
}) | |
} | |
private fun remove(ownerId: String) { | |
owners.remove(ownerId) | |
if (owners.isEmpty()) { | |
clear() | |
onCleared.invoke() | |
} | |
} | |
private fun LifecycleOwner.isChangingConfigurations(): Boolean { | |
return when (this) { | |
is Fragment -> activity?.run { isChangingConfigurations } ?: false | |
is Activity -> isChangingConfigurations | |
else -> throw IllegalStateException("Unknown lifecycle owner $this") | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Usage: