博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Dagger2 入门笔记
阅读量:6376 次
发布时间:2019-06-23

本文共 14870 字,大约阅读时间需要 49 分钟。

网上对 Dagger2 进行介绍的文章也已经很多了,一开始看的时候却总是有种从入门到放弃的感觉,因为 Dagger2 中注解的配套使用是需要一定规则的,而文章介绍得并不算太详细,如果搭配不当,Dagger2 是不会为我们生成相应的文件的,这就导致应用在编译时总是遇到各种报错,然后就一脸蒙蔽,所以这就需要很多的实践操作了

这里我就将本人在学习 Dagger2 的过程中的实践记录下来,希望对你有所帮助

一、配置

dependencies {    implementation 'com.google.dagger:dagger:2.16'    annotationProcessor 'com.google.dagger:dagger-compiler:2.16'}

二、@Inject

假设当前有一个 Person 类,其声明如下所示

/** * 作者:叶应是叶 * 时间:2018/7/8 16:21 * 描述: */public class Person {    private String name;    public Person() {        name = "person default name";    }    public Person(String name) {        this.name = name;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }}

在一般情况下,如果我们要使用到一个 Person 变量,就需要以如下方式来声明

Person person = new Person();

而这隐藏着一个问题,就是没当 Person 类的构造函数发生了变化时(参数变多或变少),所有使用到 Person 的代码就需要都修改一遍,这对于较大的项目来说是一件很耗时耗力的工作,Dagger2 就是用来解决这一问题的依赖注入框架

首先为 Person 的构造函数添加 @Inject 注解,指定 Dagger2 在为我们初始化 Person 变量时要调用的构造函数

public class Person {        @Inject    public Person() {        name = "person default name";    }        ···        }

此外,还需要一个接口来作为 Person 和需要进行依赖注入的类之间的桥梁

此处即为 PersonComponent 接口,该接口需要使用 @Component 进行注解,且包含一个方法用于将需要使用到依赖注入的类对象传递进来,此外为 MainActivity,注意此处需要是确切的对象,而不能是任何父类对象

此外,接口名和方法名没有硬性规定

/** * 作者:叶应是叶 * 时间:2018/7/8 16:35 * 描述: */@Componentpublic interface PersonComponent {    void inject(MainActivity mainActivity);}

接下来就可以在 MainActivity 中进行依赖注入了

/** * 作者:叶应是叶 * 时间:2018/7/8 16:35 * 描述: */public class MainActivity extends AppCompatActivity {    private static final String TAG = "MainActivity";    @Inject    Person person1;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        DaggerPersonComponent.builder().build().inject(this);        Log.e(TAG, "person1: " + person1);        Log.e(TAG, "person1 name : " + person1.getName());    }}

在运行前需要先 build 工程,这样 DaggerPersonComponent 类才会生成,运行结果如下所示

person1: com.leavesc.dagger2samples.test1.Person@420a5ee0person1 name : person default name

使用 @Inject 进行注解的 person1 变量我们并没有对其进行初始化,但是应用在运行时并没有报空指针异常,说明 Dagger2 在后台为我们进行初始化操作了

实际进行初始化操作的是以下代码

DaggerPersonComponent.builder().build().inject(this);

DaggerPersonComponent 是 Dagger2 依照 PersonComponent 的命名而生成的文件,可以点进去看下其源码

DaggerPersonComponent 实现了 PersonComponent 接口,在为 person1 赋值时是直接调用了 Person 类的无参构造函数。因为 MainActivity_MembersInjector 是依靠 MainActivity 对象引用到 person1 变量,因此在 person1 不能声明为私有的,否则引用不到 person1 也就无法实现依赖注入了

public final class DaggerPersonComponent implements PersonComponent {  private DaggerPersonComponent(Builder builder) {}  public static Builder builder() {    return new Builder();  }  public static PersonComponent create() {    return new Builder().build();  }  @Override  public void inject(MainActivity mainActivity) {    injectMainActivity(mainActivity);  }  private MainActivity injectMainActivity(MainActivity instance) {    MainActivity_MembersInjector.injectPerson1(instance, new Person());    return instance;  }  public static final class Builder {    private Builder() {}    public PersonComponent build() {      return new DaggerPersonComponent(this);    }  }}
public final class MainActivity_MembersInjector implements MembersInjector
{ ··· public static void injectPerson1(MainActivity instance, Person person1) { instance.person1 = person1; }}

三、@Module、@Provides

@Inject 注解在用于工程中自己建立的类时是可行的,但面对工程中依赖到的各种开源库却无能为力了,因为我们无法修改它们的构造函数,此时就需要用到 @Module@Provides 注解

假设当前有个 User 类来自于项目中依赖到的开源库中,此时该类的构造函数并没有添加 @Inject 注解

/** * 作者:叶应是叶 * 时间:2018/7/8 17:11 * 描述: */public class User {    private String name;    public User() {        name = "user default name";    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }}

新建 UserModule 类用于对外部提供 User 类的实例,@Provides 注解用于告诉 Dagger2 ,如果需要 User 类的实例就调用此方法来获取

/** * 作者:叶应是叶 * 时间:2018/7/8 17:12 * 描述: */@Modulepublic class UserModule {    @Provides    public User provideUser() {        return new User();    }}

此时一样需要一个 Component 类来作为依赖注入的入口,并为 @Component 注解提供注解值 UserModule.class

/** * 作者:叶应是叶 * 时间:2018/7/8 17:14 * 描述: */@Component(modules = {UserModule.class})public interface UserComponent {    void inject(Main2Activity mainActivity);}
/** * 作者:叶应是叶 * 时间:2018/7/8 16:35 * 描述: */public class Main2Activity extends AppCompatActivity {    private static final String TAG = "Main2Activity";    @Inject    User user1;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        DaggerUserComponent.builder().build().inject(this);        Log.e(TAG, "user1: " + user1);        Log.e(TAG, "user1 name : " + user1.getName());    }}

运行结果如下所示

user1: com.leavesc.dagger2samples.test2.User@420cb218user1 name : user default name

四、带有参数的依赖对象

修改 User 类,为之添加一个带有参数的构造函数

/** * 作者:叶应是叶 * 时间:2018/7/8 17:11 * 描述: */public class User {    private String name;    public User() {        name = "user default name";    }    public User(String name) {        this.name = name;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }}

假设我们在 UserModule 中要调用的是 User 的有参构造函数,那此时就需要通过 UserModule 的构造函数从外部向它传入字符串参数了

此处也不直接将成员变量 name 传给 provideUser() 方法,而是新建一个 provideName() 方法用于实现依赖注入,这也是为了尽量解耦

/** * 作者:叶应是叶 * 时间:2018/7/8 17:12 * 描述: */@Modulepublic class UserModule {    private String name;    public UserModule(String name) {        this.name = name;    }    @Provides    public String provideName() {        return name;    }    @Provides    public User provideUser(String name) {        return new User(name);    }}

由于之前 UserModule 只有无参构造函数,所以在使用 DaggerUserComponent 进行注入时无需显式传入 UserModule 对象,此时 UserModule 的构造函数需要传入参数了,所以现在只能显示调用 userModule() 方法传入 UserModule 对象

/** * 作者:叶应是叶 * 时间:2018/7/8 16:35 * 描述: */public class Main2Activity extends AppCompatActivity {    private static final String TAG = "Main2Activity";    @Inject    User user1;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        DaggerUserComponent.builder().userModule(new UserModule("leavesC")).build().inject(this);        Log.e(TAG, "user1: " + user1);        Log.e(TAG, "user1 name : " + user1.getName());    }}

运行结果如下所示

user1: com.leavesc.dagger2samples.test2.User@420c4a30user1 name : leavesC

五、@Singleton

假设在 Main2Activity 中有两个 User 对象需要进行实例化,按照以上的使用方式,在依赖注入时是会为每个不同的变量重新 new 一个实例的

/** * 作者:叶应是叶 * 时间:2018/7/8 16:35 * 描述: */public class Main2Activity extends AppCompatActivity {    private static final String TAG = "Main2Activity";    @Inject    User user1;    @Inject    User user2;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        DaggerUserComponent.builder().userModule(new UserModule("leavesC")).build().inject(this);        Log.e(TAG, "user1: " + user1);        Log.e(TAG, "user1 name : " + user1.getName());        Log.e(TAG, "user2: " + user2);        Log.e(TAG, "user2 name : " + user2.getName());    }}

运行结果如下所示,可以看到 user1 和 user2 的内存地址并不相同

user1: com.leavesc.dagger2samples.test2.User@420cb780user1 name : leavesCuser2: com.leavesc.dagger2samples.test2.User@420cba90user2 name : leavesC

而为了实现单例模式,此处需要使用到 @Singleton 注解

/** * 作者:叶应是叶 * 时间:2018/7/8 17:12 * 描述: */@Modulepublic class UserModule {    private String name;    public UserModule(String name) {        this.name = name;    }    @Provides    public String provideName() {        return name;    }    @Provides    public User provideUser(String name) {        return new User(name);    }}
/** * 作者:叶应是叶 * 时间:2018/7/8 17:14 * 描述: */@Component(modules = {UserModule.class})public interface UserComponent {    void inject(Main2Activity mainActivity);}

此处重新运行应用,就可以看到 user1 和 user2 的内存地址是相同的了

user1: com.leavesc.dagger2samples.test2.User@420c86d8user1 name : leavesCuser2: com.leavesc.dagger2samples.test2.User@420c86d8user2 name : leavesC

六、@Named

由于 User 类有两个构造函数,有时候我们也需要指定要由哪个构造函数来初始化 User,此时就需要用到 @Named 注解

修改 UserModule 类,增加 provideUser2() 方法,并为 provideUser2()provideUser2() 方法声明 @Named 注解,注解值用于配对需要实现依赖注入的成员变量,只要成员变量声明的 @Named 注解的属性值与这两个方法的某个注解值相等,就会依赖该方法来初始化成员变量

/** * 作者:叶应是叶 * 时间:2018/7/8 17:12 * 描述: */@Modulepublic class UserModule {    private String name;    public UserModule(String name) {        this.name = name;    }    @Provides    public String provideName() {        return name;    }    @Provides    @Singleton    @Named("no empty")    public User provideUser(String name) {        return new User(name);    }    @Provides    @Singleton    @Named("empty")    public User provideUser2() {        return new User();    }}
/** * 作者:叶应是叶 * 时间:2018/7/8 16:35 * 描述: */public class Main2Activity extends AppCompatActivity {    private static final String TAG = "Main2Activity";    @Inject    @Named("no empty")    User user1;    @Inject    @Named("empty")    User user2;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        DaggerUserComponent.builder().userModule(new UserModule("leavesC")).build().inject(this);        Log.e(TAG, "user1: " + user1);        Log.e(TAG, "user1 name : " + user1.getName());        Log.e(TAG, "user2: " + user2);        Log.e(TAG, "user2 name : " + user2.getName());        startActivity(new Intent(this, Main3Activity.class));    }}

运行结果如下所示

user1: com.leavesc.dagger2samples.test2.User@420cd128user1 name : leavesCuser2: com.leavesc.dagger2samples.test2.User@420cd438user2 name : user default name

七、@Qualifier

先看下注解 @Named 的声明,该注解就使用到了 @Qualifier

@Qualifier@Documented@Retention(RUNTIME)public @interface Named {    /** The name. */    String value() default "";}

由于注解 @Named通过比较字符串的相等性来实现配对的,出错的可能性并不算低,而且也不够优雅,此时就可以通过 @Qualifier 来自己实现同样的功能

声明两个注解,用来表示在初始化 User 变量时是调用哪个构造函数

/** * 作者:叶应是叶 * 时间:2018/7/8 20:34 * 描述: */@Qualifier@Retention(RetentionPolicy.RUNTIME)public @interface UserWithoutParameter {}
/** * 作者:叶应是叶 * 时间:2018/7/8 20:34 * 描述: */@Qualifier@Retention(RetentionPolicy.RUNTIME)public @interface UserWithParameter {}

然后直接替换 @Named 即可实现与之相同的功能

@Provides    @Singleton    //@Named("no empty")    @UserWithParameter    public User provideUser(String name) {        return new User(name);    }    @Provides    @Singleton    //@Named("empty")    @UserWithoutParameter    public User provideUser2() {        return new User();    }
@Inject    //@Named("no empty")    @UserWithParameter    User user1;    @Inject    //@Named("empty")    @UserWithoutParameter    User user2;

八、延迟加载

Dagger2 也支持延迟加载,在需要的时候才对成员变量进行初始化,需要依赖于泛型接口 Lazy

/** * 作者:叶应是叶 * 时间:2018/7/8 16:35 * 描述: */public class Main2Activity extends AppCompatActivity {    private static final String TAG = "Main2Activity";    @Inject    Lazy
user3; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerUserComponent.builder().userModule(new UserModule("leavesC")).build().inject(this); User user = user3.get(); Log.e(TAG, "user3-1: " + user); Log.e(TAG, "user3 name-1 : " + user.getName()); }}

九、强制加载

Dagger2 支持在每次获取成员变量值时都返回一个重新初始化的对象,除非你使用了 @Singleton 注解要求只实例化一次

/** * 作者:叶应是叶 * 时间:2018/7/8 16:35 * 描述: */public class Main2Activity extends AppCompatActivity {    private static final String TAG = "Main2Activity";    @Inject    Provider
user4; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerUserComponent.builder().userModule(new UserModule("leavesC")).build().inject(this); User user = user4.get(); Log.e(TAG, "user4-1: " + user); Log.e(TAG, "user4 name-1 : " + user.getName()); user = user4.get(); Log.e(TAG, "user4-2: " + user); Log.e(TAG, "user4 name-2 : " + user.getName()); user = user4.get(); Log.e(TAG, "user4-2: " + user); Log.e(TAG, "user4 name-2 : " + user.getName()); }}

运行结果如下所示,可以看到 user 变量的内存地址每次各不相同

user4-1: com.leavesc.dagger2samples.test2.User@420cad48    user4 name-1 : Hello    user4-2: com.leavesc.dagger2samples.test2.User@420cb148    user4 name-2 : Hello    user4-2: com.leavesc.dagger2samples.test2.User@420cb548    user4 name-2 : Hello

十、组件间的依赖

假设现在有个需求,在多个地方中都需要获取系统服务 LocationManager,而获取 LocationManager 是需要通过 Context 来获取的,为了避免需要重复传递 Context 对象,此时就可以选择通过组件间的依赖将 Context 的获取方法移交给另外的 Component

LocationManager locationManager = (LocationManager)this.getSystemService(Context.LOCATION_SERVICE);

首先,通过 **ApplicationModule **和 ApplicationComponent 来统一对外提供 Context

/** * 作者:叶应是叶 * 时间:2018/7/8 19:25 * 描述: */@Modulepublic class ApplicationModule {    private Context context;    public ApplicationModule(Context context) {        this.context = context;    }    @Provides    public Context provideContext() {        return context;    }}
/** * 作者:叶应是叶 * 时间:2018/7/8 19:26 * 描述: */@Component(modules = {ApplicationModule.class})public interface ApplicationComponent {    Context getContext();}

然后在 Application 类中实现依赖注入,使得对外提供的 Context 对象统一都是 ApplicationContext

/** * 作者:叶应是叶 * 时间:2018/7/8 19:45 * 描述: */public class RealApplication extends Application {    public static ApplicationComponent applicationComponent;    @Override    public void onCreate() {        super.onCreate();        applicationComponent = DaggerApplicationComponent.builder().applicationModule(new ApplicationModule(this)).build();    }}

通过注解值 dependencies 来指定 ActivityComponent 需要的 Context 要从哪个 Component 中获取

/** * 作者:叶应是叶 * 时间:2018/7/8 19:33 * 描述: */@Component(dependencies = {ApplicationComponent.class}, modules = {ActivityModule.class})public interface ActivityComponent {    void inject(Main3Activity mainActivity);}
/** * 作者:叶应是叶 * 时间:2018/7/8 19:27 * 描述: */@Modulepublic class ActivityModule {    @Provides    LocationManager provideLocationManager(Context context) {        return (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);    }}

然后,在需要 LocationManager 的地方就可以通过 DaggerActivityComponent 来间接获取,而无需直接依赖于 Context 对象

/** * 作者:叶应是叶 * 时间:2018/7/8 16:35 * 描述: */public class Main3Activity extends AppCompatActivity {    private static final String TAG = "Main3Activity";    @Inject    LocationManager locationManager;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        DaggerActivityComponent.builder().applicationComponent(RealApplication.applicationComponent).build().inject(this);        Log.e(TAG, "locationManager: " + locationManager);    }}

此外也提供上述所有示例代码:

转载地址:http://wpnqa.baihongyu.com/

你可能感兴趣的文章
python开发_python中str.format()
查看>>
HTML5, CSS3, ES5新的web标准和浏览器支持一览 转
查看>>
ThinkPHP3.0启动过程
查看>>
【leetcode】Longest Common Prefix (easy)
查看>>
JAX-WS(JWS)发布WebService
查看>>
Centos7安装docker-compse踩过的坑
查看>>
细说Nullable<T>类型
查看>>
oracle 插入表数据的4种方式
查看>>
7.Ajax
查看>>
Linux vi/vim编辑器常用命令与用法总结
查看>>
对于 url encode decode js 和 c# 有差异
查看>>
centos rz sz安装
查看>>
mysql 修改列为not null报错Invalid use of NULL value
查看>>
epoll源码分析
查看>>
朱晔和你聊Spring系列S1E4:灵活但不算好用的Spring MVC
查看>>
Java使用Try with resources自动关闭资源
查看>>
china-pub十一周年庆,多重优惠隆重登场,千万别错过哟!
查看>>
HDU 3068 最长回文(manacher算法)
查看>>
二叉树
查看>>
Python featureClass clip Tin
查看>>