Dagger2 development issues for Android
One difficulty with Android development using Dagger2 is that some Android framework classes, such as activities and fragments, are instantiated by the operating system. If you want Dagger2 to inject these objects well, You have to add the following code in the lifecycle to complete the injection process.
public class FrombulationActivity extends Activity {
@Inject Frombulator frombulator;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// DO THIS FIRST. Otherwise frombulator might be null!
((SomeApplicationBaseType) getContext().getApplicationContext())
.getApplicationComponent()
.newActivityComponentBuilder()
.activity(this)
.build()
.inject(this);
// ... now you can write the exciting code
}
}
Copy the code
This has the following problems:
- 1. The above code will have to be copied and pasted into all activities, which will cause problems in refactoring the code in the future. The more people on your team copy the above code block, the less people will know what the code is really for.
- 2. More importantly, it requires the request injection type to know about its syringe, even though it can be done through an interface rather than a specific type, but it breaks the core principle of dependency injection: a class should not know anything about how it was injected.
Above, so based on Dagger2, suitable for Android development dagger2-Android came into being.
The above translation and the following code are from the Official Dagger2 website, the official portal
Code portal, submitted to Github
configuration
To use the Dagger.Android, add the following dependency.
//If you're using classes in dagger.android you'll also want to include:
compile 'com. Google. Dagger: a dagger - android: 2.15'
compile 'com. Google. Dagger: a dagger - android - support: 2.15' // if you use the support libraries
annotationProcessor 'com. Google. Dagger: a dagger - android - processor: 2.15'
Copy the code
Injection of the Activity
- Add AndroidInjectionModule to application Component To ensure that Android classes (activities, Fragments, Services, BroadcastReceivers, contentProviders, etc.) can be bound.
@Singleton
@Component(modules = {AndroidInjectionModule.class,AppModule.class,})
public interface AppComponent {
void inject(MyApplication application);
}
Copy the code
- 2 create a component – DaggerAndroidActSubComponent: It has inherited from AndroidInjector
and contains an abstract class Builder in this child component that has inherited from AndroidInjector.Builder and is annotated by @subComponent.Builder.
@Subcomponent(modules = AndroidInjectionModule.class)
public interface DaggerAndroidActSubComponent extends AndroidInjector<DaggerAndroidActivity>{
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<DaggerAndroidActivity>{
}
}
Copy the code
-
- Create module-daggerAndroidactModule: In defining the subcomponents DaggerAndroidActSubComponent, define the components of the Module – DaggerAndroidActModule, and bind DaggerAndroidActSubComponent. Builder, Then add the DaggerAndroidActModule to the component hierarchy. In the DaggerAndroidActModule, you provide a method to get the Student instance.
@Module(subcomponents = DaggerAndroidActSubComponent.class)
public abstract class DaggerAndroidActModule {
@Binds
@IntoMap
@ActivityKey(DaggerAndroidActivity.class)
abstract AndroidInjector.Factory<? extends Activity> bindDaggerAndroidActivityInjectorFactory(DaggerAndroidActSubComponent.Builder builder);
@Provides
@Singleton
static Student provideStudent() {
returnnew Student(); }}Copy the code
- 4 Add the DaggerAndroidActModule defined in the previous step to the Application Component.
@Singleton
@Component(modules = {AndroidInjectionModule.class, AppModule.class, DaggerAndroidActModule.class})
public interface AppComponent {
void inject(MyApplication application);
}
Copy the code
Note: If you provide a dependency instance in a Module, the @provides method must be static or the compiler will fail.
- 5 custom Application, and implement HasActivityInjector interface, at the same time injection (@ Inject) DispatchingAndroidInjector < Activity > instance, This instance was returned in the activityInjector() method.
public class MyApplication extends Application implements HasActivityInjector{
@Inject
DispatchingAndroidInjector<Activity> dispatchingActivityInjector;
@Override
public void onCreate() {
super.onCreate();
DaggerAppComponent.create()
.inject(this);
}
@Override
public AndroidInjector<Activity> activityInjector() {
returndispatchingActivityInjector; }}Copy the code
-
- In DaggerAndroidActivity. OnCreat () method, in the calling super. The onCreate () before calling AndroidInjection. Inject (this). I provide a LogUtil object in the AppModule and a Student object in the DaggerAndroidActModule.
public class DaggerAndroidActivity extends AppCompatActivity {
@Inject
LogUtil mLogUtil;
@Inject
Student mStudent;
@Override
protected void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dagger_android);
Log.d("hbj--",mLogUtil.toString());
Log.d("hbj--",mStudent.toString());
mLogUtil.d("inject success!"); }}Copy the code
Inject successfully. MLogUtil objects can also print logs.
Disadvantages?
The above is completely referring to the process given by the official website. Is it perfect to do so?
- 1 For every new Activity we create, we write a DaggerYourActivityModule Java file and add it to the AppComponent. If there are enough activities, there are too many.
- 2 every DaggerYourActivityModule Java file to a DaggerYourActivitySubComponent file, also need to set up multiple SubComponent files.
- 3 These DaggerYourActivityModules are added to the AppComponent, resulting in code for the AppComponent like this:
@Component(modules = {AndroidInjectionModule.class, AppModule.class, DaggerAndroidActModule.class, DaggerYourActivityModule.class, DaggerYourActivity1Module.class, DaggerYourActivity2Module.class, DaggerYourActivity3Module.class, DaggerYourActivity4Module.class, ....... }) public interface AppComponent { void inject(MyApplication application); }Copy the code
Here’s a Pro tip on the official website: If your subcomponent and its builder have no other methods or supertypes than the ones mentioned in step #2, you can use @ContributesAndroidInjector to generate them for you. Instead of steps 2 and 3, add an abstract module method that returns your activity, annotate it with @ContributesAndroidInjector, and specify the modules you want to install into the subcomponent. If the subcomponent needs scopes, Apply the scope annotations to the method as well
If your subcomponent and builder in step 2 mentioned no other method or supertype, you can use @ ContributesAndroidInjector generated them for you. We don't need steps 2 and 3, instead, add an abstract module method, this method returns your activity, using the @ ContributesAndroidInjector annotations, and specify to install to the module in the child component. If a child component needs scopes, you can annotate this method with @scopes as well.Copy the code
The code is as follows:
@ActivityScope
@ContributesAndroidInjector(modules = { /* modules to install into the subcomponent */ })
abstract YourActivity contributeYourActivityInjector();
Copy the code
Code to improve
So our code is improved as follows:
- 1 create a new AllActivitysModule, provide all ActivityModule here, each with @ ContributesAndroidInjector annotation.
@Module(subcomponents = DaggerAndroidActSubComponent.class)
public abstract class AllActivitysModule {
@ContributesAndroidInjector(modules = DaggerAndroidActModule.class)
abstract DaggerAndroidActivity contributeDaggerAndroidActivityInjector();
}
Copy the code
- 2 modify the DaggerAndroidActModule to a normal Module. Deleted (subcomponents = DaggerAndroidActSubComponent. Class) and bindDaggerAndroidActivityInjectorFactory () abstract methods. Examples can be provided here as needed.
@Module
public abstract class DaggerAndroidActModule {
@Provides
static Student provideStudent() {
returnnew Student(); }}Copy the code
- 3 Modify the AppComponent to remove the original DaggerAndroidActModule and add allActivitysModule. class to modules.
@Singleton
@Component(modules = {AndroidInjectionModule.class, AppModule.class, AllActivitysModule.class})
public interface AppComponent {
void inject(MyApplication application);
}
Copy the code
Run the code, also inject success.
Further improvement
Take a look at the code for our AllActivitysModule
@Module(subcomponents = DaggerAndroidActSubComponent.class)
public abstract class AllActivitysModule {
@ContributesAndroidInjector(modules = DaggerAndroidActproModule.class)
abstract DaggerAndroidActivity contributeDaggerAndroidActivityInjector();
}
Copy the code
It is dependent on DaggerAndroidActSubComponent. Class, in the view of DaggerAndroidActSubComponent code
@Subcomponent(modules = AndroidInjectionModule.class)
public interface DaggerAndroidActSubComponent extends AndroidInjector<DaggerAndroidActivity>{
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<DaggerAndroidActivity>{
}
}
Copy the code
AndroidInjector.Builder is DaggerAndroidActivity, which is not available for other activities. We require that it be common to all activities. We continue to improve the code. First analyze DaggerAndroidActSubComponent and AndroidInjector. Builder is DaggerAndroidActivity, and what DaggerAndroidActivity completed, check, The DaggerAndroidActivity completes the injection with just one line of code:
AndroidInjection.inject(this);
Copy the code
So we assume that we can extract a BaseActivity, and all activities inherit from BaseActivity.
- Create a BaseActivity and execute AndroidInjection. Inject (this) in the BaseActivity;
public class BaseActivity extends AppCompatActivity{ @Override protected void onCreate(@Nullable Bundle savedInstanceState) { AndroidInjection.inject(this); super.onCreate(savedInstanceState); }}Copy the code
And inherit BaseActivity from other activities.
public class DaggerAndroidActivity extends BaseActivity {
}
Copy the code
- Create a New BaseActivityComponent. In this case, androidInjector. Builder is the BaseActivity created above.
@Subcomponent(modules = {AndroidInjectionModule.class})
public interface BaseActivityComponent extends AndroidInjector<BaseActivity>{
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<BaseActivity>{
}
}
Copy the code
- Modify the AllActivitysModule code to replace the child with the BaseActivityComponent created above.
@Module(subcomponents = BaseActivityComponent.class)
public abstract class AllActivitysModule {
@ContributesAndroidInjector(modules = DaggerAndroidActproModule.class)
abstract DaggerAndroidActivity contributeDaggerAndroidActivityInjector();
}
Copy the code
Compile and run, and you’ll also see the results.
conclusion
After improvements, if we add a new Activity, we just need to do the following steps:
- 1. The new Activity inherits BaseActivity.
-
- Create a NewActivityModule for the new Activity, which is a normal Module annotated with the @Module annotation, and provide instances of the new Activity. Be careful to use the static keyword.
- Add a new Activity to the AllActivitysModule, similar to the code below.
@Module(subcomponents = BaseActivityComponent.class) public abstract class AllActivitysModule { @ContributesAndroidInjector(modules = DaggerAndroidActproModule.class) abstract DaggerAndroidActivity contributeDaggerAndroidActivityInjector(); / / a new Activity @ ContributesAndroidInjector (modules = DaggerAndroid2ActModule. Class) the abstract DaggerAndroid2Activity contributeDaggerAndroid2ActivityInjector(); }Copy the code
Refer to the official website, after improvement, injection process is more concise. Code portal In addition to the Activity, the Dagger – Android also provides fragments, BroadcastReceiver, Service and other components of injection mode, discuss it next time.