- Android MVVM with Dagger 2, Retrofit, RxJava, Architecture Components
- What is MVVM?
- Project Configuration
- Project Structure
- Setting Up Retrofit Interface
- Setting Up Application Class, Base Activity, Base Fragment
- Setting Up Dagger 2 Component & Modules
- Creating Custom ViewModel Factory
- Setting Up ViewModel
- Setting Up Fragment
- Creating RecyclerView Adapter
- Dagger-Android with MVVM: Dependency Injection for Android
- Prerequisites:
- Dagger-android Implementation:
- Step 1. Add Dependencies:
- Step 2. Create an Application class:
- Step 3. Create AppComponent:
- Step 4. Bind Application instance to AppComponent:
- Step 5. Create ActivityBuilderModule:
- Step 6. Create AppModule:
- Step 7. Scoping with singleton:
- Step 8. Subcomponents:
- Step 9. Injecting Fragments:
- Step 10. Injecting ViewModels:
- Step 11. Injecting dependencies like Retrofit etc:-
- Bonus:
- Bonus 1. Custom Scopes:-
- Bonus 2. @Named and @Qualifiers:
Android MVVM with Dagger 2, Retrofit, RxJava, Architecture Components
Jul 7, 2018 · 3 min read
I used to work with MVP pattern until now. However, when Google released nice-to-use components like the ViewModel along with the Android Jetpack, I have tried to work with MVVM pattern. In this article, we will see how can we use the MVVM pattern with Retrofit, RxJava, and Dagger 2.
What is MVVM?
Model-View-ViewModel architecture consists of 3 parts.
- The View gets user’s actions and sends to the ViewModel , or listens live data stream from the ViewModel and provides to the users.
- The ViewModel gets user’s actions from the View or provides data to View .
- The Model abstracts the data source. View and ViewModel uses that on data stream.
Project Configuration
We implement Android Lifecycle, Retrofit, RxJava, ButterKnife and Dagger 2 libraries in addition to Support libraries.
Project Structure
We will create packages by features. It will make your code more modular and manageable.
Setting Up Retrofit Interface
We have used G ithub API for Json source and as you see Single<> return type in order to observe data with RxJava .
Setting Up Application Class, Base Activity, Base Fragment
We have to use DaggerApplication , DaggerAppCompatActivity and DaggerFragment classes for injecting objects with ContributesAndroidInjector annotation.
We have used abstract layoutRes() function in order to get resource layout id from Activity which extends BaseActivity .
Setting Up Dagger 2 Component & Modules
As you can see, we have wrote only AndroidSupportInjectionModule , ActivityBindingModule and ViewModelModule to the module parameter. We will write other required modules in which Activity or Fragment they needed.
If explain what we have did in that code snippet;
- provideRetrofit — Provides Retrofit adjusted with base url , adapter and converter factories. addCallAdapterFactory() function gets adapter factory for supporting service method return types, add instance of RxJava2CallAdapterFactory for RxJava support.
- provideRepoService — Provides Retrofit interface class for making requests.
Creating Custom ViewModel Factory
ViewModelFactory is a factory that extends ViewModelProvider.Factory in order to provide ViewModel instances to consumer fragment classes. We have injected that class with the ViewModelModule .
Setting Up ViewModel
In ViewModel , we will assign the data which loaded with Retrofit to the MutableLiveData . So, how we can use MutableLiveData? We assign the data to MutableLiveData with setValue or postValue methods, and observe this data in LifeCycleOwner (Activity or Fragment). When we make any changes on the MutableLiveData , this change is dynamically declared to the view. Also, It does not oblige us to check whether View is alive in every transaction.
As you can see, we didn’t use any component injecting code, because we have injected all required classes in the MainFragmentBindingModule and also we have did same thing for activities in the ActivityBindingModule .
Setting Up Fragment
As you know, fragment is our View part. We have injected ViewModelFactory and started to observe LiveData objects.
Creating RecyclerView Adapter
In recyclerView adapter, we have only observed List LiveData and bind them to the recyclerView .
Источник
Dagger-Android with MVVM: Dependency Injection for Android
May 11, 2020 · 9 min read
We all know that the concept of dependency injection has a quite steep learning curve and is not easily understandable especially for beginners. But that does not mean we can avoid using it. It is very important for making our apps memory-efficient and scalable.
There are many DI frameworks like Spring, Guice, and Dagger but out of these, Dagger is the most popular among Android developers because of its compile-time creation of the dependency graph and other benefits.
In this blog, I want t o introduce you to dagger-android, a DI library by Google built on top of Dagger. It helps you implement DI in android apps in a simpler way by providing some helper classes and reduces boilerplate code. But soon after its launch, Google decided to proceed with a different approach to simply Dagger on android and hence stopped the development of this library but are maintaining(not deprecated) it till there is a stable release of their new library. They will provide a migration guide for dagger-android to their new library. It is a personal choice to use dagger or dagger-android in your projects but you should use one of them, as DI is important.
If you’re using dagger.android, keep using it. If you’re starting a new project, consider dagger.android as an alternative and use if it fits your use case. We’ll provide migration guides from both Dagger and dagger.android code to the new initiative.
This blog does not aim at presenting you with the dagger basics and its components or DI concepts in general. These concepts are well defined in the Android Developer’s documentation and it would be redundant to write the same thing again. That’s why I made a prerequisite list below, which contains some links to give you an overview of these concepts. If you are familiar with them then you can skip the prerequisite section.
Prerequisites:
If you want to use the normal dagger approach then you can have a look at this Google Codelab, where they tried to explain its implementation using an example project. If you want to have a deeper look, there is Documentation you can go through.
Dagger-android Implementation:
Step 1. Add Dependencies:
As we will be using Kotlin annotation processor so apply kapt plugin at the top of the app Gradle file.
Then add the following dependencies for dagger:
If you’re using the Android Databinding library, you may want to increase the number of errors that javac will print. When Dagger prints an error, Databinding compilation will halt and sometimes print more than 100 errors, which is the default amount for javac . For more information, see Issue 306. For that add this to your app Gradle file.
I am assuming that you have gone through the dagger basics link in the prerequisites so you know the concepts of components, modules, and subcomponents. Now we can start setting up our project with dagger-android.
Step 2. Create an Application class:
As we know that the app component is scoped with the application lifetime so it must be created in the Application class. So we will create an application class but using dagger-android we extend it from DaggerApplication and implement its method applicationInjector() which will be used for creating AppComponent in further steps.
Note: use DaggerApplication provided by dagger-android support
Then register your application class in the manifest using the name tag.
Step 3. Create AppComponent:
Now inside a di package create the AppComponent interface. It extends AndroidInjector with the base class type so that we can avoid writing a bunch of code to inject the application and we can directly say that this base class is the client for DI.
To tell dagger that this is the component, add @component annotation at the top. Also, include the given module to use those special android dagger classes like AndroidInjector. Then finally when you build the app, Dagger will generate a DaggerAppComponent file for you which you can see in the generated java folder.
Step 4. Bind Application instance to AppComponent:
Now we will bind the Application class instance to the component so that wherever in the application we need a context, then we can get this instance. This instance is bound to the component so it will live for the entire application lifetime.
This is same as creating dependencies in modules but using @BindsInstance, we can bind dependencies which are available during the creation of component so we do not need to create their instances later.
We will override the component factory to bind the instance.
Then create AppComponent’s instance in the application class.
Note: First build your app before doing this otherwise DaggerAppComponent will give error.
Step 5. Create ActivityBuilderModule:
Now our basic dagger setup is done so we can start with injecting dependencies. First, we will create an ActivityBuildersModule which is responsible for injecting activities and creating subcomponents for them. For that use contributesAndroidInjector to let dagger know that a given activity is a potential client so there is no need for any other injection code in the activity.
Then add this module in the AppComponent module list to let dagger know about this module.
Also, let the activity extend DaggerAppCompactActivity now.
You can test the injection by creating a dummy string method in the ActivityBuildersModule.
@Provides just helps you create instances of the classes
Then inject it into the activity and use logs to see the value.
Other than @Provides, dagger also provides one more annotation which is @Binds. This helps us to bind the implementation of an interface, so whenever we want dagger to inject a dependency of the interface type it will automatically inject it with the bound implementation. One example is provided below where we have a storage interface and we want dagger to return SharedPreferencesStorage for it:
Step 6. Create AppModule:
We can have multiple modules to separate different types(like NetworkModule, StorageModule) of dependencies and then link them to components. For some app-level dependencies that do not fall into any category create a general module named AppModule. Shift the test dependency here from the ActivityBuildersModule and test it again after adding it to the module list in AppComponent.
Referencing other dependencies:
Sometimes while creating an instance of a dependency, we need another dependency so to reference other dependencies in the provide method just add them as function arguments, and dagger will take care of the rest. For example, here we are using the application instance that we bind using the bind instance method in step 4.
Step 7. Scoping with singleton:
In simple words scoping is a way to bind the dependencies to components or subcomponents lifetime. For example, consider a dependency for user authentication, now its instance is useful only during the lifetime of authentication activity so we would be wasting our resources if its instance is there in the memory for the entire time. Hence by scoping it to its activities lifetime, we make sure that when the activity gets destroyed the dependency’s instance also gets destroyed with it.
To do this, just put a scope at the top of a component or subcomponent and then put it at the top of the dependencies you want to scope.
N ote: @Singleton annotation is inbuilt but we can create custom scopes(see Bonus-1 for that.
Then all the dependencies for the same lifetime must be annotated with the same scope.
Step 8. Subcomponents:
Subcomponents are useful when we have some dependencies which are required only for a particular flow (we saw an example of authentication flow in the previous step). Each subcomponent can access the dependencies created by the parent component. The annotation @ContributesAndroidInjector creates a subcomponent implicitly and that generated code can be seen in the generated java folder. A subcomponent has its own modules where we can define dependencies and can also scope them with custom scopes (see Bonus-1 for that).
Step 9. Injecting Fragments:
The process of injecting Fragment is similar to what we did in step-5 for activities so just create a FragmentBuilderModule and use the same approach as in ActivityBuildersModule using ContributesAndroidInjector define different functions for different fragments.
Then for connecting it to the activity’s lifecycle, add it to the activity subcomponent in ActivityBuildersModule.
Then extend fragment from DaggerFragment and inject dependencies.
You can have different FragmentBuildersModule for different activities.
Step 10. Injecting ViewModels:
Injecting ViewModel directly is difficult because their instances are created by ViewModelProvider. This problem and its various solutions are explained in detail in this blog.
We will use the concept of Multibinding to inject dependencies in ViewModels. It is a difficult concept so I will explain it with its implementation.
First, we will create a ViewModelKey annotation which when used on provider methods, basically says that the services returned by these methods must be put into special Map . The keys in this Map will be of type KClass and the values will be of type (subclass of ViewModel ).
After creating the ViewModelKey we can add a bind method for a specific ViewModel in the following manner:
Note: the return type of the bind method is ViewModel and not MyViewModel . It’s intentional. @IntoMap annotation says that Provider object for this service will be inserted into Map , and @ViewModelKey annotation specifies under which key it will reside.
As a result, Dagger will create an implicit Map filled with Provider objects and put it onto the objects graph. You can make use of that Map by creating a ViewModelFactory by extending ViewModelProvider.Factory and passing the map into it in the following manner:
Whenever we will call the create method of ViewModelProvider.factory with the ViewModel type as an argument, the method will use it as key and return the value stored in the map which the ViewModel object.
Now the only thing remaining is binding this ViewModelFactory to ViewModelProvider.Factory so that we can make use of it.
Let Dagger know about this module by adding it to the module list of AppComponent.
Add your ViewModelModule to your activity subcomponent:
Now the setup is done we can inject ViewModelFactory in fragment or activity and use it to create ViewModels:
As our ViewModel objects are getting created now, so we can inject dependencies in it using the @Inject constructor annotation and putting the dependencies in the constructor:
Note: we cannot inject dependencies in activities and fragments using inject constructor because their creation is not handled by us or the DI they are created by gradle.
Step 11. Injecting dependencies like Retrofit etc:-
Now you can create dependencies as you want, like for creating a retrofit instance you can just create its object and return it from a @provides annotated static method in the modules:
Bonus:
We are done with all the basic setup of dagger-android dependency injection for android. This does not cover the entire magic that you can do with dagger-android, there are many more things. I am providing you with some bonus topics for going ahead with dagger-android.
Bonus 1. Custom Scopes:-
Now we all know that we do not want all the dependencies to live for the entire lifetime and that’s why we create components.
To link dependencies to the component’s lifecycle we use scopes. We saw the @Singleton scope before now we will create a custom scope for our activity component and will annotate the dependency with it.
A custom scope can be created by the following code:
Then annotate your subcomponent with it:
And then annotate the dependencies in your module:
Note: Don’t annotate another subcomponent(like fragments in FragmentBuilderModule) with this scope that will lead to overlapping of annotations and will give error.
Don’t do it like this:
@MyActivityScope
@ContributesAndroidInjector
abstract fun
Bonus 2. @Named and @Qualifiers:
There are use cases when we need to inject different implementations of the same type in different places. For example, two implementations of an interface or two provider methods return the same data type.
This will confuse dagger what to provide when you inject dependency and it will give an error.
So to get a specific object of a given type we can use @Named Qualifier which tells dagger about the object type. Modifying the above example with @Named we get:
Now we can inject them by using this @Named annotation like this:
If you go deeper to see the implementation of class Named you will see it it just an annotation:
Using Qualifiers We can create our own annotations like this and then can use them to annotate dependencies. Let’s create an annotation:
Источник