参考资料:
- 官网资料:https://dagger.dev/android
- Dagger Android 学习:https://juejin.im/post/5cc72061e51d456e6154b4bc
前言
1. 进入Dagger2的世界 详细讲解了Dagger2 的基础用法,这是DaggerAndroid的基础,不懂的朋友,可以点击去了解一下。
2. Dagger2 和 DaggerAndroid的关系:
DaggerAndroid扩展库 是在Dagger2 的基础上,针对Android基本组件开发定制的一套用于对象创建获取的框架。
3. 为啥写这篇文章?
- 有的人已经发现,纯粹使用Dagger2 来进行研发,类似 XXXActivity + XXXComponent + XXXModule,并没有过多的简化代码,反而引入了很多重复的代码。
- 另外,还有的人,看了Dagger2 后,再去看一些基于Dagger2 开发的应用,依然看不懂,比如ContributesAndroidInjector,DaggerApplication等,因为这些都是DaggerAndroid开发库的内容。
- 网上的DaggerAndroid演示文档,理论知识都很多,但是提供的Demo很少有循序渐进的过程,因此本文档,模拟我之前写过的一个应用,来演示 不使用Dagger2 ,仅使用Dagger2 和使用DaggerAndroid 来进行相同开发的不同点。通过循序渐进的过程,了解DaggerAndroid是如何使用的。
Demo说明:
Demo DaggerAndroidTest工程地址先放到 这里 ,强烈建议配合Demo一起阅读,提取码为:g0pd。如果不能下载,请留言告知。
Dagger2或者DaggerAndroid其本质还是为了创建和提供对象。因此,本Demo将演示全局静态对象,局部静态动态,普通对象等,在不使用Dagger2,仅使用Dagger2和使用DaggerAndroid来开发的对比。
为了便于布局文件简化,我引入了DataBinding的简单用法,在Demo里面有相关说明,应该至少可以看懂。
本Demo 借鉴之前开发过的一个类似商店的应用。包含一个登录页面,商店页面,详情页面和我的页面。
-
类关系和数据关系说明:
- App类,继承Application,提供全局单例数据 :AppData。
- LoginActivity,模拟登录页面,可获取AppData,并拥有LoginActivityData的数据。
- MainActivity,主页面,管理三个Fragment,底部为三个Button,负责Fragment的切换。可获取AppData,并提供MainActivityData给Fragment使用。因此MainActivityData 是个 局部单例对象。
- ShoppingFragment,DetailFragment和MyFragment,可获取AppData(全局单例),MainActivityData(局部单例) 和 FragmentData。
-
DaggerAndroidTest工程中提供了4个Module,都可以独立运行。所有的Module中,使用的Data数据都是相同的。
- dagger_android: 不使用Dagger2,我们常见的创建和获取对象的方式。
- dagger_android1 仅使用Dagger2时,如何构建获取对象。
- dagger_android2: 不使用ContributesAndroidInjector时,构建获取对象的方式。
- dagger_android3: 使用ContributesAndroidInjector时,构建获取对象的方式。
-
UI 截图说明:
正式开始进入DaggerAndroid 的学习
1. 创建工程,引入依赖,建议这5个依赖的版本都相同。目前能查到的最新半包围2.32.2,如果还有更新的版本,请自行替换。
//使用Dagger2,具体的版本在http://central.maven.org/maven2上查找
//目前采用最新的2.23.2版本,如果有最新版本,请自行替换
version = [
dagger : "2.23.2"
]
//这几个dagger的版本,最好保持一致。
dependencies = [
"dagger" : "com.google.dagger:dagger:${version.dagger}",
"dagger_compiler" : "com.google.dagger:dagger-compiler:${version.dagger}",
//后三个是DaggerAndroid相关
"dagger_android" : "com.google.dagger:dagger-android:${version.dagger}",
"dagger_android_processor" : "com.google.dagger:dagger-android-processor:${version.dagger}",
"dagger_android_support" : "com.google.dagger:dagger-android-support:${version.dagger}"
]
2. 先演示不使用Dagger2 时,我们一般如何创建和提供数据(不感兴趣的可略过这点):
演示例子在DaggerAndroidTest 工程的dagger_android Module中,可以独立运行
- 全局静态对象,在Application创建,并对外提供该对象的public 获取方法。其他调用类,通过 (App)getApplication.getAppData() 的方式进行获取。
//1. 在App类中创建并提供全局使用的对象。因为App类全局唯一,因此AppData可以说是全局单例的。
public class App extends Application {
private AppData appHelper;
@Override
public void onCreate() {
super.onCreate();
appHelper = new AppData();
public AppData getAppHelper(){
return appHelper;
}
}
//2. 获取方式:
...
App app = (App) getApplication();
AppData appData = app.getAppHelper(); //获取到AppData数据。
...
- LoginActivity类中,除可以获取AppData外,还提供LoginActivityData的创建。直接new一个对象就行。
- MainActivity类,除AppData外,再new一个MainActivityData对象。Fragment可以通过getActivity来获取MainActivityData数据,这一点和AppData类似,因此MainActivityData 就是局部单例对象。
- XXXFragment,可以获取ActivityData,可以获取AppData,也提供了XXXFragmentData的创建获取。
//XXXFragment中获取对象的常见方法。
MainActivity mainActivity = (MainActivity) getActivity();
//获取MainActivity提供的数据。
data.actData = mainActivity.getMainData().getInfo();
//获取App提供的数据
App app = (App) mainActivity.getApplication();
data.appName = app.getAppHelper().getAppName();
data.appVersion = app.getAppHelper().getVersion();
data.userName = app.getAppHelper().getUserName();
data.userPassword = app.getAppHelper().getPassword();
//获取自身数据
data.info = new XXXInfo();
- 这个没啥说的,直接看DaggerAndroid工程中 dagger_android 就行了。
2. 仅使用Dagger2 时,如何创建对象获取对象。
演示例子在DaggerAndroidTest 工程的dagger_android1 Module中,可以独立运行
Dagger2的具体用法,在上一篇中已经说过了,这次只做简单说明
- App中的注入:
@Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppComponent.builder().application(this).build();
appComponent.inject(this);
}
//还要对外提供该Component接口,为简化,提供静态接口。
public static AppComponent getAppComponent(){
return appComponent;
}
- Activity中的大致注入方式:
...
DaggerLoginActivityComponent.builder().appComponent(App.getAppComponent()).build().inject(this);
...
- 还有很多的XXXComponent,XXXModule 直接在Demo里面有。
- XXXFragment的注入,没有实现,层级不好搭建,只为演示Dagger2 如何创建注入对象,目的已经达到。
3. 使用DaggerAndroid扩展库,但不使用ContributesAndroidInjector,来演示对象的创建和获取。
演示例子在DaggerAndroidTest 工程的dagger_android2 Module中,可以独立运行
- 该演示Demo会采用XXXModule + XXXSubcomponent的方式设计。
- 先看AppData的注入获取方式。
//1. AppComponent 可以看出来,不使用dependence,直接全部依赖XXXModule。
@Singleton
@Component(modules = {AndroidInjectionModule.class,
AndroidSupportInjectionModule.class,
AppModule.class,
LoginActivityModule.class,
MainActivityModule.class})
public interface AppComponent { ... }
//2. App类中的注入方式。这是一组固定搭配。使用DaggerAndroid时,Application需要继承HasActivityInjector或者HasSupportFragmentInjector。
//我没有仔细研究,应该是为了分发在Activity或者Framgent中的注入用的。
//因为之后的Activity中,就可以直接是用AndroidInjection.inject(this);来完成注入代码。
public class App extends Application implements HasActivityInjector, HasSupportFragmentInjector {
@Inject
DispatchingAndroidInjector<Activity> dispatchingAndroidInjector;
@Inject
DispatchingAndroidInjector<Fragment> dispatchingAndroidSupportInjector;
@Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppComponent.builder().application(this).build();
appComponent.inject(this);
}
@Override
public AndroidInjector<Activity> activityInjector() {
return dispatchingAndroidInjector;
}
@Override
public AndroidInjector<Fragment> supportFragmentInjector() {
return dispatchingAndroidSupportInjector;
}
- AndroidInjectionModule 和AndroidSupportInjectionModule 是DaggerAndroid提供的Module,简单理解为内部会使用Map的形式来提供对象。AndroidSupportInjectionModule是使用v4包的FragmentActivity时,可以添加。
- 在来看LoginActivity相关的代码
//1. LoginActivityModule的实现。
@Module(subcomponents = LoginActivityComponent.class)
public abstract class LoginActivityModule {
//@Binds用来提供接口,这在Dagger2 中已经说过了。
//@IntoMap + @ClassKey 这是一组固定搭配。就是说如下方式提供的构造器,会映射到某个Map中。用ClassKey来区分。
//回想上AndroidInjectionModule 里面有Map,是不是有点联系了,就是添加到了这个Map里面。
@Binds
@IntoMap
@ClassKey(LoginActivity.class)
abstract AndroidInjector.Factory<?> bindLoginActivityInjectorFactory(LoginActivityComponent.Builder builder);
//如果XXXModule已经有了@Binds,再使用@Provides时,需要使用static。
@Provides
static LoginActivityData provideData(){
return new LoginActivityData();
}
//2. LoginActivityComponent 修改如下,也算是固定搭配了。AndroidInjector.Builder为已经过时的方法。
@Subcomponent(modules = {AndroidInjectionModule.class})
public interface LoginActivityComponent extends AndroidInjector<LoginActivity> {
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<LoginActivity>{
}
}
//3. LoginActivity的onCreate接口中,增加入住代码,之后就可以使用AppData和LoginData了。
...
AndroidInjection.inject(this);
...
- 参考LoginActivity相关的代码,依葫芦画瓢,可以和容易的写出MainActivity相关的注入代码。
- XXXFragment的注入我们没有继续实现,因为有AppData的全局单例数据和LoginActivity这样的普通数据,已经可以说明一些注入步骤了。
4. 使用DaggerAndroid扩展库,并使用ContributesAndroidInjector来演示对象的创建和注入(这是重点内容哦~)
演示例子在DaggerAndroidTest 工程的dagger_android3 Module中,可以独立运行
- 在dagger_android2 中,依然存在XXXActivity + XXXModule + XXXSubcomponent的组合,重复代码还是挺多的,而且固定代码也很多,怎么办?使用ContributesAndroidInjector就可以再一次简化代码。
- 先看Application相关代码,对比上面的dagger_android2,少了一些代码了。
//1. AppModule类不做修改,AppComponent的实现。和上面的相比,需要继承AndroidInjector<App>。
@AppScope
@Component(modules = {AndroidInjectionModule.class, AndroidSupportInjectionModule.class,
AppModule.class,
BuildsActivityModule.class})
public interface AppComponent extends AndroidInjector<App> {
void inject(App app);
//下面这一堆是个固定搭配,目的给AppModule中的@Provide接口,提供外部参数。可以自行修改。
@Component.Builder
interface Builder {
AppComponent build();
@BindsInstance Builder application(Application app);
}
}
//2. App类,继承DaggerApplication,接口中实现注入代码即可。注意继承dagger.android.support.DaggerApplication.
public class App extends DaggerApplication {
@Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
return DaggerAppComponent.builder().application(this).build(); //在该位置注入。
}
}
- 多了一个BuildsActivityModule,开始使用ContributesAndroidInjector自动构造对象,本例中构造了LoginActivity和MainActivity类。同时这两个类,都可以有自己XXXModule,来提供自身使用的数据。
@Module
public abstract class BuildsActivityModule {
/**
* TODO 知识点:添加了 @ContributesAndroidInjector 后,就不需要 @IntoMap 和 @ClassKey 了。因为会自动添加。
* 如下接口,会自动生成 BuildsActivityModule_ContributeLoginActivity 类
* 该类中会自动使用 @IntoMap 和 @ClassKey。
* @return
*/
@ContributesAndroidInjector(modules = LoginActivityModule.class)
abstract LoginActivity contributeLoginActivity();
/**
* TODO 知识点:
* 如果MainActivityModule需要使用@Scope,同样需要再如下位置添加相同的作用域。
* 否则自动生成的ActivitySubComponent 会缺少@Scope。
* @return
*/
@ActivityScope
@ContributesAndroidInjector(modules = {MainActivityModule.class,BuildsFragmentModule.class})
abstract MainActivity contributeMainActivity();
}
- contributeMainActivity() 还依赖了个BuildsFragmentModule,如下。依然使用ContributesAndroidInjector来自动构造三个Fragment,且各自Fragment还可以拥有自己独立使用数据的Module。
@Module
public abstract class BuildsFragmentModule {
@FragmentScope
@ContributesAndroidInjector(modules = ShoppingFragmentModule.class)
abstract ShoppingFragment contributeShoppingFragment();
@ContributesAndroidInjector(modules = DetailFragmentModule.class)
abstract DetailFragment contributeDetailFragment();
@ContributesAndroidInjector(modules = MyFragmentModule.class)
abstract MyFragment contributeMyFragment();
}
- @Module中的每一个Activity使用@ContributesAndroidInjector标注后,会自动生成的@Module类和@Subcomponent类。比如LoginActivity,会自动生成BuildsActivityModule_ContributeLoginActivity类和BuildsActivityModule_ContributeLoginActivity.LoginActivitySubcomponent类。MainActivity也一样,甚至Fragment也是一样的。
- @ContributesAndroidInjector自动生成的Module和Subcomponent类,和上一节中的Demo中dagger_android2代码类似。这也是上一节中演示不使用ContributesAndroidInjector的目的。
//这是使用ContributesAndroidInjector后,自动生成的@Module类和@Subcomponent类。会自动使用@Binds,@IntoMap,@ClassKey。
@Module(
subcomponents = BuildsActivityModule_ContributeLoginActivity.LoginActivitySubcomponent.class
)
public abstract class BuildsActivityModule_ContributeLoginActivity {
private BuildsActivityModule_ContributeLoginActivity() {}
@Binds
@IntoMap
@ClassKey(LoginActivity.class)
abstract AndroidInjector.Factory<?> bindAndroidInjectorFactory(
LoginActivitySubcomponent.Factory builder);
//注意:@Subcomponent以内部类的方式实现。
@Subcomponent(modules = LoginActivityModule.class)
public interface LoginActivitySubcomponent extends AndroidInjector<LoginActivity> {
@Subcomponent.Factory
interface Factory extends AndroidInjector.Factory<LoginActivity> {}
}
}
- 现在来看LoginActivity,直接继承DaggerActivity,啥都不用写。这其实和上一节中的LoginActivity类似,只是DaggerActivity进行了相关的封装。DaggerActivity里面已经使用了inject接口。
//LoginActivity只要继承DaggerActivity就可以了,啥都不用写,可以直接@Inject来获取对象。
public class LoginActivity extends DaggerActivity {...}
- 再看MainActivity,由于没有DaggerFragmentActivity,因此可以仿照DaggerActivity自己写一个,也很简单,或者用原始的方法,继承FragmentActivity,实现HasAndroidSupportInjector接口就行。
public class MainActivity extends FragmentActivity implements HasSupportFragmentInjector, HasActivityInjector {
@Inject
DispatchingAndroidInjector<Activity> activityInjector;
@Inject
DispatchingAndroidInjector<Fragment> fragmentInjector;
@Override
public AndroidInjector<Fragment> supportFragmentInjector() {
return fragmentInjector;
}
@Override
public AndroidInjector<Activity> activityInjector() {
return activityInjector;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(this); //需要手动注入代码,一定要写在super方法前面。
super.onCreate(savedInstanceState);
}
}
- XXXFragment也只需要继承DaggerFragment,就可以实现对象的自动注入代码了。唯一的不同点就是,在Fragment中,注入代码的位置在onAttach接口中。
-
此时层级比较清晰,另外,任何一个项目,一定要条例清晰,这样设计才不会有大的偏差。
- DaggerAndroidTest这个Demo的扩展:这个Demo是目前常见的应用研发的一种简单形式。实际上AppData中,可以提供一些网络管理器的获取和DB管理器等的创建和获取,这样,所有的Activity,Fragment等都可以使用。同样的MainActivityData和XXXFragmentData都可能会提供一些复杂的逻辑类对象。
最后的总结:
- 经过上面的学习,DaggerAndroid扩展库应该可以正常使用了,如果还不能正常使用,就得多写,多调试,因为DaggerAndroid如果不熟悉的话,很容易就会写错。
一般这样建议是:和Dagger相关的,一次性不要写太多代码,确保写过的代码可以编译通过。是在找不出来出错点,可以通过慢慢恢复代码的方式来定位问题。我是用了git仓库管理,来比对自己的改动点。 - 在正式的项目开发中,Dagger2 一般不会独立使用。还会配合 MVVP模式中比如LiveData,Room,还有一些比较好的组件,比如RxAndroid等等,一起进行项目开发。
- 此时一定要明确每一个三方库的作用是什么?比如DaggerAndroid的就是 为项目开发中创建和提供对象用的。 MVVP 只是个设计模式,是一个思想。RxAndroid是一个异步任务处理库等等。这样当使用多个三方库时,就不会弄混。
- 有问题,可以留言,我看到了就会回复的。