- New Android Injector with Dagger 2 — Part 1
- Author of Mert şek
- Translation from: The Gold Project
- This article is permalink: github.com/xitu/gold-m…
- Translator: MummyDing
- Proofreader: Levine
- Dagger New Android Injector: Dagger 2 (1)
- Dagger New Android Injector: Dagger 2
- Dagger New Android Injector: Dagger 2
Dagger Dagger Android Support and Android Compiler are added to the Dagger module. For us, this is a big change, and all Android developers should try this new Android DEPENDENCY injection framework as soon as possible.
Before I start introducing the new AndroidInjector class and the Dagger 2.11 library, if you’re not familiar with Dagger 2 or have never used it before, I highly recommend you take a look at the Dagger Starter guide to figure out what dependency injection is. Why do you say that? Since Android Dagger involves a lot of annotations, it can be tricky to learn. In my opinion, you’d better learn Dagger 2 and DEPENDENCY Injection before you learn Android Dagger. There is an introductory article on DEPENDENCY injection, Blog 1, and an article on Dagger 2, Blog 2.
Old usage
Before Dagger 2.10, Dagger 2 was used like this:
((MyApplication) getApplication())
.getAppComponent()
.myActivity(new MyActivityModule(userId))
.build()
.inject(this);
Copy the code
What could be wrong with that? We want to use dependency injection, but what is the core principle of dependency injection?
Shouldn’t a class care how it was injected
So we had to get rid of those Builder methods and Module instance creation sections.
The sample project
I didn’t do much in the example project I created, and I wanted it to be as simple as possible. It contains only two activities, MainActivity and DetailActivity, which are injected into the corresponding Presenter implementation class and request the network interface (I didn’t actually make the HTTP request, I just wrote a fake method).
The preparatory work
Add the following dependencies to build.gradle:
compile 'com. Google. Dagger: a dagger: 2.11 rc2'
annotationProcessor 'com. Google. Dagger: a dagger - compiler: 2.11 rc2'
compile 'com. Google. Dagger: a dagger - android - support: 2.11 rc2'
Copy the code
Package structure
The Application class builds a graph using the AppComponent. The header of the AppComponent class is annotated with @Component. When AppComponent builds with its Module, we get a map with all the required instance objects. For example, when the App Module provides an ApiService, we will get an ApiService instance object when we build the Component that owns the App Module.
If we want to add the Activity to the Dagger map so that we can get the required instance directly from the parent Compponent, we can simply annotate the Activity with @subComponent. In our example, the DetailActivityComponent and MainActivityComponent classes are annotated with @subComponent. Finally, we need to tell the parent Component about the child Component, so that all root Compponent can know all of its child components.
Don’t worry, I’ll explain what @subComponent, @Component and DispatchActivity are. Just to give you an idea of @Component and @Subcomponent.
@Component and @Component.Builder
**@Component**(modules = { AndroidInjectionModule.class, AppModule.class, ActivityBuilder.class}) public interface AppComponent { **@Component.Builder** interface Builder { **@BindsInstance** _Builder application(Application application); _ _AppComponent build(); _ } void inject(AndroidSampleApp app); }Copy the code
**@Component: **Component is a graph. When we build a Component, the Component uses the Module to provide an instance object to be injected.
**@Component.Builder: ** We may need to bind some instance objects to the Component. In this case we can create an interface annotated with @Component.Builder and then add any methods we want to the Builder. In my example, I want to add the Application to the AppComponent.
Note: If you want to create a Builder for your Component, your Builder interface needs to have a Builder () method that returns the type of the Component you created.
Injection AppComponent
DaggerAppComponent
._builder_()
**.application(this)**
.build()
.inject(this);
Copy the code
As you can see from the code above, we have bound the Application instance to the Dagger map.
I think you have some understanding of @Component.Builder and @Component. Now I want to talk about the structure of the project.
The Component/Module structure
When using Dagger, we can divide the App into three layers:
- Application Component
- Activity Components
- Fragment Components
Application Component
@Component(modules = {
AndroidInjectionModule.class,
AppModule.class,
ActivityBuilder.class})
public interface AppComponent {
@Component.Builder
interface Builder {
@BindsInstance Builder application(Application application);
AppComponent build();
}
void inject(AndroidSampleApp app);
}
Copy the code
Every Android Application has an Application class, which is why I also have an Application Component. The Component representation provides instances for the application layer (such as OkHttp, Database, SharedPrefs). This Component is the root of the Dagger map. The Application Component provides three modules in our Application.
- AndroidInjectionModule: This class is not one we wrote, it is an inner class in Dagger 2.10 that provides us with activities and fragments with the given Module.
- ActivityBuilder: Module we created ourselves, this Module is for the Dagger, where we put all the Activity mappings. So the Dagger can pick up all the activities during compilation, so we have two activities in our App, MainActivity and DetailActivity, so I’m going to put them both here.
@Module
public abstract class ActivityBuilder {
@Binds
@IntoMap
@ActivityKey(MainActivity.class)
abstract AndroidInjector.Factory<? extends Activity> bindMainActivity(MainActivityComponent.Builder builder);
@Binds
@IntoMap
@ActivityKey(DetailActivity.class)
abstract AndroidInjector.Factory<? extends Activity> bindDetailActivity(DetailActivityComponent.Builder builder);
}
Copy the code
- AppModule: We provide Retrofit, okHTTP, persistent database, SharedPrefs here. One important detail is that we must add the child Component to the AppModule so that the Dagger map can recognize it.
@Module(subcomponents = {
MainActivityComponent.class,
DetailActivityComponent.class})
public class AppModule {
@Provides
@Singleton
Context provideContext(Application application) {
returnapplication; }}Copy the code
Activity Components
We have two activities: MainActivity and DetailActivity. They each have their own Module and Component, but they are also child Components, as I defined in the AppModule above.
- MainActivityComponent: This Component is a bridge to the MainActivityModule, but it does not need to add inject() and build() methods to the Component. MainActivityComponent integrates these methods from the parent class. The AndroidInjector class is a new addition to the dagger Android framework.
@Subcomponent(modules = MainActivityModule.class)
public interface MainActivityComponent extends AndroidInjector<MainActivity>{
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MainActivity>{}
}
Copy the code
- MainActivityModule: thisModule 为
MainActivity
Provides related instance objects (for exampleMainActivityPresenter
). Did you notice that the provideMainView() method takes MainActivity as an argument? Yes, we use the MainActivityComponent to create the object we need. So Dagger adds our Activity to the graph and therefore can use it.
@Module
public class MainActivityModule {
@Provides
MainView provideMainView(MainActivity mainActivity){
return mainActivity;
}
@Provides
MainPresenter provideMainPresenter(MainView mainView, ApiService apiService){
returnnew MainPresenterImpl(mainView, apiService); }}Copy the code
Similarly, we can create the DetailActivityComponent and DetailActivityModule the same way we create the MainActivityComponent and MainActivityModule, so we’ll skip over the details.
Fragment Components
What if we have two fragments in the DetailActivity? In fact, it’s not hard to imagine. Consider the relationship between an Activity and an Application. Application knows all the activities through the mapped Module (in my case, the ActivityBuilder). Add all activities to the AppModule as subcomponents.
The same is true for activities and fragments. First create a FragmentBuilder Module and add it to the DetailActivityComponent.
Now we can create the DetailFragmentComponent and DetailFragmentModule the same way we created the MainActivityComponent and MainActivityModule.
DispatchingAndroidInjector
The last thing we need to do is inject it into the injector. What does an injector do? I want to explain it in a simple piece of code.
public class AndroidSampleApp extends Application implements HasActivityInjector {
@Inject
DispatchingAndroidInjector<Activity> activityDispatchingAndroidInjector;
@Override
public void onCreate() {
super.onCreate();
//simplified
}
@Override
public DispatchingAndroidInjector<Activity> activityInjector() {
returnactivityDispatchingAndroidInjector; }}Copy the code
The Application has a lot of activities, which is why we implemented the HasActivityInjector interface. What about an Activity that has multiple fragments? Do we need to implement the HasFragmentInjector interface in our Activity? Yes, that’s what I’m saying!
public class DetailActivity extends AppCompatActivity implements HasSupportFragmentInjector {
@Inject
DispatchingAndroidInjector<Fragment> fragmentDispatchingAndroidInjector;
//simplified
@Override
public AndroidInjector<Fragment> supportFragmentInjector(a) {
returnfragmentDispatchingAndroidInjector; }}Copy the code
If you don’t have a child fragments anything you don’t need to inject into fragments, you don’t need to implement HasSupportFragmentInjector interface. But in our example we need to create a DetailFragment in DetailActivity.
AndroidInjection.inject(this)
What’s all this for? This is because activities and fragments are not supposed to be injected, so how should we inject them?
In the Activity:
@Override protected void onCreate(Bundle savedInstanceState) { **AndroidInjection._inject_(this); ** super.onCreate(savedInstanceState); }Copy the code
In the Fragment:
@Override
public void onAttach(Context context) {
**AndroidSupportInjection._inject_(this);
** super.onAttach(context);
}
Copy the code
That’s right, congratulations, that’s all done!
I know it’s complicated and the learning curve is steep, but we got there. Now, our class doesn’t know how to be injected. We can Inject the desired instance object into our UI element via the @Inject Annotation annotation.
You can find this project on my GitHub homepage and I suggest you check out the Dagger documentation.
iammert/dagger-android-injection _dagger-android-injection – Sample project explains Dependency Injection in Android using dagger-android framework._github.com
Dagger ‡ A fast dependency injector for Android and Java. A fast dependency injector for Android and Java.google.github.io
In part 2, I want to simplify the Android-Dagger injection with the new annotations provided by the Dagger, but before I simplify I want to show you what it looks like in its original form.
The second part is here.
Thanks for reading, and have fun coding!
Diggings translation project is a community for translating quality Internet technical articles from diggings English sharing articles. The content covers the fields of Android, iOS, front end, back end, blockchain, products, design, artificial intelligence and so on. For more high-quality translations, please keep paying attention to The Translation Project, official weibo and zhihu column.