Dagger2的使用

本文详细介绍使用 Dagger2 进行依赖注入的方法,包括基本配置、简单使用、单例对象注入、自定义作用域注解及全局对象注入等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.背景

1.1 前言

使用RxJava+Retrofit+MVP+Dagger2开发已经是主流了,MVP 模式网上有很多很好的文章,在此不再多述。

1.2 Dagger2的简单理解

Dagger2简单来说就是依赖注入/管理实例的。实际我们可以理解为:Dagger2代替我们之前直接new出对象,Dagger2内部提供一个类似工厂模式,帮助我们创建/管理实例。这样做的好处就是我们在使用实例的时候,并不是在类中直接实例化,这样就降低了耦合性,同时Dagger2也提供了一些注解来方便我们更好的管理这个实例。

2.开发之路

2.1 Dagger2的配置

2.1.1 在Project的bulid.gradle下添加android-apt插件
dagger2配置

  // 添加android-apt插件
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

2.1.2 在Module的bulid.gradle下应用插件
这里写图片描述

// 应用插件
apply plugin: 'com.neenbedankt.android-apt'

2.1.3 在Module的bulid.gradle下添加依赖
这里写图片描述

    // dagger 2 的配置
    compile 'com.google.dagger:dagger:2.5'
    compile 'com.google.dagger:dagger-compiler:2.5'
    // 添加java 注解库
    provided 'org.glassfish:javax.annotation:10.0-b28'

2.2 Dagger2的简单使用使用

使用Dagger2前,我们先大概了解下Dagger2常见的注解及其功能,我们在MVP模式开发中,Dagger2常用在Activity/Fragment中注入Presenter对象:
@Inject: 常用在字段,构造方法上
1) 用在字段上,表示需要注入该字段的对象;

    @Inject
    HomePresenter mHomePresenter;

2) 在构造方法上, 表示通过构造方法提供的具体对象

    @Inject
    public HomePresenter() {}

@Module: 注解在类上 , 一般和@Provides一起使用, 主要是为@Component提供需要的对象,相当于一个容器的作用
@Provides: 注解在@Module注解的类方法上, 提供无参实例对象或者有参实例的参数实例

@Module   //提供需要的对象
public class HomeModule {

    private Context mContext;

    public HomeModule(Context context){
        mContext = context;
    }

    @Provides
    Context providesContext(){
        // 提供上下文对象
        return mContext;
    }
 }

@Component: 注解在类上. 一般结合@Component的modules/dependencies属性一起使用.是Dagger2的沟通桥梁

@Component(modules = HomeModule.class)
public interface HomeComponent {
    void inject(HomeActivity homeActivity);
}

下面先说最简单的无参实例的Dagger2注入:
2.2.1 无参实例的Dagger2注入
无参实例的Dagger2注入方式有两种:区别是在@Module注解的类中是否使用@Provides提供注入实例,先看使用@Provides的方式一
①方式一
首先创建Module,提供实例化对象

@Module
public class HomeModule {

    @Provides// 提供实例化对象,以provide开头
    HomePresenter provideHomePresenter(){
        return new HomePresenter();
    }
}

构建桥梁Component

@Component(modules = HomeModule.class)
public interface HomeComponent {
    void inject(HomeActivity homeActivity);
}

在需要注入的类中进行注入,此处为HomeActivity

注解标注要注入的对象

   @Inject
    HomePresenter mHomePresenter;

此时,我们需要Make Module ‘app’,如图:
make module
这时会在build -> generated -> source -> apt -> debug ->…找到以Dagger开头的类,生成此类的话,就说明Dagger2编译实例成功。在HomeActivity中便可调用以下代码注入对象

 DaggerHomeComponent.builder().homeModule(new HomeModule()).build().inject(this);

至此,无参Dagger2注入大功告成。
②方式二
下面我们看下无参实例不使用@Provides的方式二:
在方式一的基础上进行修改
修改@Module注解的类

@Module
public class HomeModule {
    //去掉@Provides注解的方法
}

在需要被注入的类的构造中上注解@Inject,以表示要注入这个类的实例

public class HomePresenter implements IHomePresenter{

    // @Inject 注解在构造方法上,用来提供实例
    @Inject
    public HomePresenter() {}
}

其实,@Provides注解,最经常用到的地方是提供注入有参实例时的参数,下面我们就看一下有参实例的Dagger2注入:
2.2.2 有参实例的Dagger2注入
NewsPresenter的构造中需要传入NewsActivity实现的接口

public class NewsPresenter implements INewsPresenter{
    private INewsView mView;

    // @Inject 注解在构造方法上,用来提供实例
    @Inject
    public NewsPresenter(INewsView view) {
        this.mView = view;
    }
}

在NewsModule中我们发现构造中具有参数,所以我们要通过注解提供这个参数

@Module
public class NewsModule {
    private INewsView mView;

    public NewsModule(INewsView mView) {
        this.mView = mView;
    }

    @Provides
    INewsView providesINewsView(){
        // 提供构造中需要的参数
        return mView;
    }
}

NewsComponent搭建注入的桥梁

@Component(modules = NewsModule.class)
public interface NewsComponent {
    void inject(NewsActivity newsActivity);
}

在NewsActivity中注解NewsPresenter的成员变量

    @Inject
    NewsPresenter newsPresenter;

Make Module之后,在NewsActivity中注入NewsPresenter实例的代码:

DaggerNewsComponent.builder().newsModule(new NewsModule(this)).build().inject(this);

以上就是Dagger2的基本运用。

2.3 Dagger2在项目中的使用

2.3.1 Dagger2注入单例对象
在项目中,有时我们需要注入一个单例的对象如果我们使,首先声明,用@Inject注入两次实例,结果是指向不同的对象的,如下
在FirstActivity注入一个单例Person

public class Person {
    public String name = "Mr.zhang";
    public String age = "26";
}

在FirstActivity中

    @Inject
    Person person1;

    @Inject
    Person person2;

这里我们可以通过Dagger2的@Singleton注解,首先需要在@Module注解的类中的提供实例的方法上@Singleton注解

@Module
public class PersonModule {
    @Provides
    @Singleton
    Person providePerson() {
        return new Person();
    }
}

然后再PersonComonent上进行@Singleton

@Singleton
@Component(modules = PersonModule.class)
public interface PersonComonent {
    void inject(FirstActivity firstActivity);
}

完成以上两步,即可将HomePresenter单例化

2.3.2 @Scope自定义作用域注解——注入与Activity同生命周期的实例
其实@Singleton也属于@Scope自定义作用域注解的一种,只是它是java写好的,用来提供一个单例实例。那么在开发中我们常常需要一个实例在某个或者某几个Activity/Fragment中实现单例,这时我们就需要@Scope自定义作用域了。
首先看在一个Activity/Fragment实现单例,举一个简单的例子:在SecondActivity中注入一个User的javabean实例

public class User {
    private String username;
    private int password;

    public User(String username, int password) {
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public int getPassword() {
        return password;
    }
}

我们需要一个@Scope自定义作用域注解

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface PreActivity {

}

其中

RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;

这样,我们就可以用@PreActivity注解了,比如在UserModule中

@Module
public class UserModule {
    private String mUsername;
    private int mPassword;
    public UserModule(String username,int pasword) {
        this.mUsername = username;
        this.mPassword = pasword;
    }

    @Provides
    String provideUsername(){
        return mUsername;
    }
    @Provides
    int providePassword(){
        return mPassword;
    }

    @Provides
    @PreActivity
    User providesUser(String username,int pasword) {
        return new User(username,pasword);
    }

}

已经给实例注解上了,下面就是要看注入哪里了,也就是Component中:

@PreActivity
@Component(modules = UserModule.class)
public interface UserComonent {
    void inject(SecondActivity secondActivity);
}

在SecondActivity中进行注入User实例

    @Inject
    User user;

Make Module之后,就会出现DaggerUserComonent,在SecondActivity中调用

 DaggerUserComonent.builder().userModule(new UserModule("123",456)).build().inject(this);

这样就大功完成。我们可以在SecondActivity注入两个User实例,即:

    @Inject
    User user1;

    @Inject
    User user2;

Toast一下,可以看出这两个User实例的引用地址是同一个的,也就是在SecondActivity的生命周期中,实现了User单例。下面实现在SecondActivity和ThirdActivity两个Activity中实现User单例,其实很简单,修改UserComonent:

@PreActivity
@Component(modules = UserModule.class)
public interface UserComonent {
    void inject(SecondActivity secondActivity);
    void inject(ThirdActivity thirdActivity);

}

这样就表示:User和SecondActivity及ThirdActivity都搭建了桥梁,Make Module一下,在ThirdActivity中注入

    @Inject
    User user;
 DaggerPersonComonent.builder().personModule(new PersonModule()).build().inject(this);

可以记录一下,SecondActivity和ThirdActivity中的User,发现都是指向一个引用地址。这里你可以试一下,在另外一个Activity中注入一个没有@PreActivity注解的User实例,会发现,这个User实例指向不同的引用

2.3.3 Dagger2注入全局对象
在项目开发中,我们经常需要一些全局的对象,比如Retrofit实例/DaoSession实例等等,并且希望它的生命周期和Application一样。这个时候我们就需要定义一个ApplicationComponent了。另外还有一个好处就是可以让一个ActivityComponent依赖ApplicationComponent,从而可以利用ApplicationComponent向下提供的实例。
先写一个@Scope作用域注解

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface PreApplication {

}

再写一个MyApplicationModule,提供一个Context 实例:

@Module
public class MyApplicationModule {
    private  Context mContext;

    public MyApplicationModule(Context context) {
        mContext = context;
    }

    @Provides
    @PreApplication
    Context provideApplicationContext() {
        return mContext;
    }
}

再写一个MyApplicationComponent,向下提供Context实例:

@PreApplication
@Component(modules = MyApplicationModule.class)
public interface MyApplicationComponent {
    // 向对其依赖的Component提供Context对象
    Context proContext();
}

既然MyApplicationComponent已经向下提供了一个Context对象,如何利用呢?因为下层的Component依赖于MyApplicationComponent,所以先将MyApplicationComponent在MyApplication实例化

public class MyApplication extends Application {

    public static MyApplicationComponent sAppComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        sAppComponent = DaggerMyApplicationComponent.builder().myApplicationModule(new MyApplicationModule(this)).build();
    }
}

前奏已经写完,接下来就是下层组件利用MyApplicationComponent提供的Context对象了,下面进行在ListActivity中注入一个MyAdapter对象

public class MyAdapter {

    private Context mContext;
    private int mLayoutId;
    private List<String> mData;

    public MyAdapter(Context context, int layoutId, List<String> data) {
        this.mContext = mContext;
        this.mLayoutId = mLayoutId;
        this.mData = mData;
    }
}

MyAdapter的构造中需要三个参数,下面看下MyAdapterModule怎么提供的这三个参数

@Module
public class MyAdapterModule {

    private int mLayoutId;
    private List<String> mData;

    public MyAdapterModule(int layoutId, List<String> data) {
        this.mLayoutId = layoutId;
        this.mData = data;
    }

    @Provides
    int provideLayoutId(){
        return mLayoutId;
    }

    @Provides
    List<String> provideList(){
        return mData;
    }

    @Provides
    @PreActivity  // 注意:声明其所构造的对象单例
    MyAdapter provideMyAdapterContext( Context context, int layoutId, List<String> data){
        return new MyAdapter(context,layoutId,data);
    }
}

从MyAdapterModule可以看出,我们只使用了@Provides提供了两个参数,Context并没有提供。(注意需要添加@PreActivity声明其所构造的对象单例)
接着就是下层桥梁:MyAdapterComponent

@PreActivity  //对应MyAdapterModule的注解
@Component(dependencies = MyApplicationComponent.class,modules = MyAdapterModule.class)
public interface MyAdapterComponent {
    void inject(ListActivity listActivity);
}

从这里可以看到组件间的依赖关系,在ListActivity进行注入:

        DaggerMyAdapterComponent.builder()
                .myApplicationComponent(MyApplication.sAppComponent)
                .myAdapterModule(new MyAdapterModule(R.layout.item,data))
                .build()
                .inject(this);

这样就完成了组件间依赖注入。
奉上本文的Demo地址:https://github.com/Mr-zhang0101/Dagger2Demo.git
感谢:
http://blog.youkuaiyun.com/lisdye2/article/details/51942511
http://blog.youkuaiyun.com/u012702547/article/details/52213706

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值