热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Dagger2完全解析(四),Android中使用Dagger2

Dagger2完全解析系列:Dagger2完全解析(一),Dagger2的基本使用与原理Dagger2完全解析(二),进阶使用Lazy、Qualifier、Scope等Dagger

Dagger 2 完全解析系列:

Dagger 2 完全解析(一),Dagger 2 的基本使用与原理

Dagger 2 完全解析(二),进阶使用 Lazy、Qualifier、Scope 等

Dagger 2 完全解析(三),Component 的组织关系与 SubComponent

Dagger 2 完全解析(四),Android 中使用 Dagger 2

Dagger 2 完全解析(五),Kotlin 中使用 Dagger 2

Dagger 2 完全解析(六),dagger.android 扩展库的使用

上面文章地址是我博客上的地址,本系列文章是基于 Google Dagger 2.11-rc2 版本

在理解了 Dagger 2 完全解析系列的前三篇文章后,可能还是会对在 Android 实际项目中如何使用 Dagger 2 有些疑问,本文以 GoogleSamples 的 android-architecture(mvp 分支) 为例,逐步说明如何在 ToDo 项目中使用 Dagger 2。

本文中代码示例的地址:https://github.com/JohnnyShieh/ToDo/tree/mvp

使用 Dagger 2 后的代码地址:https://github.com/JohnnyShieh/ToDo/tree/mvp-dagger2

Android 中使用 Dagger 2

为了具有代表性,我选择 android-architecture 中的 todo-mvp 作为例子,MVP 架构大家都比较熟悉。现在假设我们的 Android 项目为 ToDo,那么如何引入 Dagger 2 实现依赖注入?

绘制依赖关系图

在使用 Dagger 2 之前,需要理清项目的中依赖关系,这样方便设计 Component,建议大家引入 Dagger 2 之前也绘制下大致的依赖关系图。ToDo 项目中的依赖关系如下图:

《Dagger 2 完全解析(四),Android 中使用 Dagger 2》

ToDo 项目中依赖关系非常简单,4 个 Activity 各自依赖自己的 Presenter,而 Presenter 依赖同一个 TaskRepository 对象。在 Android 项目,依赖关系一般以 Activity、Fragment 这些组件开始划分,因为 app 运行中是以这些组件存在的,所以在后面的 Component 的划分中一般以 Activity、Fragment 来划分。

绘制 Component 依赖关系图

在确定了项目依赖关系,就可以以此划分 Component,从而进一步绘制 Dagger 2 中的依赖关系图。4 个 Activity 分别对应一个 Component,而这 4 个 Component 都依赖一个 TasksRepository。所以还需要一个 Component 提供单例的 TasksRepository 依赖,一般是 AppComponent,用来管理 app 中单例的依赖,而其他 Activity 的 Component 是 AppComponent 的 SubComponnet。

《Dagger 2 完全解析(四),Android 中使用 Dagger 2》

AddEditTaskComponent、StatisticsComponenet、TaskDetailComponent、TasksComponent 都继承 AppComponent,共享同一个 TasksRepository 依赖。

引入 Dagger 2

添加 Dagger 2 依赖,在第一篇文章也有讲述:

dependencies {
...
compile 'com.google.dagger:dagger:2.11-rc2'
annotationProcessor 'com.google.dagger:dagger-compiler:2.11-rc2'
}

上面已经设计好 Component 依赖关系图,下面看看具体的 Component、Module 的编写。

先看看 TasksComponent,其他三个 SubComponent 也是类似的:

@ActivityScope
@Subcomponent(modules = TasksPresenterModule.class)
public interface TasksComponent {
void inject(TasksActivity activity);
@Subcomponent.Builder
interface Builder { // SubComponent 必须实现 @Subcomponent.Builder 接口
@BindsInstance
Builder view(TasksContract.View view); // 在创建 Component 前绑定 TasksContract.View 依赖
TasksComponent build();
}
}
@Module
public class TasksPresenterModule {
@Provides
@ActivityScope
TasksPresenter provideTasksPresenter(TasksRepository tasksRepository, TasksContract.View view) { // tasksRepository 实例由 AppComponent 提供,而 view 实例由 TasksComponent.Builder 中的 view(TasksContract.View view) 方法提供
return new TasksPresenter(tasksRepository, view);
}
}

首先来看@ActivityScope注解,这是一个自定义作用域,和网上一些示例中的@PerActivity类似。@ActivityScope可以让 TasksComponent 间接持有 TasksPresenter 的引用,而且增加可读性,表示 TasksComponent 的生命周期应该在对应的 Activity 中。

TasksComponent 作为 SubComponent 必须实现@Subcomponent.Builder接口,因为 TasksComponent 的创建必须由 AppComponent 调用 TasksComponent.Builder 完成。TaskPresenter 还需要 TasksContract.View 依赖,但是它只能在创建 TasksComponent 时提供,有两种方法:(1)TasksPresenterModule 把 TasksContract.View 作为构造函数参数,这样 TasksComponent.Builder 还需要添加Builder tasksPresenterModule(TasksPresenterModule module);(2)使用@BindsInstance方法,如同上面的代码一样,更多关于@BindsInstance请看 Dagger 2 完全解析(二),进阶使用 Lazy、Qualifier、Scope 等的末尾部分。这时推荐使用第二种方法,简单明了。

上面可以看到 TasksPresenterModule 的 provideTasksPresenter 方法中还有参数,provide 方法中的参数必须由绑定的 Component 提供依赖,而这里 tasksRepository 实例由 AppComponent 提供依赖,view 实例由@BindsInstance方法绑定到 TasksComponent 提供。

看完 SubComponent,再看 AppComponent:

@Singleton
@Component(modules = {AppModule.class, TasksRepositoryModule.class})
public interface AppComponent {
TasksRepository tasksRespository();
AddEditTaskComponent.Builder addEditTaskComponent();
StatisticsComponenet.Builder statisticsComponenet();
TaskDetailComponent.Builder taskDetailComponent();
TasksComponent.Builder tasksComponent();
}
@Module(subcompOnents= {AddEditTaskComponent.class, StatisticsComponenet.class,
TaskDetailComponent.class, TasksComponent.class})
public class AppModule {
private final Context mContext;
public AppModule(Context context) {
mCOntext= context;
}
@Provides
@Singleton
Context provideContext() {
return mContext;
}
}
@Module
public class TasksRepositoryModule {
@Provides
@Singleton
@Local
TasksDataSource provideTasksLocalDataSource(Context context) {
return new TasksLocalDataSource(context);
}
@Provides
@Singleton
@Remote
TasksDataSource provideTasksRemoteDataSource() {
return new TasksRemoteDataSource();
}
}

上面可以看到 AppModule 提供了 ApplicationContext 依赖,而且确定了 4 个 SubComponent 的继承关系。TasksRepositoryModule 提供了两个 TasksDataSource 依赖(TasksRepositoryModule 在 prod 和 mock 中各有一份),用@Local@Remote两个自定义 Qualifier 区分,但是没有提供 TasksRepository 依赖,那么 AppComponent 管理的 TasksRepository 依赖从哪里来呢?大家不要忘记了提供依赖的两种方法 Module 和 Inject 构造函数。

@Singleton
public class TasksRepository implements TasksDataSource {
...
@Inject
TasksRepository(@Remote TasksDataSource tasksRemoteDataSource,
@Local TasksDataSource tasksLocalDataSource) {
mTasksRemoteDataSource = checkNotNull(tasksRemoteDataSource);
mTasksLocalDataSource = checkNotNull(tasksLocalDataSource);
}
...
}

AppComponent 调用 Inject 构造函数创建 TasksRepository 时,会使用 TasksRepositoryModule 提供的两个 TasksDataSource 依赖。推荐大家 clone ToDo 到本地,编译后到app/build/generated/source/apt/目录下看 DaggerAppComponent 的源码,这里就不为大家分析,之前介绍 Component 时有分析过它的原理。

调用 Component 完成依赖注入

最后一步就是使用 Component 完成依赖注入了,先看 AppComponent:

public class ToDoApplication extends Application {
// Application 只生成一个 AppComponent,AppComponent 只生成一个 TasksRepository,以此来完成 TasksRepository 的单例
private AppComponent mAppComponent;
@Override
public void onCreate() {
super.onCreate();
// AppComponent 这里为任何 target 注入依赖,是为它的 SubComponent 提供依赖的。
mAppCompOnent= DaggerAppComponent.builder()
.appModule(new AppModule(this))
.tasksRepositoryModule(new TasksRepositoryModule())
.build();
}
public AppComponent getAppComponent() { // 提供该接口后是为了 Activity 调用
return mAppComponent;
}
}

再看 TasksComponent 的使用:

public class TasksActivity extends AppCompatActivity {
...
@Inject
TasksPresenter mTasksPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
// 调用 AppComponent..tasksComponent() 返回 TasksComponent.Builder 创建 TasksComponent
((ToDoApplication) getApplication()).getAppComponent()
.tasksComponent()
.view(tasksFragment) // @BindsInstance 方法必须在 build 前调用
.build()
.inject(this);
...
}
}

其他几个 SubComponent 的调用过程也是类似的。

其他 Dagger 2 使用示例

除了上面 ToDo App 的示例,我还写了一个 Gank Android 客户端,基于 Dagger 2、Rx Java、Retrofit、Glide等开源库使用 RxFlux 架构,里面还有 FragmentComponent 部分,Component 的继承关系为 FragmentComponent -> ActivityComponent -> AppComponent,有兴趣的朋友可以去看下。

总结

  • 一般会有个 AppComponent,管理 app 中的单例依赖,同时提供 ApplicationContext 依赖。

  • 一般一个页面对应一个 Component,例如 Activity、Fragment 对应各自的 Component,但是两个页面的依赖相同时,可以用同一个 Component。

  • Android 中推荐使用 Component 的继承关系。

  • 尽量多使用 Scope 作用域,增加可读性还能方便控制依赖实例的生命周期。

如何在 Android 项目中使用 Dagger 2 就讲解到这里,有什么问题欢迎各位在下面留言。

本来 Dagger 2 完全解析系列第五篇文章,是打算写 2.9 版之后新增的 dagger.android 扩展库关于 Android 中使用 Dagger 2 的简化写法的,但是发现 dagger.android 还有部分缺陷(如 SubCompoennt 的 Module 不能构造函数,也不能自定义 @BindsInstance 方法等),所以打算等 dagger.android 更成熟后再介绍。而现在 kotlin 大火,所以第五篇文章改为如何在 kotlin 语言的 Android 项目中使用 Dagger 2。

想看更多精彩内容,欢迎关注我的公众号 JohnnyShieh,每周一准时更新!

《Dagger 2 完全解析(四),Android 中使用 Dagger 2》


推荐阅读
  • 1.在gradle中添加依赖在主项目的build.gradle中添加Dagger2库的依赖dependencies{compilecom.google.dagger:dagger: ... [详细]
  • 推荐:看到如此多的MVP+Dagger2+Retrofit+Rxjava项目,轻松拿star,心动了吗?看到身边的朋友都已早早在项目中使用这些技术,而你还不会,失落吗?MVPArm ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • flowable工作流 流程变量_信也科技工作流平台的技术实践
    1背景随着公司业务发展及内部业务流程诉求的增长,目前信息化系统不能够很好满足期望,主要体现如下:目前OA流程引擎无法满足企业特定业务流程需求,且移动端体 ... [详细]
  • 本文介绍了Python语言程序设计中文件和数据格式化的操作,包括使用np.savetext保存文本文件,对文本文件和二进制文件进行统一的操作步骤,以及使用Numpy模块进行数据可视化编程的指南。同时还提供了一些关于Python的测试题。 ... [详细]
  • #前言AndroidArchitectureComponents是谷歌在GoogleIO2017发布的。官方的描述:https:developer.android.google.c ... [详细]
  • 本文整理了Java中javax.lang.model.util.Types.getNullType()方法的一些代码示例,展示了Types.getNullT ... [详细]
  • 开发笔记:Dagger2 探索记3——两大进阶组件
        今天要讲的时@Scope这个组件。为什么说它是进阶组件,就是因为它基本上没作用,但在理解了基本组件之后又必须用到。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • 本文介绍了DataTables插件的官方网站以及其基本特点和使用方法,包括分页处理、数据过滤、数据排序、数据类型检测、列宽度自动适应、CSS定制样式、隐藏列等功能。同时还介绍了其易用性、可扩展性和灵活性,以及国际化和动态创建表格的功能。此外,还提供了参数初始化和延迟加载的示例代码。 ... [详细]
author-avatar
菜大虾
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有