简介 这是一个DI(依赖注入)框架,JSR330是依赖注入的规范,在服务器端有spring实现,dagger2使用注解实现
Dagger2解决了什么问题
没有用到反射,编译期生成静态代码,生成一些Factory模板代码,性能上对比Guice,Dagger1提高
解决了依赖的顺序问题。当有多个类需要初始化(比如A先初始化,B后初始化,A作为B的入参),有依赖的时候要小心顺序问题
不用自己写单例(单例写法没办法抽象),少写很多Factory模板代码
需要用到的时候@Inject,解耦、方便
基本使用 所有代码托管在git@osc上Dagger2Learn
简单注入
假设MainActivity里有一个NuclearController
。现在想用依赖注入,注入到MainActvity里
1 2 3 4 5 6 public class NuclearController { @Inject public NuclearController () { } }
这里NuclearController是无参数构造,所以不需要Module提供资源。MainActivityComponent
如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Component public interface MainActivityComponent { void inject (MainActivity activity) ; } @Inject NuclearController nuclearController; @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerMainActivityComponent.builder().build().inject(this ); Preconditions.checkNotNull(nuclearController); }
记得在DaggerMainActivityComponent
调用之前Build一下 , 按照写代码的顺序,先写自己的模块/layer,然后写Component
,Component的作用是桥梁 用来连接你在Activity里@Inject的地方和在NuclearController构造函数上的@Inject。这是最简单的例子,通常我们的模型不可能空参,而且会依赖各种其他模型
Module提供依赖资源 用@Module标注的类,可以为依赖提供参数,比如说此时NuclearController的构造函数变成
1 2 3 4 @Inject public NuclearController (long electricity) { this .electricity = electricity; }
我们需要给一个module用来提供构造需要的参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Module public class MainModule { private long electricity; public MainModule (long electricity) { this .electricity = electricity; } @Provides public long provideElectry () { return electricity; } } DaggerMainActivityComponent.builder().mainModule(new MainModule(10000 )).build().inject(this );
用@Module修饰类,用@Provides修饰方法,提供需要的资源。对比更改前的代码,只是改了module和注入的地方,改动相当少 所以Component就是桥梁,将Controller和Activity连接起来,Module就是Provider,提供这个连接过程中需要的依赖参数
Scope Ⅰ
换一个实际的例子来说明Scope,Component的概念
.
|-- HeroApplication.java
|-- common
| |-- AppComponent.java
| `-- AppModule.java
dagger2自带一个@Singleton,看看源码:
1 2 3 4 @Scope @Documented @Retention (RUNTIME)public @interface Singleton {}
并没有什么特殊的东西,所以我们自定义Scope只需要换一个名字就行了。Scope代表一种约束,这么说,所有Scope都可以看做是单例,但是不同于我们常规的单例,Scope在哪里用就在哪里是单例,生命周期需要我们自己处理。 比如AppComponent和AppModule都是全局性的,跟随应用生命周期的,我们用Singleton修饰
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 @Singleton @Component (modules = AppModule.class ) public interface AppComponent { Application getApplication () ; Context getApplicationContext () ; } @Module public class AppModule { private Application application; public AppModule (Application application) { this .application = application; } @Provides @Singleton public Application provideApplication () { return application; } @Provides @Singleton public Context provideApplicationContext () { return application.getApplicationContext(); } } public class HeroApplication extends Application { private AppComponent appComponent; @Override public void onCreate () { super .onCreate(); appComponent = DaggerAppComponent.builder().appModule(new AppModule(this )).build(); } public AppComponent getAppComponent () { return appComponent; } }
AppComponent虽然我们用Singleton修饰了,但是如果不在Application里提供一个get方法,这个Singleton的实例就没有用,接下来可以通过Application.getAppComponent使用了(只做理解,这个例子并没有什么卵用)Scope需要注意一点,一旦Component修饰了Scope,Module提供的方法上就必须声明同样的Scope
Scope Ⅱ & dependencies
为了说明Scope生命周期需要自己定义,我们添加BasicComponent,提供App开发过程中一些基础模型
.
|-- HeroApplication.java
|-- common
| |-- ActivityScope.java
| |-- ApiService.java
| |-- AppComponent.java
| |-- AppModule.java
| |-- BasicComponent.java
| |-- DBModule.java
| |-- LocalModule.java
| |-- NetworkModule.java
| `-- ThirdpartyService.java
|-- main
| |-- MainActivity.java
| `-- MainActivityComponent.java
|-- models
| `-- WeatherData.java
网络模块,常用的httpclient,gson都在这里提供
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 @Module public class NetworkModule { @Singleton @Provides public OkHttpClient provideOkHttpClient () { return new OkHttpClient(); } @Singleton @Provides public Gson provideGson () { return new Gson(); } @Singleton @Provides public Retrofit provideRetrofit (OkHttpClient okHttpClient, Gson gson) { return new Retrofit.Builder().baseUrl("http://api.openweathermap.org/data/2.5/" ) .client(okHttpClient) .addConverterFactory(GsonConverterFactory.create(gson)) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build(); } @Singleton @Provides public ApiService provideApiService (Retrofit retrofit) { return retrofit.create(ApiService.class ) ; } @Singleton @Provides public ThirdpartyService provideThirdpartyService (Retrofit retrofit) { return retrofit.create(ThirdpartyService.class ) ; } }
Local模块,包括Cache和Shareperference,需要依赖Application
1 2 3 4 5 6 7 8 @Module public class LocalModule { @Provides @Singleton public Cache provideCache (Application application) { return new Cache(application.getCacheDir(),30 *1024 *1024 ); } }
BasicComponent基本管理类统一注入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Singleton @Component (modules = {AppModule.class , NetworkModule .class , LocalModule .class , DBModule .class }) public interface BasicComponent { ApiService getApiService () ; ThirdpartyService getThirdpartyService () ; Gson getGson () ; OkHttpClient getOkHttpClient () ; Cache getCache () ; } basicComponent = DaggerBasicComponent.builder() .appModule(new AppModule(this )) .localModule(new LocalModule()) .networkModule(new NetworkModule()) .build();
同样的,此时需要给BasicComponent
提供get方法,方便我们后面调用 好了,现在在Activity里就可以直接使用Application.getBasicComponent().getOkHttpClient() 获取实例了,但是饶了这么大圈子,只是这样,跟直接写单例没什么区别。 我想直接在MainActivity里使用@Inject注入这些常用工具
新建ActivityScope
MainActivityComponent里使用依赖BasicComponent模块
1 2 3 4 5 @ActivityScope @Component (dependencies = BasicComponent.class ) public interface MainActivityComponent { void inject (MainActivity activity) ; }
MainActivity里只要@Inject指定需要的注入的工具就可以了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class MainActivity extends AppCompatActivity { @Inject ApiService apiService; @Inject OkHttpClient okHttpClient; @Inject Gson gson; @Inject Cache cache; @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerMainActivityComponent.builder().basicComponent(((HeroApplication) getApplication()).getBasicComponent()).build().inject(this ); } @Override protected void onResume () { super .onResume(); } }
上面的依赖,生成的代码里需要传入BasicComponent,实际上就是从basicComponent里取出来对MainActivity的@Inject成员变量赋值。因为我们在Application里已经初始化了BasicComponent,所以这里直接取,这里就可以说明@Singleton并没有 为我们做什么,生命周期是由我们自己控制的。为什么这里dependencies
可以用,因为ActivityScope和Singleton名字不一样,仅此而已。所以说,层级关系,是逻辑上的,实际上的控制需要我们自己实现。为什么在BasicComponent里我们需要暴露一些方法返回OkHttpClient等等?
This is actually an important property of how components work in Dagger: they do not expose types from their modules unless you explicitly make them available
Subcomponent
@Subcomponent
同样可以实现上面的效果,@Subcomponent
和dependencies
的区别有点像继承和组合,Subcomponent可以使用父Component的全部module资源,不需要在父Component里声明一些方法(像getOkHttpClient()这种) 插入一段文章: The main difference between them is an objects graph sharing. Subcomponents have access to entire objects graph from their parents while Component dependency gives access only to those which are exposed in Component interface.
以Activity和Fragment的关系为例
.
|-- HeroApplication.java
|-- common
| `-- ActivityScope.java
`-- subcomponentdemo
|-- ChildFragment.java
|-- ChildFragmentComponent.java
|-- SecondActivity.java
|-- SecondActivityComponent.java
`-- SecondActivityModule.java
用法比较奇怪,@Subcomponent修饰的类,需要在他的父Component上添加方法返回Subcomponent
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 @Module public class SecondActivityModule { @Provides @ActivityScope public Random provideRandom () { return new Random(); } } @ActivityScope @Component (modules = {SecondActivityModule.class }) public interface SecondActivityComponent { ChildFragmentComponent simpleComponent () ; } public class SecondActivity extends AppCompatActivity { private SecondActivityComponent secondActivityComponent; @Override protected void onCreate (@Nullable Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.second_activity); secondActivityComponent = DaggerSecondActivityComponent.builder().build(); getSupportFragmentManager().beginTransaction().replace(R.id.main, new ChildFragment()).commit(); } public SecondActivityComponent getSecondActivityComponent () { return secondActivityComponent; } }
同样的还是得在Activity里添加Component的get方法,在ChildFragment里@Inject注入就可以了,但是Dagger2不会生成DaggerChildFragmentComponent这样的类,ChildFragmentComponent
需要从附着的Activity上的SecondActivityComponent身上获取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Subcomponent public interface ChildFragmentComponent { void inject (ChildFragment fragment) ; } public class ChildFragment extends Fragment { @Inject Random random; @Override public void onAttach (Activity activity) { super .onAttach(activity); ((SecondActivity) activity).getSecondActivityComponent().simpleComponent().inject(this ); Preconditions.checkNotNull(random); Log.i("TAG" , "random:" + random); } @Nullable @Override public View onCreateView (LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.child_fragment, container, false ); } }
Lazy和Provider
Lazy有点类似懒汉模式的加载,他将创建对象延迟到第一次调用 ,Provider每次get得到的值都是不一样的。 TODO: 后面有更高级的用法再补
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class MainActivity extends AppCompatActivity { @Inject Lazy<PersonController> mPersonProvider; @Inject Provider<NuclearController> mNuclearProvider; @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerMainActivityComponent.builder().mainModule(new MainModule(10000 )).build().inject(this ); mPersonProvider.get().output(); NuclearController nuclearController1 = mNuclearProvider.get(); NuclearController nuclearController2 = mNuclearProvider.get(); Log.i("TAG" ,(nuclearController1==nuclearController2)+"" ); } }
参考链接
我把自己学习的结论写在这里,理解这个过程花了不少时间,建议学习的过程还是看看源码。
Making a Best Practice App #4 — Dagger 2 Dagger2从入门到放弃再到恍然大悟 Dependency injection with Dagger 2 - Custom scopes Dependency Injection with Dagger 2
注意事项
dagger2要注意声明的component生命周期问题,需要自己控制
Component里定义的方法名,不要纠结inject名字,可以随意起。是否要注入(void inject(Activity activity);)取决于你是否在用的地方(Activity)使用@Inject标注了变量