前言:
Dagger是由Square公司开发的一款开源的依赖注入工具,现在由Google接手进行开发和维护。
使用
- 引入依赖
implementation 'com.google.dagger:dagger:2.16'
implementation 'com.google.dagger:dagger-compiler:2.16'
//Dagger2用到了javax.annotation中的@Generated注解
compileOnly 'org.glassfish:javax.annotation:10.0-b28'
简单示例
例如有一个LogUtil工具类,需要在Activity中使用
public class LogUtil {
private static final String TAG = "LogUtil";
public LogUtil() {
Log.e(TAG, "new LogUtil()");
}
public void print() {
Log.e(TAG, "LogUtil:" + toString());
}
}
常规方法是在Activity中直接new出来
LogUtil logUtil = new LogUtil();
logUtil.print();
使用Dagger时,首先需要新建LogUtil类的提供者,@Module标注的对象类似工厂,向外提供其他类的对象。@Provides标注的方法就是提供对象的。
@Module
public class ActivityModule {
private static final String TAG = "ActivityModule";
@Provides
LogUtil provideLogUtil() {
Log.e(TAG, "Provide LogUtil without Context.");
return new LogUtil();
}
}
接着在需要使用LogUtil的地方加上@Inject注解
public class MainActivity extends Activity {
@Inject
LogUtil mLogUtil;
......
}
接下来需要一个Component容器, Module中产出的东西都放在里面。将Component与要注入的MainActivity做关联,MainActivity中需要的LogUtil 就可以从Component中取出。
@Component(modules = {ActivityModule.class})
public interface ActivityComponent {
void inject(MainActivity activity);
}
这个时候build一下工程,将 MainActivity和Module通过Component关联起来。DaggerActivityComponent是build的时候,Dagger2自己生成的具体的Component类(自己定义的是ActivityComponent 接口)
public class MainActivity extends Activity {
......
@Override
protected void onCreate(Bundle savedInstanceState) {
......
ActivityComponent component = DaggerActivityComponent.builder()
.activityModule(new ActivityModule())
.build();
component.inject(this);
}
......
}
到此,就可以在MainActivity 中使用注入的对象LogUtil 了。可以依次看到如下log。
E/ActivityModule: Provide LogUtil without Context.
E/LogUtil: new LogUtil()
E/LogUtil: LogUtil:com.example.admin.studytest.LogUtil@6eb57e0
有参依赖
上面的简单示例中,LogUtil是无参构造,如果是有参构造,使用方法则有所区别。为LogUtil添加有参构造:
......
private Context mContext;
public LogUtil(Context context) {
mContext = context;
Log.e(TAG, "new LogUtil()" + " Context:" + mContext.toString());
}
......
修改ActivityModule,其中provideLogUtilWithContext(Context context)中的context,不能直接使用成员变量this.context,要另外提供一个Context providesContext()的@Provides方法,这样在发现需要Context 的时候会调用 provideContext()获取。
······
@Provides
LogUtil provideLogUtilWithContext(Context context) {
Log.e(TAG, "Provide LogUtil with Context.");
return new LogUtil(context);
}
private Context context;
public ActivityModule(Context context) {
this.context = context;
}
@Provides
public Context providesContext() {
return this.context;
}
······
组件依赖
假设有一个AppModule提供Context对象,ActivityModule依赖AppModule,然后从中获取Context 对象。
@Module
public class AppModule {
private Context context;
public AppModule(Context context) {
this.context = context;
}
@Provides
public Context providesContext() {
return this.context;
}
}
@Component(modules = AppModule.class)
public interface AppComponent {
Context getContext();
}
ActivityModule 只留下LogUtil 的@Provides即可。
@Module
public class ActivityModule {
private static final String TAG = "ActivityModule";
@Provides
LogUtil provideLogUtilWithContext(Context context) {
Log.e(TAG, "Provide LogUtil with Context.");
return new LogUtil(context);
}
@Provides
LogUtil provideLogUtil() {
Log.e(TAG, "Provide LogUtil without Context.");
return new LogUtil();
}
}
ActivityComponent添加dependencies字段。
@Component(dependencies = {AppComponent.class}, modules = {ActivityModule.class})
public interface ActivityComponent {
void inject(MainActivity activity);
}
在Application中创建后
public class APP extends Application {
private static ActivityComponent mActivityComponent;
private static APP instance;
public static synchronized APP getInstance() {
return instance;
}
@Override
public void onCreate() {
super.onCreate();
instance = this;
AppComponent appComponent = DaggerAppComponent.builder()
.appModule(new AppModule(this))
.build();
mActivityComponent = DaggerActivityComponent.builder()
.appComponent(appComponent)
.activityModule(new ActivityModule())
.build();
}
public ActivityComponent getActivityComponent() {
return mActivityComponent;
}
}
Activity可以通过APP对象来使用其中的ActivityComponent
APP.getInstance().getActivityComponent().inject(this);
限定标记
前面为LogUtil加入了有参构造,在依赖注入的时候,如何区分使用哪个?此处有两种方案。
@Named("...")
为ActivityModule 的@Provides方法添加@Named("...")注解
@Module
public class ActivityModule {
private static final String TAG = "ActivityModule";
@Provides
@Named("provideLogUtilWithContext")
LogUtil provideLogUtilWithContext(Context context) {
Log.e(TAG, "Provide LogUtil with Context.");
return new LogUtil(context);
}
@Provides
@Named("provideLogUtil")
LogUtil provideLogUtil() {
Log.e(TAG, "Provide LogUtil without Context.");
return new LogUtil();
}
}
注入时再次使用相同注解即可区分
@Inject
@Named("provideLogUtilWithContext")
LogUtil mLogUtil;
@Inject
@Named("provideLogUtil")
LogUtil mLogUtil1;
@Qualifier
定义如下两个注解
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface LogUtilWithContext {
}
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface LogUtilWithoutContext {
}
为ActivityModule 的@Provides方法添加自定义注解
@Module
public class ActivityModule {
private static final String TAG = "ActivityModule";
@LogUtilWithContext
@Provides
LogUtil provideLogUtilWithContext(Context context) {
Log.e(TAG, "Provide LogUtil with Context.");
return new LogUtil(context);
}
@LogUtilWithoutContext
@Provides
LogUtil provideLogUtil() {
Log.e(TAG, "Provide LogUtil without Context.");
return new LogUtil();
}
}
同样的,注入时再次使用相同注解即可区分
@Inject
@LogUtilWithContext
LogUtil mLogUtil;
@Inject
@LogUtilWithoutContext
LogUtil mLogUtil1;
单例模式和自定义生命周期
Dagger2中提供了@Singleton注解来实现单例,但需要注意的是,这个单例跟Component相关联,即@Singleton注解后的@Provides方法,在同一个Component对象中多次注入返回同一个对象。
@Module
public class ActivityModule {
private static final String TAG = "ActivityModule";
@LogUtilWithContext
@Provides
@Singleton
LogUtil provideLogUtilWithContext(Context context) {
Log.e(TAG, "Provide LogUtil with Context.");
return new LogUtil(context);
}
@LogUtilWithoutContext
@Provides
@Singleton
LogUtil provideLogUtil() {
Log.e(TAG, "Provide LogUtil without Context.");
return new LogUtil();
}
}
@Singleton
@Component(dependencies = {AppComponent.class}, modules = {ActivityModule.class})
public interface ActivityComponent {
void inject(MainActivity activity);
}
在Activity中使用
@Inject
@LogUtilWithContext
LogUtil mLogUtil;
@Inject
@LogUtilWithContext
LogUtil mLogUtil1;
@Inject
@LogUtilWithContext
LogUtil mLogUtil2;
@Inject
@LogUtilWithoutContext
LogUtil mLogUtil3;
@Inject
@LogUtilWithoutContext
LogUtil mLogUtil4;
@Inject
@LogUtilWithoutContext
LogUtil mLogUtil5;
调用print()方法后,发现只分别生成了一个空参和有参的对象。
LogUtil:com.example.admin.studytest.LogUtil@a59a137
LogUtil:com.example.admin.studytest.LogUtil@a59a137
LogUtil:com.example.admin.studytest.LogUtil@a59a137
LogUtil:com.example.admin.studytest.LogUtil@6eb57e0
LogUtil:com.example.admin.studytest.LogUtil@6eb57e0
LogUtil:com.example.admin.studytest.LogUtil@6eb57e0
查看@Singleton的源码,发现@Scope注解,这个就是Dagger2中的自定义生命周期,可以自定义一个@Scope来实现与@Singleton相同的效果。
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}
自定义一个@ActivityScope
@Scope
@Documented
@Retention(RUNTIME)
public @interface ActivityScope {}
和@Singleton同样的使用方法
@Module
public class ActivityModule {
private static final String TAG = "ActivityModule";
@LogUtilWithContext
@Provides
@ActivityScope
LogUtil provideLogUtilWithContext(Context context) {
Log.e(TAG, "Provide LogUtil with Context.");
return new LogUtil(context);
}
@LogUtilWithoutContext
@Provides
@ActivityScope
LogUtil provideLogUtil() {
Log.e(TAG, "Provide LogUtil without Context.");
return new LogUtil();
}
}
@ActivityScope
@Component(dependencies = {AppComponent.class}, modules = {ActivityModule.class})
public interface ActivityComponent {
void inject(MainActivity activity);
}
打印日志的结果也与@Singleton相同
LogUtil:com.example.admin.studytest.LogUtil@a59a137
LogUtil:com.example.admin.studytest.LogUtil@a59a137
LogUtil:com.example.admin.studytest.LogUtil@a59a137
LogUtil:com.example.admin.studytest.LogUtil@6eb57e0
LogUtil:com.example.admin.studytest.LogUtil@6eb57e0
LogUtil:com.example.admin.studytest.LogUtil@6eb57e0
以上就是Dagger2的简易指北。