加拿大快乐8最快开奖
当前位置:首页 > 安卓源码 > 技术博客 >

Andriod-Dagger2使用技巧,代码参考

时间:2019-08-15 09:05 来源:互联网 作者:源码搜藏 浏览: 收藏 挑错 推荐 打印

参考资料: https://www.jianshu.com/p/1d84ba23f4d2 https://mp.weixin.qq.com/s/lh3dgJK95cgbG-bUZfvbFA 听说好多开发者采用Dagger2+RxJava+Retrofit+mvp结构来进行开发,虽然Dagger2已经出来好久了,但一直没有进行总结,今天所以有必要总结一下Dagger2。

参考资料:
https://www.jianshu.com/p/1d84ba23f4d2
https://mp.weixin.qq.com/s/lh3dgJK95cgbG-bUZfvbFA

听说好多开发者采用Dagger2+RxJava+Retrofit+mvp结构来进行开发,虽然Dagger2已经出来好久了,但一直没有进行总结,今天所以有必要总结一下Dagger2。

1.什?#35789;荄agger2

Dagger中文意思是匕首,Dagger2是Dagger的第二个版本,之前的Dagger已经?#29260;?#32500;护了,Dagger2用官方的话来说就是:

 
Andriod-Dagger2使用技巧,代码参考
 

翻译成中文就是:
Dagger2是Java和Android?#30446;?#36895;?#35272;?#27880;入器,是?#35272;?#27880;入的编译时框架。它不使用反射或运行时字节码生成,在编译时进行所有分析,并生成纯Java源代码。一般的IOC框架都是通过反射来实?#20540;?但Dagger2作为Android?#35828;腎OC框架,为了不影响性能,它是通过apt动态生成代码来实?#20540;摹?#20854;主要作用就是解耦和管理实例对象。

2.相关配置

我们先?#32431;?#23448;网怎么说:

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?#35272;怠?/p>

2.1.为什么要添加dagger-android2.x?#35272;?

我们都知道Android应用使用Dagger最主要的困难就是一些Framework类(如Activity、Fragment)是由操作系统实例化?#27169;?#32780;Dagger更好工作的前提是它可以构建所有的注入对象。所以,你只能在生命周期方法中进行成员变量注入,这样就会产生两个问题:

  • 代码冗余,会产生好多公用的代码,以后会很难维护。
  • 更重要的是,它要求注射类型(Activity)知道其注射器。 即使这是通过接口而不是具体类型完成?#27169;?#23427;打破了?#35272;?#27880;入的核心原则:一个类不应该知道如何实现?#35272;?#27880;入。

虽然仅仅添加dagger-2x?#37096;?#20197;实现?#35272;?#27880;入,但如果添加了dagger-android2.x的?#35272;?#23601;可以避免上面提到的问题了。

2.2 引入?#35272;擔?/h4>
//Dagger2相关?#35272;?
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'

3.基本使用

Dagger主要是以下几个注入标签,我们来一一说明:

  • @Inject
    简介:如果在类上添加此?#35272;?#27880;入,Dagger 就会构造一个这个类的实例并满足他们的?#35272;怠?br style="overflow-wrap: break-word; padding: 0px; margin: 0px;" /> 通过这个inject注解可以将?#35272;?#38656;求方对象送到Component类中,Component类就会根据?#35272;?#38656;求方对象中声明的?#35272;?#20851;系来注入?#35272;?#38656;求方对象中所需要的对象,注意:inject方法的参数不能用父类来接收,@Inject注解的字段不能是private和protected的
  • @Module
    简介:编写Module类时要在该类上声明@Module以表明该类是Module类,这样Dagger2才能识别,Modules 类里面的方法专门提供?#35272;擔?#22914;返回你需要?#35272;?#30340;对象实例。
  • @Provide
    简介:在 modules 中我们定义的方法就用@Provide注解,作用?#24039;?#26126;Module类中哪些方法是用来提供?#35272;?#23545;象的,当Component类需要?#35272;?#23545;象时,他就会根据返回值的类型来在有@Provides注解的方法中选择调用哪个方法。
  • @Component
    Components 从根本上来说就是一个注入器,?#37096;?#20197;说是@Inject 和@Module 的桥梁,来连接@Inject 和@Module这两个部分。但@Component注解的作用可不是单单用来声明Component类,@Component注解有modules和dependencies两个属性,这两个属性的类型都是Class数组,modules的作用就?#24039;?#26126;该Component含有哪几个Module;而dependencies属性则?#24039;?#26126;Component类的?#35272;?#20851;系,这个我们之后详细讲解。
    不理解没关系,我们通过代码来说明一下:
    ?#28909;?#25105;们要做MainActivity类中调用选图类中的拍照方法takePhoto(),一般代码应该是这样?#27169;?br style="overflow-wrap: break-word; padding: 0px; margin: 0px;" /> PhotoUtil :选图工具类
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,我?#24039;?#38754;说了Module 类里面的方法专门提供返回对象?#35272;擔?#25152;以我们定义一个类,用@Module 注解,这样 Dagger 在构造类的实例时候,就知?#26469;?#21738;里去?#19994;?#38656;要的?#35272;怠?/strong>我们取名为PhotoMudule.
相关代码:

@Module //Module 注解类里面的方法专门提供?#35272;擔?#25152;以我们定义一个类,用@Module 注解
public class PhotoMudule {


    //module类中要提供?#35272;?#30340;注解方法用@Provides注解声明,?#28304;?#26469;告诉Dagger要构造对象并提供这些?#35272;?
    @Provides
    public PhotoUtil  photoInstance (){
         return new PhotoUtil();
    }
}

接下来用在modules中,我们定义的方法用@Provides注解,?#28304;?#26469;告诉Dagger我们想要构造对象并提供这些?#35272;擔?#27880;意:用@Provides注解的方法必须是public,不然外部怎么?#26790;省?/strong>

第二步:

我?#24039;?#38754;提到了Component说它的作用主要是桥梁,那我们现在就来把Component和Module关联,取名photoComponent,代码如下:

//通过@Component注解来绑定我们的PhotoMudule(从{}这个符号我们就可以看到,他可以同?#24065;览?#22810;个 module)
@Component(modules = {PhotoMudule.class})
public interface photoComponent {

    //定义inject方法,参数是MainActivity,因为我们想在这个类中使用我们实例PhotoUtil
    void inject(MainActivity mainActivity);
}

注意:Component注解的类是一个接口
注解写的都很明白,这样 module 和 MainActivity 通过 Component 就关联起来了,切记我们还要执行以下Rebuild Progect,。

 
Andriod-Dagger2使用技巧,代码参考
image.png

在执行Rebuild Progect之后,会生成一个DaggerPhotoComponet 类,这个类命名是以 Dagger 开?#26041;由?#25105;们 PhotoComponet 类名。此类的主要作用就是将我们的 MainActivity 和 Component通过我们定义的inject()方法传参Activity关联起来,我们接下来的调用也是调用这个生成的DaggerPhotoComponet类:

在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 注解并?#19994;?#29992; DaggerPhotoComponet.create().inject(this)关联; 后我们就可以拿到这个对象的实例了。
结果打印:

 

 
Andriod-Dagger2使用技巧,代码参考
image.png

可以看到我们调用相关方法成功,但我们要思考一个问题,这样并不能很好的解耦,什么意思呢,我们来思考个问题?

  • 我们只是简单的调用了PhotoUtil中的 takePhoto方法,那万一我们还要调用其它类中的其他方法该怎么写呢?
    答:我们需要创建一个管理类,来管理我们的相关类,我们就叫做PhotoClsMannger吧
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中的构造方法?#34892;?#22909;多参数,我们说了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();
    }

}

那我们来?#32431;碢hotoMudule类中要怎么写:
首先我们在PhotoMudule中传入需要的参数,要通过PhotoMudule的构造参数来写:

@Module //Module 注解类里面的方法专门提供?#35272;擔?#25152;以我们定义一个类,用@Module 注解
public class PhotoMudule {
    Context context;
    String photoUrl;

    public PhotoMudule(Context context, String photoUrl) {
        this.context = context;
        this.photoUrl = photoUrl;
    }

    //module类中要提供?#35272;?#30340;注解方法用@Provides注解声明,?#28304;?#26469;告诉Dagger要构造对象并提供这些?#35272;?
    @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调用:

 

 
Andriod-Dagger2使用技巧,代码参考
image.png

执行结果:

 

 
Andriod-Dagger2使用技巧,代码参考
image.png

另一?#20013;?#27861;

假设我们拍照之后裁剪之后还需要上传,所以我们创建一个上传类

public class PhotoUp {

    @Inject
    public PhotoUp() {
    }

    @Override
    public String toString() {
        return "调用了?#35745;?#19978;传方法";
    }
    
}

这次我们创建的方式和之前有点不一样了,我们直接在构造函数上声明了@Inject注解,这个注解有什么用呢?当Component在所拥有的Module类中找不到?#35272;?#38656;求方需要类型的提供方法时,Dagger2就会检查该需要类型的有没有用@Inject声明的构造方法,有则用该构造方法创建一个,注意:这次我没是没有在Module类?#34892;?#36820;回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);
    }

结果:

 

 
Andriod-Dagger2使用技巧,代码参考
image.png

那么又?#34892;?#20249;伴要问了,后一种直接在构造方法上添加 @Inject注解要方便好多,为什么还需要@Module注解呢?
答:
项目中我们会用到别?#35828;膉ar包,我们无法修改别?#35828;?a href='http://www.pczbxx.tw/' target='_blank'>源码,就更别说在?#24605;业?#31867;上添加注解了,所以我们只能通过Module类来提供

总结:
我们有两种方式可以提供?#35272;擔?#19968;个是注解了@Inject的构造方法,一个是在Module里提供的?#35272;擔?#35268;则是这样?#27169;?#26597;找Module中是否存在创建该类的方法,如果没有则查找Inject注解的构造函数

4.高级使用

说完了基本使用,我们来?#32431;碊agger2高级使用的注解

  • @Named和@Qualifier: 要作用是用来区分不同对象实例
    @Named 其实是@Qualifier 的一种实现
    **@Qulifier是自定义注解用?#27169;?#21487;以替代@Named **
  • @Singleton和@Scope
    **Singleton其实是继承@Scope注解的 **
    @Scope和@Qulifier一样,需要我们自定义注解才能使用
  • Subcomponent
  • Lazy与Provider

4.1 @Named和@Qulifier注解的使用

4.1.1 @Named

上面我们模拟了?#35745;?#30340;选取-裁剪-上传通过注解如何获取?#35272;?#23545;象,然后调用相关方法,那么现在假设我要在?#35745;?#26412;身做分类,将?#35745;?#31867;型分为人物,?#21543;?#31561;呢?
首先我们先创建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中增加两个获取不同类别照片的方法:

 /**
     * 获取人物?#35745;?#23545;象
     * @return
     */
    @Provides
    public Photo getfigurePhoto(){
        Photo photo = new Photo("人物?#35745;?quot;);
        return photo;
    }

    /**
     * 获取景物?#35745;?#23545;象
     * @return
     */
    @Provides
    public Photo getsceneryPhoto(){
        Photo photo = new Photo("景物?#35745;?quot;);
        return photo;
    }

这个时候我们的问题就来了,Dagger2是通过返回值类型来确定的,当你需要人物?#35745;?#26102;,它又怎么知道哪个是人物?#35745;?#21602;?
答:还好有@Named注解。
@Named注解有一个value值,用来标识这个方法是给谁用的.修改我们的代码:

  /**
     * 获取人物?#35745;?#23545;象
     * @return
     */
    @Provides
    @Named("figure")
    public Photo getfigurePhoto(){
        Photo photo = new Photo("人物?#35745;?quot;);
        return photo;
    }

    /**
     * 获取景物?#35745;?#23545;象
     * @return
     */
    @Provides
    @Named("scenery")
    public Photo getsceneryPhoto(){
        Photo photo = new Photo("景物?#35745;?quot;);
        return photo;
    }

调用:注意在 Moudle 用了@Named 标签,在调用?#24065;?#38656;要加上@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);

结果:

 

 
Andriod-Dagger2使用技巧,代码参考
image.png
[email protected]

@Qulifier功能和@Named一样,并且@Named就是继承@Qulifier的,没错,@Qulifier就是自定义注解用的。接下来我们定义一个@CustomeQualifier来替代@Named:



   
    //自定义注解名为:CustomeQualifier
  //@Qualifier :注明是Qualifier(关键词)
 // @Documented :标记在文档(可不写)
 // @Retention(RUNTIME) :运行时级别 
    @Qualifier
    @Retention(RetentionPolicy.RUNTIME)
    public @interface CustomeQualifier {
        String value() default "";
    }
  /**
     * 采用Qualifier自定义注解实?#30452;?#35760;区分
     * @return
     */
    @Provides
    @CustomeQualifier("scenery")
    public Photo customeQualifierPhoto(){
        Photo photo = new Photo("景物?#35745;?quot;);
        return photo;
    }
   
   //执行相关代码
    @Inject
    @PhotoMudule.CustomeQualifier(value = "scenery")
    Photo customeQualifier;
     Log.d(TAG, "customeQualifier:"+customeQualifier);

4.2 @Singleton和@Scope的使用

学习@Singleton注解之前,我们先想这么一个需求,有没有一个?#35272;?#31867;以另外一个类作为?#35272;?#31867;呢??#28909;紓业?#35009;剪对象需要一个?#35745;?#23545;象,我们先在PhotoMudule?#34892;?#19968;下相关代码:

 @Provides
    @Named("phototailorbyphoto")
    public PhotoTailor  photoTailorByPhoto (){
        Photo photo =new Photo("景物?#35745;?quot;);
        return new PhotoTailor(photo);
    }

但是这么写有点麻烦,Dagger2提供了这样的功能,我们只要在photoTailorByPhoto 方法中添加Photo 参数,Dagger2就会像帮?#35272;?#38656;求方找?#35272;?#23545;象一样帮你?#19994;?#35813;方法?#35272;?#30340;Photo 实例,所以我们代码可以这样改:

 @Provides
    public Photo getPhoto(){
        return  new Photo("景物?#35745;?quot;);
    }

  @Provides
    @Named("phototailorbyphoto")
    public PhotoTailor  photoTailorByPhoto (Photo photo){
        return new PhotoTailor(photo);
    }

打印:

 

 
Andriod-Dagger2使用技巧,代码参考
image.png
[email protected]

关于@Singleton ,我们需要接着上面的例子看:

  @Provides
    public Photo getPhoto(){
        return  new Photo("景物?#35745;?quot;);
    }

    @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));

打印:

 

 
Andriod-Dagger2使用技巧,代码参考
image.png

 

为什么会出?#32456;?#31181;情况呢?
答:,注入过程中,对Photo注入时会调用一次getPhoto方法,创建了一个Photo对象;注入PhotoTailor 时又会调用一次getPhoto方法,这时又会创建一个Cloth对象,所以才会出现上面的结果,那如果我们想Photo和PhotoTailor 中的是一个Photo改怎么办呢?这就要用到@Singleton注解了,顾名思义@Singleton?#32622;?#24847;思就?#24039;?#26126;单例模式。怎么用呢?改两个地方:Module中获取实例的方法和Component?#25216;由螥Singleton,代码如下:

   @Singleton
    @Provides
    public Photo getPhoto(){
        return  new Photo("景物?#35745;?quot;);
    }

@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));

打印:

 

 
Andriod-Dagger2使用技巧,代码参考
image.png

小总:

  • module 的 provide 方法使用了 scope ,那么 component 就必须使用同一个注解
  • @Singleton 的生命周期依附于 component,也就是说同一个 module 被不同的@Component ?#35272;?#33719;取的实例不会是同一个, @Singleton属于 Activity级别单例。那如果我想定义为全?#20540;?#29983;命周期单例该怎么办呢?我?#24039;?#21518;会将,别急。
4.2.2Scope

@Scope就是用来声明作用?#27573;?#30340;[email protected]和@Qulifier一样,需要我们自定义注解才能使用,我们先自定义一个注解:

//定义@Scope注解名为:CustomeScope
    @Scope
    @Retention(RetentionPolicy.RUNTIME)
    public @interface CustomeScope {
    }

这个自定义注解的作用是:声明作用?#27573;?当我们将这个注解使用在Module类中的Provide方法上时,就?#24039;?#26126;这个Provide方法是在CustomeScope 作用?#27573;?#20869;的,并?#19994;?#19968;个Component要引用这个Module时,必须也要声明这个Component是CustomeScope 作用?#27573;?#20869;的,否则就会报错,声明方法也很简单,就是在Component接口上使用这个注解。那我们把之前的@Singleton替换一下?#32431;?#25928;果:

 //@Singleton
    @CustomeScope
    @Provides
    public Photo getPhoto(){
        return  new Photo("景物?#35745;?quot;);
    }

 //定义@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));

打印:

 

 
Andriod-Dagger2使用技巧,代码参考
image.png

那?#27425;?#20160;么这么做?#37096;?#20197;实现Activity级别的单例效果呢?
答:
Dagger2有这样一个机制:在同一个作用?#27573;?#20869;,Provide方法提供的?#35272;?#23545;象就会变成单例,也就是说?#35272;?#38656;求方不管?#35272;?#20960;次Provide方法提供的?#35272;?#23545;象,Dagger2都只会调用一次这个方法.

4.3 组件?#35272;礵ependencies的使用

我们刚才通过自定义@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("景物?#35745;?quot;);
    }

    @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);

    }

结果展示:

 

 
Andriod-Dagger2使用技巧,代码参考
image.png

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("人物?#35745;?quot;);
        }

        @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);
    }

结果展示:

 

 
Andriod-Dagger2使用技巧,代码参考
image.png

我们来?#21592;?#19968;下:

 

 
Andriod-Dagger2使用技巧,代码参考
image.png

 

 
Andriod-Dagger2使用技巧,代码参考
image.png

你会发现,虽然我们成功的将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 {
    
    //它的作用就是告诉?#35272;?#20110;BaseComponent的Component,BaseComponent能为你们提供PhotoToTailor对象
    PhotoToTailor getPhotoToTailor();
}

删除PhotoMudule和SecondModule中原有的photoTailorByPhoto方法:

@Module
public class PhotoMudule {
    @Singleton
    @Provides
    public Photo getPhoto(){
        return  new Photo("景物?#35745;?quot;);
    }

@CustomeScope
    @Provides
    public PhotoToTailor getPhotoToTailor(){
        return new PhotoToTailor();
    }
}
@Module
    public class SecondModule {

        public SecondModule() {
        }

        @Singleton
        @Provides
        public Photo getPhoto(){
            return  new Photo("人物?#35745;?quot;);
        }

     @CustomeScope
    @Provides
    public PhotoToTailor getPhotoToTailor(){
        return new PhotoToTailor();
    }

    }

接下来在MainComponent和SecondComponent中声明?#35272;?就要用到@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?#35828;?#19979;的Rebuild Object后,你会发现创建MainComponent和SecondComponent实例时多了一个baseComponent方法,

 
Andriod-Dagger2使用技巧,代码参考
image.png

我们一般都会自定义一个Application类,用它来提供BaseComponent实例,因为在整个App生命周期内都只有一个Application实例,所以其中的BaseComponent实例也不会变.我们自定义一个BaseApplication类

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对象,来确保全?#20540;?#20363;

 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);

结果打印:

 

 
Andriod-Dagger2使用技巧,代码参考
image.png
 
Andriod-Dagger2使用技巧,代码参考
image.png

这次获取的PhotoToTailor对象地址一致了,但有个细节不知?#26469;?#23478;注意了没?如图:

 
Andriod-Dagger2使用技巧,代码参考
image.png

 

 
Andriod-Dagger2使用技巧,代码参考
 

我们的子组件PhotoComponent和父组件BaseComponent没有使用同一个单例注解关键字,子组件用的是自定义的@Scope为什么呢?原因是:Singleton 的组件不能?#35272;?#20854;他 scope 的组件,只能其他 scope 的组件可以?#35272;?Singleton的组件 。demo中我们的BaseComponent已经用@Singleton 修饰就不能再去?#35272;当?#30340;Component,但子别的 Component,即其他scope的组件 可以?#35272;?#20854;他组件。

4.4 Subcomponent

从注解关键字可以看出Subcomponent和Component?#24039;?#19979;级关系,@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
    //它的作用就是告诉?#35272;?#20110;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);

效果查看:

 

 
Andriod-Dagger2使用技巧,代码参考
image.png
 
Andriod-Dagger2使用技巧,代码参考
image.png

首先我们先创建BaseComponent,他属于App级别的。我们BaseApplication创建它。BaseComponent中调用者提供SubMainComponent 。这和我们之前使用?#34892;?#19981;同。
总结一下@Subcomponent的使用:

子组件的声明方式由@Component改为@Subcomponent
在父组件中要声明一个返回值为子组件的方法,当子组件需要什么Module时,就在该方法中添加该类型的参数
注意:用@Subcomponent注解声明的Component是无法单独使用的,想要获取该Component实例必须经过其父组件

4.5 Lazy与Provider

Lazy和Provider都是用于包装Container?#34892;?#35201;被注入的类型,Lazy用于延迟加载,所谓的懒加载就是当你需要用到该?#35272;?#23545;象时,Dagger2才帮你去获取一个;Provide用于强制重新加载,也就是每一要用到?#35272;?#23545;象时,Dagger2都会帮你?#35272;?#27880;入一次。
相关代码:
PhotoMudule中获取人物或者景物?#35745;?#30340;方法


    /**
     * 获取人物?#35745;?#23545;象
     * @return
     */
    @Provides
    @Named("figure")
    public Photo getfigurePhoto(){
        Log.d(TAG, "调用了module中的getfigurePhoto方法");

        Photo photo = new Photo("人物?#35745;?quot;);
        return photo;
    }

    /**
     * 获取景物?#35745;?#23545;象
     * @return
     */
    @Provides
    @Named("scenery")
    public Photo getsceneryPhoto(){
        Log.d(TAG, "调用了module中的getsceneryPhoto方法");

        Photo photo = new Photo("景物?#35745;?quot;);
        return photo;
    }

MainActivity中的调用:

//Lazy声明方式
@Inject
@Named(value = "figure")
Lazy<Photo> figurePhoto;
//Provider声明方式
@Inject
@Named(value = "scenery")
Provider<Photo> 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());

打印:

 

 
Andriod-Dagger2使用技巧,代码参考
image.png

注意我?#24039;?#38754;是没有声明作用域内单例?#27169;?#25105;们现在在PhotoModule中加一下?#32431;?

/**
     * 获取人物?#35745;?#23545;象
     * @return
     */
    @CustomeScope //这里声明作用域内单例
    @Provides
    @Named("figure")
    public Photo getfigurePhoto(){
        Log.d(TAG, "调用了module中的getfigurePhoto方法");

        Photo photo = new Photo("人物?#35745;?quot;);
        return photo;
    }

    /**
     * 获取景物?#35745;?#23545;象
     * @return
     */
    @CustomeScope //这里声明作用域内单例
    @Provides
    @Named("scenery")
    public Photo getsceneryPhoto(){
        Log.d(TAG, "调用了module中的getsceneryPhoto方法");

        Photo photo = new Photo("景物?#35745;?quot;);
        return photo;
    }

打印:

 

 
Andriod-Dagger2使用技巧,代码参考
image.png

总结:
声明单例后,使用getsceneryPhoto()?#24065;?#19981;会每次都去调用module中的方法了,这是因为Provider的作用是每次使用时都对?#35272;?#23545;象重新注入,但是getsceneryPhoto()在Component中是单例的,所以每次注入的都是同一个实例,所以只会调用一次module中的方法。

demo 已上传github
Dagger2基本就这么多,完毕!

Andriod-Dagger2使用技巧,代码参考 转载<\/script>');
加拿大快乐8最快开奖