您当前的位置: 首页 >  android

蔚1

暂无认证

  • 0浏览

    0关注

    4753博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Android 之 Dagger 的用法(详细莫过于此)

蔚1 发布时间:2020-03-16 23:31:36 ,浏览量:0

类似的依赖注入框架还有 butterknife,不过 butterknife,最多叫奶油刀,Dagger2 被叫做利器!他的主要作用,就是对象的管理,其在代码实现上高度解耦了逻辑上对象之间的耦合性。

@Inject,@Component,@Module 逻辑上的关系

如图所示:

image

  • @Inject: 在 Activity/Fragemnt 中通过 @Inject 标注需要使用的成员变量对象
  • @Module : 是实质上 new 创建我们所需要的成员变量的地方
  • @Component : 可以理解为一根管道,连接我们所需要使用的被@Inject 标注成员变量和创建对象的@Module 之间的管道.
引入依赖
implementation 'com.google.dagger:dagger:2.4'annotationProcessor 'com.google.dagger:dagger-compiler:2.4'

Dagger 的基本用法
@Modulepublic class StudentModule {    @Provides    public Student getStudent(){        return new Student();    }     public class Student{        String name = "我是小明";        int ID;        public String getName() {            return name;        }        public void setName(String name) {            this.name = name;        }        public int getID() {            return ID;        }        public void setID(int ID) {            this.ID = ID;        }    }}
  1. 首先先构建提供创建对象的 Modeule 类,在类上用@module 注明
    1. @module : 作用于类上,用于声明我们提供 module 的类
    2. @Provides : 作用于方法上,用于声明 module 类中对外提供创建对象的方法
@Component(modules = {StudentModule.class})public interface MainComponent {    void inject(MainActivity activity);}
  1. 构建前面所说的"管道"
    1. 需要说明的是这个部分创建的类是 Interface,是接口类型的
    2. @Component 声明于接口上
    3. 经过前面的介绍知道了 module 中就是实质创建对象的,在@Component 后面的 modules 中,我们需要用哪些对象,就添加哪些创建这些对象的 module.class
    4. inject(MainActivity activity) : 通过 inject 方法将对象注入到该类中,将会赋值给被@Injetc 注解标注的对象.
public class MainActivity extends AppCompatActivity {    @Inject    StudentModule.Student student;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        MainComponent mainComponent = DaggerMainComponent                .builder()                .studentModule(new StudentModule())                .build().inject(this);        System.out.println(student.getName());    }}
输出结果:System.out: 我是小明
  1. 使用
    1. @Injetc 注解标注所需要使用的对象
    2. 在工具类中点击"Build",然后进行"ReBuildProject"操作
    3. 经过 2 操作后,APT 工具自动生成代码,我们通过可以获取到 DaggerMainComponent 完成一整个的依赖注入过程.
    4. 调用我们需要的对象 student 进行使用

一个 component 中依赖加载多个 module 的使用

在上面的例子,component 中就添加一个 module 也就是 StudentModule.class,其支持添加多个 module

创建所需要使用的 module: 老师的 module
@Modulepublic class TeacherModule {    @Provides    public Teacher getTeacher(){        return new Teacher();    }    public class Teacher{        String name = "我是老师";        int ID;        public String getName() {            return name;        }        public void setName(String name) {            this.name = name;        }        public int getID() {            return ID;        }        public void setID(int ID) {            this.ID = ID;        }    }}

学生的 module
@Modulepublic class StudentModule {    @Provides    public Student getStudent(){        return new Student();    }     public class Student{        String name = "我是小明";        int ID;        public String getName() {            return name;        }        public void setName(String name) {            this.name = name;        }        public int getID() {            return ID;        }        public void setID(int ID) {            this.ID = ID;        }    }}
创建 Component:
@Component(modules = {StudentModule.class, TeacherModule.class})public interface MainComponent {    void inject(MainActivity activity);}
  • 上面 modules{}中新增了一个 TeacherModule.class
使用@Inject 标注的对象
public class MainActivity extends AppCompatActivity {    @Inject    StudentModule.Student student;    @Inject    TeacherModule.Teacher teacher;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        MainComponent mainComponent = DaggerMainComponent                .builder()                .studentModule(new StudentModule())                .build().inject(this);        System.out.println(student.getName());        System.out.println(teacher.getName());    }}
输出结果:System.out: 我是小明System.out: 我是老师

component 中可加入调用方法
@Component(modules = {StudentModule.class, TeacherModule.class})public interface MainComponent {    void inject(MainActivity activity);    TeacherModule.Teacher getTeacher();    StudentModule.Student getStudent();}
  • 就还是上面的例子,我们在其中创建添加一个方法 getTeacher()方法,除了通过@Inject 方式获取到生成的对象,通过调用该 Component 对象中的 getTeacher()方法我们也可以直接获取 Teacher 对象
  • Dagger 会根据方法的返回类型,自动去声明的 module 中匹配对应的对象
  • 以 Student 为例,如果通过@Inject 创建了一个对象,又通过 getStudent()获取了一次,那么这两个 Student 是不同的两个对象,除非使用单例模式,关于单例后面会讲
public class MainActivity extends AppCompatActivity {    @Inject    StudentModule.Student student;    @Inject    TeacherModule.Teacher teacher;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        MainComponent mainComponent = DaggerMainComponent                .builder()                .studentModule(new StudentModule())                .teacherModule(new TeacherModule())                .build();        mainComponent.inject(this);        System.out.println(student.getName());        System.out.println(mainComponent.getStudent().getName()+"   :getStudent 方式");        System.out.println(teacher.getName());        System.out.println(mainComponent.getTeacher().getName()+"   :getTeacher 方式");    }}
输出结果:System.out: 我是小明System.out: 我是小明   :getStudent 方式System.out: 我是老师System.out: 我是老师   :getTeacher 方式

Dagger 的单例实现

局部单例
  1. 在上文中列举的代码 学生的 module在对外提供的创建对象的方法上加上: @Singleton 注解
    @Singleton    @Provides    public Student getStudent(){        return new Student();    }
  1. 为 Component 也添加上@Singleton 注解
@Singleton@Component(modules = {StudentModule.class, TeacherModule.class})public interface MainComponent {    void inject(MainActivity activity);    void inject(Activity2 activity);    TeacherModule.Teacher getTeacher();    StudentModule.Student getStudent();}

3.实现如下:

public class MainActivity extends AppCompatActivity {    @Inject    StudentModule.Student student;    @Inject    TeacherModule.Teacher teacher;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        MainComponent mainComponent = DaggerMainComponent                .builder()                .studentModule(new StudentModule())                .teacherModule(new TeacherModule())                .build();        mainComponent.inject(this);        System.out.println("student address: "+student.toString());        System.out.println("student address: "+mainComponent.toString()+"   :getStudent 方式");        System.out.println("teacher address: "+teacher.toString());        System.out.println("teacher address: "+mainComponent.getTeacher().toString()+"   :getTeacher 方式");
输出结果:System.out: student address:...StudentModule$Student@1c87043System.out: student address:...StudentModule$Student@1c87043   :getStudent 方式System.out: student address:...TeacherModule$Teacher@3006ff9System.out: student address:...TeacherModule$Teacher@3006ff9   :getTeacher 方式
  • 通过输出结果我们可以看出,经过 singleton 注解的 Student 内存地址一致是单例
  • 未被 singleton 修饰的 Teacher 内存地址不同,不是单例
  • ==这种方式的单例是局部单例也就是只在该 Activity 中为单例,如果在其他 activity/fragment 中再使用 Student 那么对象内存地址将与上述结果日志中的地址不同==

全局单例
  • 在 Dagger 中的全局单例需要依赖于我们的 Application 才能够实现所需要的全局单例.
@Modulepublic class AppModule {    @Singleton    @Provides    public Object getApp(){        return new Object();    }}
@Singleton@Component(modules = {AppModule.class})public interface AppComponent {    Object getAPP();}
public class ExampleApplication extends Application {    private AppComponent mAppComponent;    @Override public void onCreate() {        super.onCreate();        mAppComponent = DaggerAppComponent                .builder()                .appModule(new AppModule())                .build();    }    public AppComponent getAppComponent() {        return mAppComponent;    }}
  • 首先在我们的 Application 中做好如上构建
@PreActivity@Component(modules = {StudentModule.class, TeacherModule.class},dependencies = {AppComponent.class})public interface MainComponent {    void inject(MainActivity activity);    void inject(Activity2 activity);    Object getAPP();}
  • 在我们调用的 Component 中添加 dependencies
  • 同时,我们发现还新增了一个方法 getAPP(),虽然在我们添加的 module 中没有提供该返回类型的方法,但该方法被调用的时候,会先去我们添加的 module 中去查找对应的提供该返回值类型的方法,当没有,就会去 dependencies 中的 AppComponent.class 中去查找对应的提供该返回值类型的方法,通过 getAPP()我们就可以获取到 AppComponent 中返回的单例对象
  • 我们还发现多了一个 @PreActivity 注解,这是因为
    • component 的 dependencies 与 component 自身的 scope 不能相同,即组件之间的 scope 不同
    • 没有 scope 的 component 不能依赖有 scope 的 component

自定义注解 @PreActivity 实现方式如下:
@Scope@Documented@Retention(RetentionPolicy.RUNTIME)public @interface PreApp {}
  • 然后在我们需要调用全局单例的地方完成如下操作:
Activity1,和 Activity2 中分别如下:
protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        MainComponent mainComponent = DaggerMainComponent                .builder()                .studentModule(new StudentModule())                .teacherModule(new TeacherModule())                .appComponent(((ExampleApplication) getApplication()).getAppComponent())                .build();        mainComponent.inject(this);        System.out.println(mainComponent.getAPP().toString());}
  • 将我们在 Application 中构建的 AppComponent 加入,并调用其单例进行 print 输出
输出结果:System.out: java.lang.Object@1c87043System.out: java.lang.Object@1c87043

可以看到,两个 activity 中的内存地址一致,是一个全局单例

@Binds 的使用

首先看下面的例子:

@Provides    public IPerson getTeacher(Student student){        return new Teacher(new Student());    }
  • 在此之前,是通过@Provides 方式进行实现的
@Provides    public IPerson getTeacher(Student student){        return new Teacher(new Student());    }
  • 假如在创建 Teahcer 对象的时候需要创建 Student 对象,那么就只能通过这种方式进行创建
  • 这也就意味着,当 Teacher 构造函数发生改变的时候我们也需要来这里进行对应的修改
  • 那么这种方式下的逻辑构建会因 Teacher 构造函数发生改变而存在耦合
通过@Bind 进行相同实现:
@Modulepublic abstract class TeacherModule {    @Binds    public abstract IPerson getTeacher(Teacher teacher);}@Provides    public static String setName(){        return "我是老师";    }}    
  • ==@Binds :我们可以理解为会将方法的形参进行返回==
  • 方法的形参会自动去构建相应的对象实例
  • 会自动将上一步构建好的实例进行返回
  • ==这样一来,不论 Teacher 这个对象的构造函数如何变换,都与当前的引用完全解耦==
  • 有两个地方需要注意
    • ==当使用@Binds 返回类型是 抽象类的时候,当前的@Module,其类也将会变成抽象类==
    • ==当@Module 是抽象类的时候,其内部的@Provides 方法必须是 static 修饰的静态方法==
  • 上面这个只是@Binds 实现的片段代码,具体实现步骤请看下面

@Bind 具体实现步骤:
@Modulepublic abstract class TeacherModule {    @Binds    public abstract IPerson getTeacher(Teacher teacher);@Provides    public static String setName(){        return "我是老师";    }}    
  • 使用 @Binds 声明相应的方法
public interface IPerson {    String getName();    int getID();}
  • Teacher 所实现用的接口
public class Teacher implements IPerson{    @Inject    public Teacher(Student student){        System.out.println(student.getName());    }    String name = "";    int ID;    @Override    public String getName() {        return name;    }    @Override    public int getID() {        return ID;    }}
  • 在 Teahcer 类中,通过@Inject 对构造函数进行声明
  • 到此可能会问,那么构造函数中的 Student 怎么传进来,请看下面
public class Student{    String name = "老师,我是小明";    int ID;    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getID() {        return ID;    }    public void setID(int ID) {        this.ID = ID;    }}
@Modulepublic class StudentModule {    @Provides    public  Student getStudent(){        return new Student();    }}
@Component(modules = {StudentModule.class, TeacherModule.class})public interface MainComponent {    void inject(MainActivity activity);    void inject(Activity2 activity);}
  • 我们创建了一个 StudentModule 并且在其中@Provides 提供了对外创建 Student 对象的方法
  • 并且在@Component 中声明了所使用到的 module
  • 在 Teacher 构造函数需要传入 Student 对象时,Dagger 就会在@Component(modules = {StudentModule.class, TeacherModule.class})中查找提供返回 Student 对象的 @Provides 方法去执行该方法,进行对象的创建
public class MainActivity extends AppCompatActivity {    @Inject    IPerson teacher;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        MainComponent mainComponent = DaggerMainComponent                .builder()                .studentModule(new StudentModule())                .build();        mainComponent.inject(this);        System.out.println("teacher : "+teacher.toString());    }}
  • 最后在 MainActivity 中进行执行调用
输出结果:System.out: 老师,我是小明

@Binds 使用注意事项
   @Binds    public abstract IPerson getTeacher(Teacher teacher);
  1. 前面的例子中使用 @Binds,如上面的代码,注意其形参 Teacher,Teacher 的构造函数通过@Inject 声明的时候只能有一个构造函数被@Inject 声明,假如在 Teacher 的两个或者多个构造函数上都进行@Inject 声明,则无法通过编译
  2. Teacher 其构造函数的形参 Student 必须在@Component(modules = {StudentModule.class, TeacherModule.class})中任意一 module 中有相应的@Provide 方法提供相同的返回类型,否则无法通过编译3.
MainComponent mainComponent = DaggerMainComponent                .builder()                .studentModule(new StudentModule())                .teacherModule(new TeacherModule() {                    @Override                    public IPerson getTeacher(Teacher teacher) {                        Student student = new Student();                        student.setName("老师,我是小明  :teacherModule");                        return new Teacher(student);                    }                })                .build();        mainComponent.inject(this);

当通过 @Binds 方式实现后,再在 activity 里面进行实现 teacherModule,则 activity 中的创建的 teacherModule 实现并不会被执行,原因是在尽管实现了这个方法,但 Dagger 在执行的时候,执行到@Binds 注解的方法时,依旧是按照其注解的逻辑去执行

@Qualifier 和@Named
  • 当我们所需要的类型值有多个方法提供该类型时,则可以使用@Named 注解
  • 在@Name 注解中,事实上真正起作用的是@Qualifier
  • 用法是:
    • 在@provide 的方法上用@Name 声明该方法的别名
    • 在参数、成员变量、方法上等需要调用的地方,用@Name 指明所需要调用的@provide 方法
public interface IPerson {    String getName();    int getID();}
public class Teacher implements IPerson{      @Inject      public Teacher(@Named("Teacher3")String name){          this.name = name;      }    String name = "";    int ID;    @Override    public String getName() {        return name;    }    @Override    public int getID() {        return ID;    }}
@Modulepublic abstract class TeacherModule {    @Binds    public abstract IPerson getTeacher(Teacher teacher);    @Provides    public static String setName(){        return "我是老师(Binds 方式实现 2)";    }    @Provides    @Named("Teacher3")    public static String setName2(){        return "我是老师(Binds 方式实现 3)";    }}
  • 在上述代码可以看到在 Teacher(String name)的构造函数中需要传入 String 类型
  • 但是在 TeacherModule 中含有多个方法具有该类型的返回值
  • 这个时候我们可以通过@Named 对不同的方法进行命名,然后在需要引用的构造函数的形参前面进行指导,Dagger 在执行的时候就会去调用指定别名的方法
public class MainActivity extends AppCompatActivity {    @Inject    IPerson teacher;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        MainComponent mainComponent = DaggerMainComponent                .builder()                .studentModule(new StudentModule())                .build();        mainComponent.inject(this);        System.out.println(teacher.getName().toString());    }}
输出结果:System.out: 我是老师(Binds 方式实现 3)

懒加载 Lazy 的使用

通过 Lazy 修饰,该对象它在真正被使用时才被实例化

    @Inject    Lazy teacher;    .......    IPerson person = teacher.get();//获取对象

Dagger 使用总结
  1. Component 的 inject 方法接收父类型参数,而调用时传入的是子类型对象则无法注入,也就是说无法使用多态方式进行注入。
  2. component 关联的 modules 中不能有重复的 provide
  3. module 的 provide 方法使用了 scope ,那么 component 就必须使用同一个注解
  4. module 的 provide 方法没有使用 scope,那么 component 和 module 是否加注解都无关紧要,可以通过编译
  5. component 的 dependencies 与 component 自身的 scope 不能相同,即组件之间的 scope 不同
  6. Singleton 的组件不能依赖其他的 scope 的组件,只能其他 scope 的组件依赖 Singleton 的组件。
  7. 没有 scope 的 component 不能依赖有 scope 的 component
  8. 一个 component 不能同时有多个 scope(Subcomponent 除外)
  9. @Singleton 的生命周期依附于 component,同一个 module 有 provideXX()提供一个实例,且被@Singleton 标注,针对不同的 component,创建的实例不同。

阅读全文: http://gitbook.cn/gitchat/activity/5e6e63d807412e6201e1d6fc

您还可以下载 CSDN 旗下精品原创内容社区 GitChat App ,阅读更多 GitChat 专享技术内容哦。

FtooAtPSkEJwnW-9xkCLqSTRpBKX

关注
打赏
1560489824
查看更多评论
立即登录/注册

微信扫码登录

0.0839s