Android custom view lifecycle with dependency injection as a bonus
Today, I am going to show you how to add the activity lifecycle to a custom view alongside supporting dependency injection.
Lifecycle is important to pause/resume data retrieving or any calculation while the activity is active/inactive. Views have their own lifecycle and it has lots of hassle to bind it to activity lifecycle, now, imagine you want to use
Dagger2 to inject your
ViewModel in your view which needs the Activity lifecycle that you do not have in a custom view.
OK, with the introduction above, now we know we need to set up Dagger2 for our custom view and bind the activity lifecycle to it.
What I love about Kotlin is the easy build-in delegate pattern using keyword
by. The reason that I am telling this is, we will use this keyword multiple places and you can see how it makes your code easier to read and understand.
OK, let jump to the implementation. Just need to mention that in all example I remove
package xxxx to make it easier to copy-paste.
We need an interface for our lifecycle methods and another one for our
ViewModelUtil is a custom implementation of view model initiator for Dagger2. Here is a great article about it.
ViewModel with Dagger2 (Android Architecture Components)
Hello everyone! 👋 In this story, I want to share some light on how you can use ViewModel (Android Architecture…
Let’s dive deep and see what above methods do one by one:
Detached: Will be called whenever the view is destroyed or removed from the parent
(onDetachedFromWindow). We need this method to dispose all listeners and mark the lifecycle as destroyed.
Attached: Will be called whenever the view is attached to the parent
(onAttachedToWindow). We will add our listeners here and mark the lifecycle as started.
Disposable.track: As you might guess, we want to track our
Disposables and dispose them in detached method.
getLifeCycle: No explanation needed. This is a method from
LifeCycleOwner interface that we need to override it and return our custom lifecycle.
inject: And finally the most important part, our beloved injection. To make this method work, we need some definitions in our Dagger2 module which will be covered later in this article.
Now, it is time to implement our interface.
Some clarification besides the interface definition.
@Suppress("UNUSED") : The reason that we need this is, as we are not going to extend this class directly and we just use it with
lint complain about it, so we make sure it does not happen.
lifeCycleRegistry: This is the most important part of this implementation, as I mentioned above I love Kotlin and I love Kotlin
lazy initialization even more. We define our custom lifecycle here and we will bind it to our activity lifecycle. As you can see, we pass
LifecycleRegistry constructor because we want to make it listen to our custom lifecycle changes. We pass the
LifecycleOwner to our ViewModel observer to make it aware of the lifecycle.
contextLifecycle: It is self-explanatory. We need this variable to make sure we are removing all observers whenever the activity is paused or is destroyed.
application: We need this variable to enable the injection in our custom view.
@OnLifecycleEvent annotation: Annotated methods with
@LifecycleEvent annotation are the main part of the implementation, as we extended
LifecycleObserver interface in our
ComponentAccessor interface, we are making the android aware of our lifecycle listeners, so whenever android is firing lifecycle events we can catch those in annotated methods for relevant events
(ON_CREATE,ON_RESUME,...) and then we update our lifecycle state.
@SuppressLint("MissingSuperCall") : As we do not use this class directly, lint might give error related to not calling super.
LifecycleOwnerNotFoundException is just a simple exception to make it easier to catch the relevant errors.
With all mentioned above, now it is the time to create our base custom view. Here is the base layout that we can extend it later to create our UI.
It is straightforward, as you see we are using delegate pattern to implement our interface
OK, it is almost done, just two small things remain.
I do not think it needs an explanation. The only thing that I need to mention is, do not mix the constructors. This article dives deep in the problem by causing mixing the constructors.
The danger of assumptions: Kotlin with Android custom views
Exploring the wonders that Kotlin offers for Android developers I came across a nice language feature that can simplify…
And finally, you need to define your custom component in Dagger2 module to make
Done. Now you have a custom view with activity lifecycle and dependency injection.
I hear some places talking about using dependency injection is not recommended in CustomView but I had specific needs for my component and I wanted to have my custom view to preventing duplication or binding to fragment or activity. So I had to research a lot and find my way to implement it. I have no performance issues so far, and it works like a charm.
Also, here is the issue raised in Dagger2 GitHub related to having an injection in view. https://github.com/google/dagger/issues/720
I hope this article was helpful, if it was, please clap it and leave me a comment and let me know your thoughts.