Michael Pohl
09/23/2022, 12:39 PMclass SomeFragment: Fragment() {
val myVal: SomeClass by lazy {
SomeClass { requireContext().packageName }
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
myVal.runLambda()
}
}
class SomeClass(
private val willBeRunSomeTimeInTheFuture: () -> String) {
fun runLambda() {
Thread.sleep(1000)
println(willBeRunSomeTimeInTheFuture())
}
}
Trying to wrap my head around if I have to worry when using requireContext
in a lambda that might get executed at a point the fragment is potentially already gone again, and what to do about it. If this is problematic (happy to hear why exactly), would a WeakReference
be enough to mitigate this, like below?
WeakReference(requireContext()).get()?.packageName ?: ""
Or do I completely misunderstand something here?
Let me know if this example or my question is not sufficient to understand the problem.ephemient
09/23/2022, 12:42 PMlazy {
val context = requireContext()
SomeClass { context.packageName }
}
or
lazy {
SomeClass(requireContext()::packageName)
}
then you'll leak the context instead, but it will be presentephemient
09/23/2022, 12:43 PMgetContext()
?ephemient
09/23/2022, 12:44 PMMichael Pohl
09/23/2022, 12:57 PMJuliane Lehmann
09/23/2022, 12:58 PMcontext
bound in the lambda - that works.
4. For the specific example, I'd go with 1. In general, 3 is a solution.Juliane Lehmann
09/23/2022, 1:01 PMby lazy
. It doesn't matter at all whether you bind the context to a separate name inside the lambda or not.Juliane Lehmann
09/23/2022, 1:03 PMclass SomeClass(
private val contextRef: WeakReference<Context>
)
fun runLambda() {
Thread.sleep(1000)
contextRef.get()?.let {
// do something with the context if it's still there. If not, we don't care anymore.
}
}
class SomeFragment : Fragment() {
lateinit var myVal: SomeClass
override fun onAttach(context: Context) {
myVal = SomeClass(WeakReference(context))
}
}
Michael Pohl
09/23/2022, 1:04 PMrequireContext()
, doesn't my SomeClass
instance need to keep a reference to my fragment, possibly preventing it from being garbage collected?ephemient
09/23/2022, 1:05 PMSomeClass
is the fragment, then they can all be GC'ed at once. cycles aren't a problemephemient
09/23/2022, 1:05 PMWeakReference(requireContext())
over a simple getContext()
(the nullable one, not requireContext()
)Juliane Lehmann
09/23/2022, 1:07 PMRobert Williams
09/23/2022, 1:09 PMRobert Williams
09/23/2022, 1:10 PMget()
immediatelyRobert Williams
09/23/2022, 1:13 PMMichael Pohl
09/23/2022, 2:07 PMfun <T> ViewModelStoreOwner.componentStore(
componentProvider: () -> T
) = lazy {
ViewModelProvider(
this,
object : ViewModelProvider.Factory {
override fun <VM : ViewModel> create(modelClass: Class<VM>): VM {
return ComponentStore(
componentProvider(),
doOnClear
) as VM
}
}
)[ComponentStore::class.java].component as T
}
private class ComponentStore<T> constructor(
val component: T,
) : ViewModel()
class MyFragment: Fragment() {
private val component: SomeComponent by componentStore(
componentProvider = {
requireContext().getAppComponentAs<SomeComponent.Factory>()
.createSomeComponent()
},
doOnClear = {
MyFragmentViewModel().destroy()
}
)
}
I have this delegate function by componentStore()
that stores a Dagger component in a generic store (which extends ViewModel
, so it potentially lives longer than my fragment.
There is a extension Context.getAppComponentAs()
(which is why I use requireContext()
) that retrieves the application component as a factory interface - that's so I have access to the application component without actually knowing it.
There also is a doOnClear()
function that gets run
So I want to understand if this use of requireContext()
is safe. If it is not, how can I make it safe? Not sure I can safely hook this up to any kind of LifeCycle-related things...?Robert Williams
09/23/2022, 2:22 PMRobert Williams
09/23/2022, 2:23 PMRobert Williams
09/23/2022, 2:24 PMRobert Williams
09/23/2022, 2:25 PMMichael Pohl
09/23/2022, 2:27 PMMichael Pohl
09/23/2022, 2:28 PMRobert Williams
09/23/2022, 2:29 PMRobert Williams
09/23/2022, 2:32 PMMichael Pohl
09/23/2022, 2:36 PMViewModel
, but I'm capuring these two functions that I pass in to my lazy function and from what I understand (which might be wrong) this creates an implicit reference to my fragment. Obviously I don't understand it all fully 🙂Robert Williams
09/23/2022, 2:54 PMby lazy
will have a reference to the Fragment but it’s also only referenced from the Fragment so will be cleaned up when the Fragment is destroyedMichael Pohl
09/23/2022, 3:02 PM