i'm making an app and i want to separate my UI logic into multiple UI classes with BaseUi class being lifecycle aware. I'm using Kodein as my DI and i have an issue with fragment.viewLifecycleOwnerLiveData.observe
not being called when instance of my ui class is being retrieved by Kodein.
Here is my Fragment class:
class ListFragment : Fragment(), DIAware {
override val di: DI by closestDI()
override val diTrigger: DITrigger = DITrigger()
private var binding: FragmentMoviesBinding? = null
private val fragmentBinding get() = binding
private val kodeinMoviesUi: MoviesUi by instance() //fragment does not observe viewLifecycleOwnerLiveData
private val moviesUi: MoviesUi = MoviesUi(this) //fragment now observe viewLifecycleOwnerLiveData
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentMoviesBinding.inflate(inflater, container, false)
return binding?.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
diTrigger.trigger()
}
override fun onDestroyView() {
super.onDestroyView()
binding = null
}
}
BaseUi class:
abstract class BaseUi<F : Fragment>(private val fragment: F) : LifecycleObserver {
init {
fragment.viewLifecycleOwnerLiveData.observe(fragment, { subscribeToLifecycle() })
}
private fun subscribeToLifecycle() {
fragment.viewLifecycleOwner.lifecycle.addObserver(object : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
fun onCreate() {
onViewCreated()
}
})
}
abstract fun onViewCreated()
}
And UiModule:
val uiModule = DI.Module("uiModule") {
bind<ListFragment>() with provider { ListFragment() }
bind<MoviesUi>() with provider { MoviesUi(instance()) }
}
Cross post from https://github.com/Kodein-Framework/Kodein-DI/issues/353
Here is your problem
bind<ListFragment>() with provider { ListFragment() }
.You bound the
ListFragment
with a provider, meaning every time you ask to the container it will create an instance ofListFragment
. So, when you injectMoviesUi
withprivate val kodeinMoviesUi: MoviesUi by instance()
, it gets another instance ofListFragment
.I suggest that you define the binding for
MoviesUi
as a factory, waiting to receive aListFragment
instance:bind<MoviesUi>() with factory {fragment: ListFragment -> MoviesUi(fragment) }
then you can inject it in theListFragment
like:private val kodeinMoviesUi: MoviesUi by instance(args = this)