dagger2学习

最近在学习单元测试的过程中,学习一下使用dagger2来做依赖注入

在介绍dagger2的使用前,先来介绍一下DL的几种方式。

  1. set方法
  2. 将依赖作为参数
  3. 将依赖作为构造方法的参数传入(推荐使用这种方式)

下面重点介绍dagger2的使用

1.定义

ModuleDependency生产工厂

一个Module对应代码中的一个类,同时为了表示这是一个Module,而不是一个简单的类,需要添加dagger2里面的一个annotation @Module来进行标识,之所以说Module是一个生产dependency的工厂,是因为Module中有很多方法,而这些方法主要是创建dependency,但是一个Module就是一个类,他里面也有正常的方法,而为了区别哪些是用来创建dependency的方法,需要用dagger2里面的一个annotation @Provides来加以区分。具体实例代码如下:

@Module
public class AppModule {

    @Provides
    public LoginPresenter provideLoginPresenter(UserManager userManager){
        return new LoginPresenter(userManager);
    }

    @Provides
    public UserManager provideUserManager(){
        return new UserManager();
    }

    @Provides
    public PassWordValidator providePassWordValidator(){
        return new PassWordValidator();
    }
}

例如创建一个LoginPresenter对象,会先到module中寻找Loginpresenterprovide方法,这时发现创建LoginPresenter对象需要传入一个UserManager对象,就会在Module中继续寻找UserManagerProvide方法,再调用LoginPresenterProvide方法创建LoginPresenter对象

ComponentDependency生产工厂管理员

Component是一个接口,和Module需要使用@Module一样,也是需要使用@Component来修饰,表示这是一个dagger2Component,实际使用中,由于有多个Module以及Component,我们需要告知Component去哪些Module中寻找Provide方法,这时我们的Component就可以定义为:

@Component(modules = {AppModule.class})  //<=
    public interface AppComponent {
}

其中modules属性接收一个数组,里面是这个Component管理的所有Module

2.使用

方法一:在Component中定义一个Dependency的返回方法

例如我们需要一个Loginpresenter,我们就可以在Component中定义一个Loginpresenter的返回方法,如下:

@Component(modules = {AppModule.class})
public interface AppComponent {
    LoginPresenter loginPresenter();
}

了解完使用方法以后,我们来了解一下dagger2的工作原理,在你的java代码编译成字节码的过程中,dagger2会对所有的Component(就是用 @Component修饰过的interface进行处理,自动生成一个实现了这个interface的类,生成的类名是Component的名字前面加上Dagger,比如我们定义的 AppComponent,对应的自动生成的类叫做DaggerAppComponent。我们知道,实现一个interface需要实现里面的所有方法,因此,DaggerAppComponent是实现了 loginPresenter()这个方法的。实现的方式大致就是从 AppComponent管理的 AppModule里面去找LoginPresenterProvider方法,然后调用这个方法,返回一个LoginPresenter

因此,使用这种方式,当Client需要Dependency的时候,首先需要用DaggerAppComponent这个类创建一个对象,然后调用这个对象的loginPresenter()方法,这样Client就能获得一个LoginPresenter了,这个DaggerAppComponent对象的创建及使用方式如下:

public class MainActivity extends AppCompatActivity {

    LoginPresenter mLoginPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        AppComponent appComponent = DaggerAppComponent.builder().appModule(new AppModule()).build();
        LoginPresenter presenter = appComponent.loginPresenter();
    }
}

方法二:使用Field Injection

使用这种方式的做法是,我们就在LoginActivity里面定义一个LoginPresenterfield,这个field需要使用@Inject修饰一下,然后在onCreate()里面,我们把DaggerAppComponent对象创建出来,调用这个对象的inject方法,把LoginActivity传进去

public class LoginActivity extends AppCompatActivity {

    @Inject
    LoginPresenter mLoginPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        AppComponent appComponent = DaggerAppComponent.builder().appModule(new AppModule()).build();
        appComponent.inject(this);
    }
}

当然,我们需要先在AppComponent里面定义一个inject(LoginActivity loginActivity)方法:

@Component(modules = {AppModule.class})
public interface AppComponent {
    void inject(LoginActivity loginActivity);
}

需要注意的是,@Inject field不能使用private,不然dagger2找不到这个field。不过,需要注意的一点是,这种方式不支持继承,比如说LoginActivity继承自一个 BaseActivity,而@Inject StatManager mStatManager是放在BaseActivity里面的,那么在LoginActivity里面调用 appComponent.inject(this),并不会让BaseActivity里面的 mStatManager得到实例化,你必须在 BaseActivity里面也调用一次appComponent.inject(this);

其他注解标识@SingletonConstructor Injection

如果每次ClientComponent索要一个DependencyComponent都会创建一个新的出来,这可能会导致资源的浪费,或者说很多时候不是我们想要的,比如说,SharedPreferencesUserManagerOkHttpClient, Retrofit这些都只需要一份就好了,不需要每次都创建一个instance,这个时候我们可以给这些DependencyProvider方法加上@Singleton就好了

@Module
public class AppModule {

    @Provides
    @Singleton          
    public OkHttpClient provideOkHttpClient() {
        OkHttpClient okhttpClient = new OkHttpClient.Builder()
                .connectTimeout(30, TimeUnit.SECONDS)
                .build();
        return okhttpClient;
    }
}

其实AppModule里面的很多Provider方法是不需要定义的。比如说在这种情况下,LoginPresenterProvider方法 provideLoginPresenter(UserManager userManager)就不需要定义,你只需要在定义LoginPresenter的时候,给它的Constructor加上 @Inject修饰一下,这也就是我们现在要讨论的Constructor Injection

public class LoginPresenter {

    UserManager mUserManager;

    @Inject
    public LoginPresenter(UserManager userManager) {
        mUserManager = userManager;
    }

    public void login(String name, String pwd){
        if (name==null||name.length()==0){return;}
        if (pwd==null||pwd.length()==0){return;}
        mUserManager.performLogin(name,pwd);
    }
}

dagger2会自动创建这个LoginPresenter所需要的Dependency,所以会去Module里面找到这个LoginPresenter所需的Dependency,交给LoginPresenterConstructor,创建好这Dependency,交给Client,同理我们也不需要提供UserManagerProvider方法,只需要在它的构造方法上添加@Inject。说白了,你只需要给那些不是通过Constructor来创建的Dependency(比如说SharedPreferencesUserApiService等)定义Provider方法。

参考文章

使用dagger2来做依赖注入,以及在单元测试中的应用

你可能感兴趣的:(框架学习)