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.
- Dagger2教程
- Dagger2图文完全教程
- Dagger2使用教程
- Dagger2使用教程
- Android MVP+Dagger2使用教程
- Dagger2
- Dagger2
- Dagger2
- Dagger2
- dagger2
- Dagger2
- Dagger2
- Dagger2
- Dagger2
- Dagger2
- Dagger2
- Dagger2
- Dagger2
- jdk源码阅读之AbstractList
- 2017-12-15 centos 7 lamp环境搭建
- 不需要旋转,却能力压群雄的数据结构——非旋Treap 看完还不会你打我
- 中国科技大学网站
- 一只程序猿的养成日记 第一章 第十一节 编写一个函数实现n^k,使用递归实现
- Dagger2教程
- request.getContextPath()获取的是什么路径?
- 基于mvc模式的应用框架之struts(二)
- shell 替换不可见字符^@
- 大学生活随笔
- 3492. 【NOIP2013模拟联考12】数数(count)(循环节/DP)
- 性能测试新法宝:performance.now()
- 兄弟连学python(4)——列表、元祖、字典、集合数据类型介绍
- 一只程序猿的养成日记 第一章 第十二节 输入一个非负整数,返回组成它的数字之和