深入浅出Dagger2 : 从入门到爱不释手
Dagger2是什么?
Dagger2是Dagger的升级版,是一个依赖注入框架,第一代由大名鼎鼎的Square公司共享出来,第二代则是由谷歌接手后推出的,现在由Google接手维护.
那么,什么是依赖注入?
依赖注入是面向对象编程的一种设计模式,其目的是为了降低程序耦合,这个耦合就是类之间的依赖引起的.
举个栗子:
public class ClassA{
private ClassB b
public ClassA(ClassB b){
this.b = b }
}
这里ClassA的构造函数里传了一个参数ClassB,随着后续业务增加也许又需要传入ClassC,ClassD.试想一下如果一个工程中有5个文件使用了ClassA那是不是要改5个文件?
这既不符合开闭原则, 也太不软工了.这个时候大杀器Dagger2就该出场了.
public class ClassA{
@inject
private ClassB b
public ClassA(){
}
}
通过注解的方式将ClassB b注入到ClassA中, 可以灵活配置ClassA的属性而不影响其他文件对ClassA的使用.
如何使用Dagger2
下面就跟着我的节奏来在实际项目中体验一下Dagger2.我手头有一个MVP模式的地图导航的Demo, 我们就来使用Dagger2改造它.
1.导入
compile"com.google.dagger:dagger:2.14.1"
annotationProcessor"com.google.dagger:dagger-compiler:2.14.1"
咦?一般的第三方库像Glide,okhttp不都是一行代码引入吗?这里怎么多了一个annotationProcessor?估计用过其他注解框架比如Butterknife,EventBus的同学已经了然了,小白也不要急,此处且留个悬念.
2.定义一个注解ActivityScope, 创建一个接口CommonComponent, 创建一个类CommonModule
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}
@ActivityScope
@Component (modules = CommonModule.class)
public interface CommonComponent {
void inject(GdmapActivity activity); //GdmapActivity是导航Demo的一个Activity
}
@Module
public class CommonModule{
}
@Scope和@Retention是元注解, 大家应该是了解的, @Component@Module是Dagger2定义的注解. 那这三个文件分别是干啥用的呢?先带着这个问题, 我们一边用一边去理解.
3.使用我们定义的Module和Component开始改造导航Demo
Demo中之前有个Presenter长这样(prenter中具体的业务逻辑先忽略, 后面会附上完整Demo的git地址)
public class Gpresenter {
private ILocateView view;
private Application application;
private RxErrorHandler rxErrorHandler;
/**定位类对象*/
private AMapLocationClient mLocationClient;
/**逆地理编码类对象*/
private GeocodeSearch mGeocodeSearch;
/**地理围栏类对象*/
private GeoFenceClient fenceClient;
public Gpresenter(Application application,
ILocateView view,
RxErrorHandler rxErrorHandler,
AMapLocationClient mLocationClient,
GeocodeSearch mGeocodeSearch,
GeoFenceClient fenceClient) {
this.view = view;
this.application = application;
this.rxErrorHandler = rxErrorHandler;
this.mLocationClient = mLocationClient;
this.mGeocodeSearch = mGeocodeSearch;
this.fenceClient = fenceClient;
}
改造前在Activity中初始化这个Gpresenter:
public class GdmpActivity extends GdBaseActivity implements ILocateView {
protected Gdpresenter gdpresenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initPresnter();
}
private void initPresnter(){
RxErrorHandler handler = RxErrorHandler.builder().with(getApplication());
responseErrorListener(ResponseErrorListener.EMPTY).build();
AMapLocationClient client =new AMapLocationClient(this);
GeocodeSearch codeSearch =new GeocodeSearch(this);
GeoFenceClient fenceClient =new GeoFenceClient(this);
gdpresenter =new
Gdpresenter(getApplication(),this,handler,client,codeSearch,fenceClient);
}
}
现在我们把Gpresenter初始化的事交给Dagger2去做.优雅的省略GdmpActivity中初始化Gdpresenter的过程.
先使用我们刚才定义的CommonModule :
@Module
public class CommonModule{
private ILocateView view;
public CommonModule(ILocateView view){
this.view = view;
}
@ActivityScope
@Provides
public ILocateViewprovideIView(){
return this.view;
}
@ActivityScope
@Provides
public Application provideApplication(){
return view.getActivity().getApplication();
}
@ActivityScope
@Provides
public RxPermissions provideRxPermission(){
return new RxPermissions(view.getActivity());
}
@ActivityScope
@Provides
public AMapLocationClient provideAMapLocationClient(){
return new AMapLocationClient(view.getActivity());
}
@ActivityScope
@Provides
public GeocodeSearch provideGeocodeSearch(){
return new GeocodeSearch(view.getActivity());
}
@ActivityScope
@Provides
public GeoFenceClientgetGeoFenceClient(){
return new GeoFenceClient(view.getActivity().getApplicationContext());
}
@ActivityScope
@Provides
public RxErrorHandlerprovideRxErrorHandler(Application application ){
return RxErrorHandler.builder().with(application).
responseErrorListener(ResponseErrorListener.EMPTY).build();
}
解释一下, 我们自定义的Annotation @ActivityScope指定作用域, ActivityScope 的元注解@Retention(RetentionPolicy.RUNTIME)指定了它是运行时注解,就是说注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在.
@Provides是Dagger2定义的Annotation(待深挖)
同时给Gpresnter加上注释 @Inject
@Inject
public Gpresenter(Application application, ILocateView view, RxErrorHandler rxErrorHandler, AMapLocationClient mLocationClient,GeocodeSearch mGeocodeSearch,
GeoFenceClient fenceClient)
现在build一下,看看项目的build-->generate-->source-->apt目录下面都有什么:
image生成了很多CommonModule开头的.class文件,还有DaggerCommonComponent,这都是哪来的?
在build的过程中,APT(就是dagger-compiler)扫描到注解(@Component@Module)生成的.这个过程用下面这张图表示:
image.png
该最后一步了: 把Gpresenter初始化的事交给Dagger2去做.在GdmapActivity中给Gpresenter加上@Inject标签, 同时删掉Gpresenter初始化过程
public class GdmapActivity implements ILocateView {
@Inject
protected Gdpresenter gdpresenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerCommonComponent.builder().
commonModule(new CommonModule(this)).
build().inject(this);
}
}
是不是清爽了很多?
那么,Dagger2是如何工作的?
生成的*.class文件里面主要做了什么?
这些文件时如何生成的呢?
且等后续
参考博客:
Dagger2:
https://www.jianshu.com/p/22c397354997
https://www.jianshu.com/p/39d1df6c877d
APT:
https://www.jianshu.com/p/1910762593be
https://blog.csdn.net/Ru_Zhan/article/details/78232851?locationNum=8&fps=1