Dagger2教程

依赖注入(Dependency Injection, DI)

依赖注入简单的说就是将实例对象交由第三方管理,目的是降低耦合度。dagger2的依赖注入是通过自动生成代码的方式来进行注入的。

添加dagger依赖:

annotationProcessor "com.google.dagger:dagger-compiler:$rootProject.daggerVersion"
compile "com.google.dagger:dagger :$rootProject.daggerVersion"
compile "com.google.dagger:dagger-android:$rootProject.daggerVersion"
annotationProcessor "com.google.dagger:dagger-android-processor:$rootProject.daggerVersion"

其中daggerVersion为2.13
github代码:https://github.com/dtjc/DaggerExample
(注:每一个commit相当于本教程的一个步骤)

@Inject

创建一个LoginContract接口和一个LoginActivity,接口如下:

public interface LoginContract {
    interface LoginView extends BaseView{
        void finishLogin();
    }

    interface LoginPresenter extends BasePresenter<LoginView>{
        void attachView(LoginView view);
        void login(String user, String password);
    }
}

BaseView和BasePresenter是一个基本接口。
LoginPresenterImp类,@Inject注解的意思是让dagger使用这个构造器创建实例。

public class LoginPresenterImpl implements LoginContract.LoginPresenter {

    private LoginContract.LoginView loginView;

    @Inject
    public LoginPresenterImpl(){}

    @Override
    public void attachView(LoginContract.LoginView view) {
        loginView = view;
    }

    @Override
    public void login(String user, String password) {}

}

创建一个LoginComponent,component是用来完成注入过程的一个桥梁,调用其inject()函数后即可完成注入。

@Component
public interface LoginComponent {
    void inject(LoginActivity loginActivity);
}

然后在LoginActivity中声明LoginPresenterImpl对象并使用@Inject注解对其进行注入,注意其访问属性不可以是私有的:

@Inject
LoginPresenterImpl presenter;

onCreate函数:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_login);
    DaggerLoginComponent.builder().build().inject(this);
    presenter.attachView(this);
}

DaggerLoginComponent是dagger自动生成的类(build后会生成),dagger会为每个component生成一个以Dagger为前缀的类,真正的注入是发生在LoginActivity_MembersInjector的injectMembers(LoginActivity instance)中:

@Override
public void injectMembers(LoginActivity instance) {
  if (instance == null) {
    throw new NullPointerException("Cannot inject members into a null reference");
  }
  instance.presenter = presenterProvider.get();
}

看到instance.presenter=presenterProvide.get(),这也是为什么presenter不能是私有的原因。到这,一个简单的dagger依赖注入就完成了。

@Module与@Provides

对于第三方库和接口,inject就显得无能为力了,这时就需要@Module和@Provides了。Provides需要在Module内使用。与对类构造器标注@Inject类似,@Provides用于提供实例对象。

@Module
public class AppModule {
    @Provides
    public ExecutorService provideExecutorService(){
        return Executors.newCachedThreadPool();
    }
}

为LoginComponent添加AppModule.class,其中module可以有多个,中间用’,’隔开:

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

在LoginPresenterImpl中标注@Inject

@Inject
ExecutorService es;

在login中使用

@Override
public void login(final String user,final String password) {
    es.submit(new Runnable() {
        @Override
        public void run() {
            Log.i("login","user: " + user + ", password: " + password);
        }
    });
}

需要在activity的onCreate中调用

presenter.login("abc","123456");

可以声明一个有参构造函数的module

public AppModule(Context context){
    this.context = context;
}

为Context提供实例

@Provides
public Context context(){
    return context;
}

注入

DaggerLoginComponent.builder()
        .appModule(new AppModule(getApplicationContext()))
        .build()
        .inject(this);

@Singleton 与 @Scope

这里会有一个问题,就是对于每一次inject注入,dagger都会生成一个实例,而对于ExecutorsService,应该是全局单例的,而不是每次生成一个实例。@Singleton与@Scope能解决上面的问题,其中@Singleton是@Scope的一个实现。在AppModule中为provideExecutorService加上@Singleton注解.

@Provides
@Singleton
public ExecutorService provideExecutorService(){
    return Executors.newCachedThreadPool();
}

module中Scope注解必须与component一致,因此也要在LoginComponent接口声明@Singleton注解.同时创建一个AppComponent接口,如下所示:

@Component(modules = {AppModule.class})
@Singleton
public interface AppComponent {
    void inject(MyApplication myApplication);
}

在MyApplication和LoginPresenterImpl中进行注入,并打印ExecutorService的地址。MyApplication继承自Application.

public class MyApplication extends Application {

    private static final String TAG = "MyApplication";
    @Inject
    ExecutorService es;

    @Override
    public void onCreate() {
        super.onCreate();
        DaggerAppComponent.builder().build().inject(this);
        Log.i(TAG,"ExecutorService: " + es.toString());
    }
}

然而你会发现,这两个并不是同一个实例。说好的singleton呢???这是因为scope(注:singleton是scope默认实现) 作用域是与component绑定的,LoginComponent与AppComponent是两个完全独立的component,因此会生成两个实例。解决方案有两个,一是使用component的依赖关系,即component依赖component; 二是使用subcomponent。

component依赖

component依赖component,component的scope不能相同,因此先实现一个ActivityScoped(当然还可以实现一个FragmentScoped):

@Scope
@Documented
@Retention(RUNTIME)
public @interface ActivityScoped {}

在LoginPresenterImpl上加上@ActivityScoped

@ActivityScoped
public class LoginPresenterImpl implements LoginContract.LoginPresenter {

LoginComponent的module不应该是AppModule,所以创建一个LoginModule,并提供一个LoginPresenter接口实例,方法参数也是通过注入的形式来传递的(相当于方法注入,也就是说下面的@ActivityScoped可省略,这里加上只是为了易读性)。通过@Inject LoginContract.LoginPresenter presenter进行注入。注意与之前的@Inject LoginPresenterImpl present的微小区别,如果不在LoginModule中提供以下方法,则无法通过@Inject LoginContract.LoginPresenter presenter进行注入。通过使用@ActivityScoped,可以保证在LoginComponent里的LoginPresenter 都是同一个实例。

@Module
public class LoginModule {
    @Provides
    @ActivityScoped
    public LoginContract.LoginPresenter providePresenter(LoginPresenterImpl presenter){
        return presenter;
    }
}

修改LoginComponent的注解为:

@Component(modules = {LoginModule.class},dependencies = {AppComponent.class})
@ActivityScoped

还需要在AppComponent中将ExecutorService暴露给LoginComponent,即在AppComponent中加入如下方法:

ExecutorService executorService();

在MyApplication中声明一个AppComponent;

public AppComponent appComponent;

MyApplication注入

appComponent = DaggerAppComponent.builder().build();
appComponent.inject(this);

LoginActivity注入

DaggerLoginComponent.builder()
        .appComponent(((MyApplication)getApplicationContext()).appComponent)
        .build()
        .inject(this);

这样就完成了component之间的依赖注入。

subcomponent

在android中使用更多的是subcomponent,这是因为activity是app的组件,而fragment又是activity的组件。将LoginComponent改为subcomponent。需要注意的是子组件与父组件的scope也不能是相同的。

@Subcomponent(modules = {LoginModule.class})
@ActivityScoped
public interface LoginComponent {
    void inject(LoginActivity loginActivity);
    @Subcomponent.Builder
    interface Builder{
        LoginComponent build();
    }
}

@Subcomponent.Builder用于指定接口,提供必要的方法来构造subcomponent。
将子组件添加到父组件中很简单,在父组件的module中添加subcomponent,并在父组件中声明子组件的builder构造器,如下:

@Module(subcomponents = {LoginComponent.class})
public class AppModule {

在AppComponent中声明builder

LoginComponent.Builder loginComponent();

在LoginActivity中注入:

((MyApplication)getApplicationContext()).appComponent
        .loginComponent()
        .build()
        .inject(this);

Lazy与Provider注入

Lazy注入就是在使用时再进行初始化。通过@Inject Lazy进行注入,调用get()方法获取实例。

@Inject
Lazy<LoginPresenterImpl> presenterLazy;

presenterLazy.get().attachView(this);
presenterLazy.get().login("abc","123456");

Provider的使用与Lazy一样,也是通过@Inject Provider进行注入,调用get()方法获取实例。对于没有提供scope作用域的@Providers方法和类,调用get()方法后,会创建一个新的对象并返回;对于提供了scope作用域的@Providers方法和类,则会返回同一个实例。

@Named与@Qualifier

在AppModule中加入下面的代码,build的时候dagger就会报错,这是因为有两个Provides可以提供ExecutorService,注入时,dagger不知道应该调用哪个方法进行注入

@Provides
@Singleton
ExecutorService provideSingleThreadPool(){
    return Executors.newSingleThreadExecutor();
}

使用@Named

@Provides
@Singleton
@Named("cacheThreadPool")
ExecutorService provideExecutorService(){ return Executors.newCachedThreadPool(); }

@Provides
@Singleton
@Named("singleThreadPool")
ExecutorService provideSingleThreadPool(){ return Executors.newSingleThreadExecutor(); }

@Named注入所需要的实例

@Inject
@Named("cacheThreadPool")
ExecutorService es;

使用@Qualifier,自定义两个qualifier注解:

@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheThreadPool {}

@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface SingleThreadPool {}

使用自定义的qualifier注解对方法注解:

@Provides
@Singleton
@CacheThreadPool
ExecutorService provideExecutorService(){ return Executors.newCachedThreadPool(); }

@Provides
@Singleton
@SingleThreadPool
ExecutorService provideSingleThreadPool(){ return Executors.newSingleThreadExecutor(); }

注入

@Inject
@SingleThreadPool
ExecutorService es;

@BindsInstance

@BindsInstance使得component可以在构建时绑定实例:

@Component.Builder
interface Builder {
    @BindsInstance
    AppComponent.Builder application(Context context);
    AppComponent build();
}

构建时绑定

appComponent = DaggerAppComponent.builder().application(this).build();

绑定后可以像@Provides和@Inject一样提供实例

@Inject
Context context;

@Binds

@BInds注解可以用来代替@Provides,被@Binds注解的方法返回该方法参数,被@Binds注解的方法必须是抽象的:

@Module
public abstract class LoginModule {
    @Binds
    abstract LoginContract.LoginPresenter providePresenter(LoginPresenterImpl presenter);
}

@ContributesAndroidInjector

该注解在Module里使用,与@Binds一样,被@ContributesAndroidInjector注解的方法必须是抽象的,返回的是一个具体的android组件,方法不应该有参数。该注解会为android组件生成subcomponent,而不需要像之前那样自己实现@Subcomponent。如:

@Module
public abstract class ActivityBindingModule {
    @ActivityScoped
    @ContributesAndroidInjector(modules = LoginModule.class)
    abstract LoginActivity loginActivity();
}

@ContributesAndroidInjector只有配合dagger的android组件,使用起来才更为简便。即使用DaggerApplication,DaggerAppCompatActivity,DaggerFragment,DaggerService以及由dagger实现的另外两大Android组件。当然可以根据Dagger*模仿其代码以及实现其接口。关于这个可以查看Google的todo示例,分支为todo-mvp-dagger.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值