Dagger2教程

来源:互联网 发布:天龙八部装备精通数据 编辑:程序博客网 时间:2024/06/07 11:06

依赖注入(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.3
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()函数后即可完成注入。

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

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

@InjectLoginPresenterImpl presenter;

onCreate函数:

@Overrideprotected 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)中:

@Overridepublic 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用于提供实例对象。

@Modulepublic 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

@InjectExecutorService es;

在login中使用

@Overridepublic 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提供实例

@Providespublic 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@Singletonpublic ExecutorService provideExecutorService(){    return Executors.newCachedThreadPool();}

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

@Component(modules = {AppModule.class})@Singletonpublic 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

@ActivityScopedpublic 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 都是同一个实例。

@Modulepublic 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})@ActivityScopedpublic 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()方法获取实例。

@InjectLazy<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@SingletonExecutorService 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@CacheThreadPoolExecutorService provideExecutorService(){ return Executors.newCachedThreadPool(); }@Provides@Singleton@SingleThreadPoolExecutorService provideSingleThreadPool(){ return Executors.newSingleThreadExecutor(); }

注入

@Inject@SingleThreadPoolExecutorService es;

@BindsInstance

@BindsInstance使得component可以在构建时绑定实例:@Component.Builderinterface Builder {    @BindsInstance    AppComponent.Builder application(Context context);    AppComponent build();}

构建时绑定

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

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

@InjectContext context;

@Binds

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

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

@ContributesAndroidInjector

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

@Modulepublic 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.

原创粉丝点击