参考资料:
https://www.jianshu.com/p/1d84ba23f4d2
https://mp.weixin.qq.com/s/lh3dgJK95cgbG-bUZfvbFA
听说好多开发者采用Dagger2+RxJava+Retrofit+mvp结构来进行开发,虽然Dagger2已经出来好久了,但一直没有进行总结,今天所以有必要总结一下Dagger2。
Dagger中文意思是匕首,Dagger2是Dagger的第二个版本,之前的Dagger已经放弃维护了,Dagger2用官方的话来说就是:
我们先看看官网怎么说:
Android Gradle
// Add Dagger dependencies
dependencies {
compile 'com.google.dagger:dagger:2.x'
annotationProcessor 'com.google.dagger:dagger-compiler:2.x'
}
If you're using classes in dagger.android you'll also want to include:
compile 'com.google.dagger:dagger-android:2.x'
compile 'com.google.dagger:dagger-android-support:2.x' // if you use the support libraries
annotationProcessor 'com.google.dagger:dagger-android-processor:2.x'
If you're using classes in dagger.android you'll also want to include:这句话的意思是:如果你要使用dagger.android里面的东西。你就需要添加dagger-android:xxx依赖。
我们都知道Android应用使用Dagger最主要的困难就是一些Framework类(如Activity、Fragment)是由操作系统实例化的,而Dagger更好工作的前提是它可以构建所有的注入对象。所以,你只能在生命周期方法中进行成员变量注入,这样就会产生两个问题:
虽然仅仅添加dagger-2x也可以实现依赖注入,但如果添加了dagger-android2.x的依赖就可以避免上面提到的问题了。
//Dagger2相关依赖
implementation 'com.google.dagger:dagger:2.24'
annotationProcessor 'com.google.dagger:dagger-compiler:2.24'
implementation 'com.google.dagger:dagger-android:2.24'
// if you use the support libraries
implementation 'com.google.dagger:dagger-android-support:2.24'
annotationProcessor 'com.google.dagger:dagger-android-processor:2.24'
Dagger主要是以下几个注入标签,我们来一一说明:
public class PhotoUtil {
private static final String TAG = "PhotoUtil";
/**
* 拍照
* @param activity
*/
public void takePhoto(Activity activity){
//执行拍照的代码
Log.d(TAG, "takePhoto:调用拍照方法");
}
}
MainActivity直接调用:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
PhotoUtil photoUtil =new PhotoUtil();
photoUtil.takePhoto(this);
}
那我们看一个用Dagger2如何实现?
首先我们要先创建一个Module,我们上面说了Module 类里面的方法专门提供返回对象依赖,所以我们定义一个类,用@Module 注解,这样 Dagger 在构造类的实例时候,就知道从哪里去找到需要的依赖。我们取名为PhotoMudule.
相关代码:
@Module //Module 注解类里面的方法专门提供依赖,所以我们定义一个类,用@Module 注解
public class PhotoMudule {
//module类中要提供依赖的注解方法用@Provides注解声明,以此来告诉Dagger要构造对象并提供这些依赖
@Provides
public PhotoUtil photoInstance (){
return new PhotoUtil();
}
}
接下来用在modules中,我们定义的方法用@Provides注解,以此来告诉Dagger我们想要构造对象并提供这些依赖,注意:用@Provides注解的方法必须是public,不然外部怎么访问。
我们上面提到了Component说它的作用主要是桥梁,那我们现在就来把Component和Module关联,取名photoComponent,代码如下:
//通过@Component注解来绑定我们的PhotoMudule(从{}这个符号我们就可以看到,他可以同时依赖多个 module)
@Component(modules = {PhotoMudule.class})
public interface photoComponent {
//定义inject方法,参数是MainActivity,因为我们想在这个类中使用我们实例PhotoUtil
void inject(MainActivity mainActivity);
}
注意:Component注解的类是一个接口
注解写的都很明白,这样 module 和 MainActivity 通过 Component 就关联起来了,切记我们还要执行以下Rebuild Progect,。
在MainActivity直接调用:
@Inject
PhotoUtil photoUtil;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//一般写法
// PhotoUtil photoUtil =new PhotoUtil();
// photoUtil.takePhoto(this);
//加入dagger2写法
DaggerPhotoComponent.create().inject(MainActivity.this);
photoUtil.takePhoto(MainActivity.this);
}
注意 这里我们 @Inject注解的对象不能用 privite 修饰。在我们的想要的创建的对象上加@Inject 注解并且调用 DaggerPhotoComponet.create().inject(this)关联; 后我们就可以拿到这个对象的实例了。
结果打印:
可以看到我们调用相关方法成功,但我们要思考一个问题,这样并不能很好的解耦,什么意思呢,我们来思考个问题?
public class PhotoClsManager {
PhotoUtil photoUtil;
Activity mActivity;
public PhotoClsManager(Activity activity,PhotoUtil photoUtil) {
this.photoUtil = photoUtil;
this.mActivity=activity;
}
public void startMethod(){
//执行拍照方法
photoUtil.takePhoto(mActivity);
}
}
假如我们调用完拍照还需要调用裁剪方法,我们先创建相关类:
public class PhotoTailor {
private static final String TAG = "PhotoTailor";
/**
*
* @param photoUrl
*/
public void photoTailor(String photoUrl){
//执行裁剪的代码
Log.d(TAG, "photoTailor:调用裁剪相关方法传入参数+"+photoUrl);
}
}
那么我们的管理类就要做相关的变动,加入裁剪类相关代码:
public class PhotoClsManager {
PhotoUtil photoUtil;
Activity mActivity;
PhotoTailor photoTailor;
String photoUrl;
public PhotoClsManager(Activity activity,PhotoUtil photoUtil,PhotoTailor photoTailor,String photoUrl) {
this.photoUtil = photoUtil;
this.mActivity=activity;
this.photoTailor = photoTailor;
this.photoUrl=photoUrl;
}
public void startMethod(){
//执行拍照方法
photoUtil.takePhoto(mActivity);
//执行裁剪相关方法
photoTailor.photoTailor(photoUrl);
}
}
PhotoClsManager这么写也有个问题,如果PhotoClsManager中的类需要好多参数,那我岂不是要在PhotoClsManager中的构造方法中写好多参数,我们说了PhotoClsManager只是管理相关类,并不负责类中方法需要的参数,那么这些参数要怎么传递呢?通常我们都是在PhotoClsManager管理类中的具体类的构造方法中执行传参操作,那么上面的代码就可以被我们改为:
public class PhotoUtil {
private static final String TAG = "PhotoUtil";
Context context;
public PhotoUtil(Context context) {
this.context = context;
}
/**
* 拍照
*/
public void takePhoto(){
//执行拍照的代码
Log.d(TAG, "takePhoto:调用拍照方法,接受的参数是:"+context);
}
}
public class PhotoTailor {
private static final String TAG = "PhotoTailor";
String photoUrl ;
public PhotoTailor(String photoUrl) {
this.photoUrl = photoUrl;
}
/**
* 裁剪方法
*/
public void photoTailor(){
//执行裁剪的代码
Log.d(TAG, "photoTailor:调用裁剪相关方法传入参数+"+photoUrl);
}
}
那么PhotoClsManager中的代码也需要调整了
public class PhotoClsManager {
PhotoUtil photoUtil;
PhotoTailor photoTailor;
public PhotoClsManager(PhotoUtil photoUtil,PhotoTailor photoTailor) {
this.photoUtil = photoUtil;
this.photoTailor = photoTailor;
}
public void startMethod(){
//执行拍照方法
photoUtil.takePhoto();
//执行裁剪相关方法
photoTailor.photoTailor();
}
}
那我们来看看PhotoMudule类中要怎么写:
首先我们在PhotoMudule中传入需要的参数,要通过PhotoMudule的构造参数来写:
@Module //Module 注解类里面的方法专门提供依赖,所以我们定义一个类,用@Module 注解
public class PhotoMudule {
Context context;
String photoUrl;
public PhotoMudule(Context context, String photoUrl) {
this.context = context;
this.photoUrl = photoUrl;
}
//module类中要提供依赖的注解方法用@Provides注解声明,以此来告诉Dagger要构造对象并提供这些依赖
@Provides
public PhotoUtil photoInstance (){
return new PhotoUtil(context);
}
@Provides
public PhotoTailor photoTailorInstance (){
return new PhotoTailor(photoUrl);
}
@Provides
public PhotoClsManager providePhotoClsManager(PhotoUtil photoUtil, PhotoTailor photoTailor) {
return new PhotoClsManager(photoUtil, photoTailor);
}
MainActivity调用:
执行结果:
假设我们拍照之后裁剪之后还需要上传,所以我们创建一个上传类
public class PhotoUp {
@Inject
public PhotoUp() {
}
@Override
public String toString() {
return "调用了图片上传方法";
}
}
这次我们创建的方式和之前有点不一样了,我们直接在构造函数上声明了@Inject注解,这个注解有什么用呢?当Component在所拥有的Module类中找不到依赖需求方需要类型的提供方法时,Dagger2就会检查该需要类型的有没有用@Inject声明的构造方法,有则用该构造方法创建一个,注意:这次我没是没有在Module类中写返回PhotoUp 类实例的方法的。
调用:
//@Inject
//PhotoUtil photoUtil;
@Inject
PhotoClsManager photoClsManager;
@Inject
PhotoUp photoUp;
String photoUrl ="android/xxx.com/1123.jpg";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//一般写法
// PhotoUtil photoUtil =new PhotoUtil();
// photoUtil.takePhoto(this);
//加入dagger2写法
// DaggerPhotoComponent.create().inject(MainActivity.this);
// photoUtil.takePhoto(MainActivity.this);
DaggerPhotoComponent.builder().photoMudule(new PhotoMudule(MainActivity.this,photoUrl))
.build().inject(this);
photoClsManager.startMethod();
Log.d(TAG, "onCreate:"+photoUp);
}
结果:
那么又有小伙伴要问了,后一种直接在构造方法上添加 @Inject注解要方便好多,为什么还需要@Module注解呢?
答:
项目中我们会用到别人的jar包,我们无法修改别人的源码,就更别说在人家的类上添加注解了,所以我们只能通过Module类来提供
总结:
我们有两种方式可以提供依赖,一个是注解了@Inject的构造方法,一个是在Module里提供的依赖,规则是这样的:查找Module中是否存在创建该类的方法,如果没有则查找Inject注解的构造函数
说完了基本使用,我们来看看Dagger2高级使用的注解
上面我们模拟了图片的选取-裁剪-上传通过注解如何获取依赖对象,然后调用相关方法,那么现在假设我要在图片本身做分类,将图片类型分为人物,景色等呢?
首先我们先创建Photo类:
public class Photo {
String photoType;
public String getPhotoType() {
return photoType;
}
public void setPhotoType(String photoType) {
this.photoType = photoType;
}
@Override
public String toString() {
return"选取了"+photoType;
}
}
然后在我们的Module类PhotoMudule中增加两个获取不同类别照片的方法:
/**
* 获取人物图片对象
* @return
*/
@Provides
public Photo getfigurePhoto(){
Photo photo = new Photo("人物图片");
return photo;
}
/**
* 获取景物图片对象
* @return
*/
@Provides
public Photo getsceneryPhoto(){
Photo photo = new Photo("景物图片");
return photo;
}
这个时候我们的问题就来了,Dagger2是通过返回值类型来确定的,当你需要人物图片时,它又怎么知道哪个是人物图片呢?
答:还好有@Named注解。
@Named注解有一个value值,用来标识这个方法是给谁用的.修改我们的代码:
/**
* 获取人物图片对象
* @return
*/
@Provides
@Named("figure")
public Photo getfigurePhoto(){
Photo photo = new Photo("人物图片");
return photo;
}
/**
* 获取景物图片对象
* @return
*/
@Provides
@Named("scenery")
public Photo getsceneryPhoto(){
Photo photo = new Photo("景物图片");
return photo;
}
调用:注意在 Moudle 用了@Named 标签,在调用时也需要加上@Named 标签
@Inject
@Named(value = "figure")
Photo figurePhoto;
@Inject
@Named(value = "scenery")
Photo sceneryPhoto;
//省略:xxxx
DaggerPhotoComponent.builder().photoMudule(new PhotoMudule(MainActivity.this,photoUrl))
.build().inject(this);
Log.d(TAG, "figurePhoto:"+figurePhoto);
Log.d(TAG, "sceneryPhoto:"+sceneryPhoto);
结果:
@Qulifier功能和@Named一样,并且@Named就是继承@Qulifier的,没错,@Qulifier就是自定义注解用的。接下来我们定义一个@CustomeQualifier来替代@Named:
//自定义注解名为:CustomeQualifier
//@Qualifier :注明是Qualifier(关键词)
// @Documented :标记在文档(可不写)
// @Retention(RUNTIME) :运行时级别
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomeQualifier {
String value() default "";
}
/**
* 采用Qualifier自定义注解实现标记区分
* @return
*/
@Provides
@CustomeQualifier("scenery")
public Photo customeQualifierPhoto(){
Photo photo = new Photo("景物图片");
return photo;
}
//执行相关代码
@Inject
@PhotoMudule.CustomeQualifier(value = "scenery")
Photo customeQualifier;
Log.d(TAG, "customeQualifier:"+customeQualifier);
学习@Singleton注解之前,我们先想这么一个需求,有没有一个依赖类以另外一个类作为依赖类呢?比如,我的裁剪对象需要一个图片对象,我们先在PhotoMudule中写一下相关代码:
@Provides
@Named("phototailorbyphoto")
public PhotoTailor photoTailorByPhoto (){
Photo photo =new Photo("景物图片");
return new PhotoTailor(photo);
}
但是这么写有点麻烦,Dagger2提供了这样的功能,我们只要在photoTailorByPhoto 方法中添加Photo 参数,Dagger2就会像帮依赖需求方找依赖对象一样帮你找到该方法依赖的Photo 实例,所以我们代码可以这样改:
@Provides
public Photo getPhoto(){
return new Photo("景物图片");
}
@Provides
@Named("phototailorbyphoto")
public PhotoTailor photoTailorByPhoto (Photo photo){
return new PhotoTailor(photo);
}
打印:
关于@Singleton ,我们需要接着上面的例子看:
@Provides
public Photo getPhoto(){
return new Photo("景物图片");
}
@Provides
@Named("phototailorbyphoto")
public PhotoTailor photoTailorByPhoto (Photo photo){
return new PhotoTailor(photo);
}
@Inject
Photo photo;
@Inject
@Named("phototailorbyphoto")
PhotoTailor photoTailor;
Log.d(TAG, "photo:" + photo);
Log.d(TAG, "photoTailor:" + photoTailor);
Log.d(TAG, "photo和photoTailor.getPhoto()是一个photo吗?" + (photoTailor.getPhoto() == photo));
打印:
为什么会出现这种情况呢?
答:,注入过程中,对Photo注入时会调用一次getPhoto方法,创建了一个Photo对象;注入PhotoTailor 时又会调用一次getPhoto方法,这时又会创建一个Cloth对象,所以才会出现上面的结果,那如果我们想Photo和PhotoTailor 中的是一个Photo改怎么办呢?这就要用到@Singleton注解了,顾名思义@Singleton字面意思就是声明单例模式。怎么用呢?改两个地方:Module中获取实例的方法和Component都加上@Singleton,代码如下:
@Singleton
@Provides
public Photo getPhoto(){
return new Photo("景物图片");
}
@Singleton
@Component(modules = {PhotoMudule.class})
public interface PhotoComponent {
//定义inject方法,参数是MainActivity,因为我们想在这个类中使用我们实例PhotoUtil
void inject(MainActivity mainActivity);
}
@Singleton在使用时调用处正常书写:
@Inject
Photo photo;
@Inject
PhotoToTailor photoToTailor;
Log.d(TAG, "photo:" + photo);
Log.d(TAG, "photoTailor:" + photoTailor);
Log.d(TAG, "photo和photoTailor.getPhoto()是一个photo吗?" + (photoTailor.getPhoto() == photo));
打印:
小总:
@Scope就是用来声明作用范围的.@Scope和@Qulifier一样,需要我们自定义注解才能使用,我们先自定义一个注解:
//定义@Scope注解名为:CustomeScope
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomeScope {
}
这个自定义注解的作用是:声明作用范围,当我们将这个注解使用在Module类中的Provide方法上时,就是声明这个Provide方法是在CustomeScope 作用范围内的,并且当一个Component要引用这个Module时,必须也要声明这个Component是CustomeScope 作用范围内的,否则就会报错,声明方法也很简单,就是在Component接口上使用这个注解。那我们把之前的@Singleton替换一下看看效果:
//@Singleton
@CustomeScope
@Provides
public Photo getPhoto(){
return new Photo("景物图片");
}
//定义@Scope注解名为:CustomeScope,作用相当于@Singleton
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomeScope {
String value() default "";
}
//@Singleton
@PhotoMudule.CustomeScope
@Component(modules = {PhotoMudule.class})
public interface PhotoComponent {
//定义inject方法,参数是MainActivity,因为我们想在这个类中使用我们实例PhotoUtil
void inject(MainActivity mainActivity);
}
//调用执行:
@Inject
Photo photo;
@Inject
@Named("phototailorbyphoto")
PhotoTailor photoTailor;
Log.d(TAG, "photo:" + photo);
Log.d(TAG, "photoTailor:" + photoTailor);
Log.d(TAG, "photo和photoTailor.getPhoto()是一个photo吗?" + (photoTailor.getPhoto() == photo));
打印:
那么为什么这么做也可以实现Activity级别的单例效果呢?
答:
Dagger2有这样一个机制:在同一个作用范围内,Provide方法提供的依赖对象就会变成单例,也就是说依赖需求方不管依赖几次Provide方法提供的依赖对象,Dagger2都只会调用一次这个方法.
我们刚才通过自定义@CustomeScope或者@Singleton的方法都实现了Photo对象是同一个,但现在我们要创建一个单例的工具类PhotoToTailor,目的是将我们的Photo通过该工具类直接转化为PohtoTailor,相关代码如下:
MainActivity相关代码:
创建PhotoToTailor类:
public class PhotoToTailor {
public PhotoTailor getPhotoTailor(Photo photo){
return new PhotoTailor(photo);
}
}
//PhotoMudule中的代码:
@Singleton
@Provides
public Photo getPhoto(){
return new Photo("景物图片");
}
@Provides
@Named("phototailorbyphoto")
public PhotoTailor photoTailorByPhoto (Photo photo){
return new PhotoTailor(photo);
}
@Singleton
@Provides
public PhotoToTailor getPhotoToTailor(){
return new PhotoToTailor();
}
MainActivty界面代码:
@Inject
Photo photo;
@Inject
PhotoToTailor photoToTailor;
TextView tvOne;
DaggerPhotoComponent.builder().photoMudule(new PhotoMudule(MainActivity.this, photoUrl))
.build().inject(this);
tvOne.setText(photoToTailor.getPhotoTailor(photo) + "photoToTailor地址:" + photoToTailor);
/**
* 监听事件,跳转下一个页面
* @param v
*/
public void onClickListener(View v){
Intent intent =new Intent(MainActivity.this,SecondActivity.class);
startActivity(intent);
}
结果展示:
SecondActivity类内容:
@Inject
Photo photo;
@Inject
PhotoToTailor photoToTailor;
TextView textView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
textView =findViewById(R.id.textView);
DaggerSecondActivity_SecondeComponent.builder().secondModule(new SecondModule())
.build().inject(this);
textView.setText(photoToTailor.getPhotoTailor(photo) + "photoToTailor地址:" + photoToTailor);
}
@Module
public class SecondModule {
public SecondModule() {
}
@Singleton
@Provides
public Photo getPhoto(){
return new Photo("人物图片");
}
@Provides
@Named("phototailorbyphoto")
public PhotoTailor photoTailorByPhoto (Photo photo){
return new PhotoTailor(photo);
}
@Singleton
@Provides
public PhotoToTailor getPhotoToTailor(){
return new PhotoToTailor();
}
}
@Singleton
@Component(modules = {SecondModule.class})
public interface SecondeComponent {
void inject(SecondActivity secondActivity);
}
结果展示:
我们来对比一下:
你会发现,虽然我们成功的将PhotoToTailor 注入到了这两个Activity中,但是你会发现,这两个Activity中的PhotoTailor 实例不是一样的,为什么我们的Singleton失效了?而且假如有多个Activity都需要用到PhotoToTailor ,我么就会产生好多冗余代码,怎么办呢?
在面向对象的思想中,我们碰到这种情况一般都要抽取父类,Dagger2也是用的这种思想,我们先创建一个BaseModule,用来提供工具类:
@Module
public class BaseModule {
@Singleton //单例
@Provides
public PhotoToTailor getPhotoToTailor(){
return new PhotoToTailor();
}
}
在创建一个BaseComponent接口:
@Singleton //对应Module中声明的单例
@Component(modules = BaseModule.class)
public interface BaseComponent {
//它的作用就是告诉依赖于BaseComponent的Component,BaseComponent能为你们提供PhotoToTailor对象
PhotoToTailor getPhotoToTailor();
}
删除PhotoMudule和SecondModule中原有的photoTailorByPhoto方法:
@Module
public class PhotoMudule {
@Singleton
@Provides
public Photo getPhoto(){
return new Photo("景物图片");
}
@CustomeScope
@Provides
public PhotoToTailor getPhotoToTailor(){
return new PhotoToTailor();
}
}
@Module
public class SecondModule {
public SecondModule() {
}
@Singleton
@Provides
public Photo getPhoto(){
return new Photo("人物图片");
}
@CustomeScope
@Provides
public PhotoToTailor getPhotoToTailor(){
return new PhotoToTailor();
}
}
接下来在MainComponent和SecondComponent中声明依赖,就要用到@Component中的dependencies属性了:
@Singleton
@Component(modules = PhotoMudule.class,dependencies = BaseComponent.class)
public interface PhotoComponent {
//定义inject方法,参数是MainActivity,因为我们想在这个类中使用我们实例PhotoUtil
void inject(MainActivity mainActivity);
}
@Singleton
@Component(modules = SecondModule.class,dependencies = BaseComponent.class)
public interface SecondeComponent {
void inject(SecondActivity secondActivity);
}
执行Android Studio中build菜单下的Rebuild Object后,你会发现创建MainComponent和SecondComponent实例时多了一个baseComponent方法,
public class BaseApplication extends Application {
BaseComponent baseComponent;
@Override
public void onCreate() {
super.onCreate();
baseComponent = DaggerBaseComponent.builder().baseModule(new BaseModule()).build();
}
public BaseComponent getBaseComponent(){
return baseComponent;
}
}
继续修改MainActivity和SecondActivity的相关代码,注意我们在创建相关Component的时候多了.baseComponent()方法,其参数就是我们在Application中创建的BaseComponent对象,来确保全局单例。
DaggerPhotoComponent.builder()
.baseComponent(((BaseApplication) getApplication()).getBaseComponent())
.photoMudule(new PhotoMudule(MainActivity.this, photoUrl))
.build()
.inject(this);
tvOne.setText(photoToTailor.getPhotoTailor(photo) + "photoToTailor地址:" + photoToTailor);
DaggerSecondActivity_SecondeComponent.builder()
.baseComponent(((BaseApplication)getApplication()).getBaseComponent())
.secondModule(new SecondModule())
.build()
.inject(this);
textView.setText(photoToTailor.getPhotoTailor(photo) + "photoToTailor地址:" + photoToTailor);
结果打印:
这次获取的PhotoToTailor对象地址一致了,但有个细节不知道大家注意了没?如图:
从注解关键字可以看出Subcomponent和Component是上下级关系,@Subcomponent注解的功能和Dependencies类似,但是使用方法有点不同,我们通过代码来看一下,首先定义一个@Subcomponent注解的子组件SubMainComponent :
@PhotoMudule.CustomeScope //单例
@Subcomponent(modules = PhotoMudule.class)//组件声明
public interface SubMainComponent {
void inject(MainActivity mainActivity);
}
在父组件也就是BaseComponent中定义一个返回值为子组件的方法,当子组件需要什么Module时,就在该方法中添加该类型的参数:
@Singleton //对应Module中声明的单例
@Component(modules = BaseModule.class)
public interface BaseComponent {
//getPhotoToTailorgetClothHandler方法,PhotoToTailor
//它的作用就是告诉依赖于BaseComponent的Component,BaseComponent能为你们提供PhotoToTailor对象
PhotoToTailor getPhotoToTailor();
//定义一个返回值为子组件的方法,当子组件需要什么Module时,就在该方法中添加该类型的参数
SubMainComponent getSubMainComponent(PhotoMudule module);
}
在MainActivity中替代原来的PhotoComponent看:
((BaseApplication) getApplication()).getBaseComponent()
.getSubMainComponent(new PhotoMudule(MainActivity.this, photoUrl))
.inject(this);
tvOne.setText(photoToTailor.getPhotoTailor(photo) + "photoToTailor地址:" + photoToTailor);
效果查看:
首先我们先创建BaseComponent,他属于App级别的。我们BaseApplication创建它。BaseComponent中调用者提供SubMainComponent 。这和我们之前使用有些不同。
总结一下@Subcomponent的使用:
子组件的声明方式由@Component改为@Subcomponent
在父组件中要声明一个返回值为子组件的方法,当子组件需要什么Module时,就在该方法中添加该类型的参数
注意:用@Subcomponent注解声明的Component是无法单独使用的,想要获取该Component实例必须经过其父组件
Lazy和Provider都是用于包装Container中需要被注入的类型,Lazy用于延迟加载,所谓的懒加载就是当你需要用到该依赖对象时,Dagger2才帮你去获取一个;Provide用于强制重新加载,也就是每一要用到依赖对象时,Dagger2都会帮你依赖注入一次。
相关代码:
PhotoMudule中获取人物或者景物图片的方法
/**
* 获取人物图片对象
* @return
*/
@Provides
@Named("figure")
public Photo getfigurePhoto(){
Log.d(TAG, "调用了module中的getfigurePhoto方法");
Photo photo = new Photo("人物图片");
return photo;
}
/**
* 获取景物图片对象
* @return
*/
@Provides
@Named("scenery")
public Photo getsceneryPhoto(){
Log.d(TAG, "调用了module中的getsceneryPhoto方法");
Photo photo = new Photo("景物图片");
return photo;
}
MainActivity中的调用:
//Lazy声明方式
@Inject
@Named(value = "figure")
Lazy figurePhoto;
//Provider声明方式
@Inject
@Named(value = "scenery")
Provider sceneryPhoto;
PhotoComponent photoComponent =DaggerPhotoComponent.builder().baseComponent(((BaseApplication) getApplication()).getBaseComponent())
.photoMudule(new PhotoMudule(MainActivity.this, photoUrl))
.build();
photoComponent.inject(MainActivity.this);
Log.d(TAG, "figurePhoto:"+figurePhoto.get());
Log.d(TAG, "sceneryPhoto:"+sceneryPhoto.get());
Log.d(TAG, "figurePhoto:"+figurePhoto.get());
Log.d(TAG, "sceneryPhoto:"+sceneryPhoto.get());
打印:
注意我们上面是没有声明作用域内单例的,我们现在在PhotoModule中加一下看看:
/**
* 获取人物图片对象
* @return
*/
@CustomeScope //这里声明作用域内单例
@Provides
@Named("figure")
public Photo getfigurePhoto(){
Log.d(TAG, "调用了module中的getfigurePhoto方法");
Photo photo = new Photo("人物图片");
return photo;
}
/**
* 获取景物图片对象
* @return
*/
@CustomeScope //这里声明作用域内单例
@Provides
@Named("scenery")
public Photo getsceneryPhoto(){
Log.d(TAG, "调用了module中的getsceneryPhoto方法");
Photo photo = new Photo("景物图片");
return photo;
}
打印:
总结:
声明单例后,使用getsceneryPhoto()时也不会每次都去调用module中的方法了,这是因为Provider的作用是每次使用时都对依赖对象重新注入,但是getsceneryPhoto()在Component中是单例的,所以每次注入的都是同一个实例,所以只会调用一次module中的方法。
demo 已上传github
Dagger2基本就这么多,完毕!