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

Android神兵利器Dagger2使用详解(三)进阶篇,不可自拔爱上Dagger2

前言在我的上一篇文章Android神兵利器Dagger2使用详解(二)Module&Component源码分析中,我们通过一点点分析@Module、@Inject以及@Co

前言

在我的上一篇文章Android 神兵利器Dagger2使用详解(二)Module&Component源码分析 中,我们通过一点点分析@Module、@Inject以及@Component注解生成的源码,了解了Dagger2依赖注入魔法的根源:

1、 @Inject 注解构造 生成“大众”工厂类 或者 @Module +@Providers 提供注入“私有”工厂类

2、通过Component 创建获得Activity,获得工厂类Provider,统一交给Injector

3、Injector将Provider的get()方法提供的对象,注入到Activity容器对应的成员变量中

我们就可以直接使用容器(Activity)中对应的成员变量了

借用一张图更形象一些:(图片来源地址)感谢@牛晓伟

这里写图片描述

但是上文的使用方法实在有些简陋,不足以体现Dagger2为什么好用,或者说好用在哪,本文将会以正常实际开发为例,阐述Dagger2实际开发中的使用方式。

一、全局类 AppComponent & AppModule

我们先简单将我们开发时需要依赖注入的对象进行简单的分类:

1、全局变量
这类变量一般为单例模式存在,比如SharedPerferences对象、Application对象,还有ServiceManager(网络请求API管理类)等等,这些东西也许我们只想初始化一次,之后处处使用就好了。
2、局部变量
这些变量可能我们只想在某个地方使用,比如Student对象,我们一般也不会只声明一次。

开发中,局部变量尚还好说,我们在没有Dagger的时候,用的时候new一个就可以了,类似于SharedPerferences这种对象我们就比较头疼,也没有必要每次都去context.getSharedPerferences()吧,但是我们有了Dagger,我们可以通过依赖注入的方式,仅仅初始化一次,之后每次想使用,只需要@Inject注解,就能直接使用了,是不是很方便呀。

说干就干,我们创建一个全局的AppComponent 和 AppModule:

0、首先创建一个自定义的Application类:

public class MyApplication extends Application{
@Override
public void onCreate() {
super.onCreate();
}
}

记得在清单文件中使用这个类哦 ~

1、创建AppModule,提供你想要提供的全局变量

@Module
public class AppModule {

private MyApplication application;

public AppModule(MyApplication application) {
this.application = application;
}

//提供全局的sp对象
@Provides
SharedPreferences provideSharedPreferences(){
return application.getSharedPreferences("spfile", Context.MODE_PRIVATE);
}

//提供全局的Application对象
@Provides
MyApplication provideApplication(){
return application;
}

//你还可以提供更多.......
}

2、创建AppComponent,然后ctrl+F9编译

@Component(modules = AppModule.class)
public interface AppComponent {

SharedPreferences sharedPreferences();

MyApplication myApplication();
}

3、编译成功后,修改Application类,把Component注入器注入你的Application中:

public class MyApplication extends Application{

@Override
public void onCreate() {
super.onCreate();
inject();
}

private void inject() {
AppComponent appCompOnent= DaggerAppComponent.builder()
.appModule(new AppModule(this))
.build();
ComponentHolder.setAppComponent(appComponent);
}
}

ComponentHolder类,方便获取appComponent对象:

public class ComponentHolder {
private static AppComponent myAppComponent;

public static void setAppComponent(AppComponent component) {
myAppCompOnent= component;
}

public static AppComponent getAppComponent() {
return myAppComponent;
}
}

ok,这样,无论何处,我们需要获得AppComponent对象时,只需要调用ComponentHolder.getAppComponent()就可以啦!

注意:

我们需要知道刚才这些文件的作用:

1、AppModule:初始化全局变量
2、AppComponent:注入器,储存了我们要用到的全局变量对象
3、MyApplication: 在onCreate()方法中唯一一次初始化了AppComponent对象,并放入了ComponentHolder中。

MyApplication (作为参数初始化)-> AppModule(初始化全局变量) -> (注入) AppComponent ->(存储到)ComponentHolder

好的,现在我们已经把所有要用到的全局变量存到了AppComponent中,接下来我们创建一个普通的容器Activity。

二、日常开发 Activity相关

0、我们先创建一个普通的Module和Component

@Module
public class A02Module {

private A02Activity activity;

public A02Module(A02Activity activity) {
this.activity = activity;
}

//显然我们并不是很多地方都需要某对象,我们在需要用该对象的界面的Module中提供注入即可
@Provides
Student provideStudent() {
return new Student();
}

}
@Component(modules = A02Module.class, dependencies = AppComponent.class)
public interface A02Component {

void inject(A02Activity activity);

}

请注意Component接口,我们发现多了这样一行代码:

dependencies = AppComponent.class

其实也很好理解,比如说我们需要Student对象和SharedPerferences对象,前者是A02Component提供的,后者则需要「依赖(dependencies)」AppComponent提供,等于这次inject(A02Activity activity)依赖注入需要两个Component共同出力完成。

1、编译成功后,在Activity中声明:

public class A02Activity extends AppCompatActivity {

@Inject
Student student;
@Inject
SharedPreferences sp;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_a02);
inject();
}

private void inject() {
DaggerA02Component.builder()
.appComponent(ComponentHolder.getAppComponent())//添加appComponent注入器
.a02Module(new A02Module(this))
.build()
.inject(this);

Log.i("tag", "注入完毕,Student = " + student.toString());
Log.i("tag", "注入完毕,sp = " + sp.toString());
}
}

查看一下结果,依赖注入成功:

这里写图片描述

是不是很神奇!试想一下,如果我们有很多类似的对象需要经常使用,我们再也不需要每次都去初始化,直接@Inject,然后直接使用就可以了;

更方便的是,如果我们需要修改(比如SharedPreferences文件的名字等等),只需要跑到提供它的Module文件中:

@Module
public class AppModule {

...
...
...

//提供全局的sp对象 -> 修改
@Provides
SharedPreferences provideSharedPreferences(){
// return application.getSharedPreferences("spfile", Context.MODE_PRIVATE);
return application.getSharedPreferences("New spfile", Context.MODE_PRIVATE);
}

...
...
...
}

当然,你的Activity容器中代码也能变得更加赏心悦目:

这里写图片描述

三*、源码分析:

因为在我的上一篇文章Android 神兵利器Dagger2使用详解(二)Module&Component源码分析 中已经对原理有了基本的了解,所以我们可以轻松看懂实现原理。

简单分析下源码,如下为编译器自动生成的代码:

这里写图片描述

1、App全局类源码(global包下的三个类)

提供对象的工厂类:
AppModule_ProvideApplicationFactory.java
AppModule_ProvideSharedPreferencesFactory.java
注入器:
DaggerAppComponent.java

工厂类直接略过,只不过是通过传入Module对象初始化,然后调用get()方法获得Module中对应的对象罢了(若不清楚请参考我的上一篇文章,分析得比较清楚)。

注入器的改变:

public final class DaggerAppComponent implements AppComponent {

private Provider provideSharedPreferencesProvider;

private Provider provideApplicationProvider;

private DaggerAppComponent(Builder builder) {
...
initialize(builder);
}

...
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
//2.初始化2个工厂类
this.provideSharedPreferencesProvider =
AppModule_ProvideSharedPreferencesFactory.create(builder.appModule);

this.provideApplicatiOnProvider= AppModule_ProvideApplicationFactory.create(builder.appModule);
}

//3.想要sp对象吗?appComponent调用一下这个方法就有了
@Override
public SharedPreferences sharedPreferences() {
return provideSharedPreferencesProvider.get();
}

@Override
public MyApplication myApplication() {
return provideApplicationProvider.get();
}

public static final class Builder {
//1、参数传入,初始化appModule
private AppModule appModule;
...
...
}
}

还是老三样,三板斧,没什么难度。

2、A02Component源码分析:

提供Student对象的工厂类:
A02Module_ProvideStudentFactory.java
把对象注入到容器的Injcetor类:
A02Activity_MembersInjector.java
注入器:
DaggerA02Component.java

1、工厂略过不提,提供Student对象的类而已;
2、Injector 我们上一篇文章中可以知道,通过Component把需要对象的容器(activity)、以及对象工厂(studentProvider)给Injector后,Injector把对象赋值给容器中对应的属性而已,比如:

activity.student = studentProvider.get();

3、DaggerA02Component,执行顺序参考代码中注释:

public final class DaggerA02Component implements A02Component {
private Provider provideStudentProvider;

private Provider sharedPreferencesProvider;

private MembersInjector a02ActivityMembersInjector;

private DaggerA02Component(Builder builder) {
assert builder != null;
initialize(builder);
}

public static Builder builder() {
return new Builder();
}

@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {

//3.初始化所有对象的工厂Provider
//3.1 从builder中的Module中获得学生Student工厂
this.provideStudentProvider = A02Module_ProvideStudentFactory.create(builder.a02Module);

//3.2 从builder中的appComponent中获得SP工厂
this.sharedPreferencesProvider =
new Factory() {
private final AppComponent appCompOnent= builder.appComponent;

@Override
public SharedPreferences get() {
return Preconditions.checkNotNull(
appComponent.sharedPreferences(),
"Cannot return null from a non-@Nullable component method");
}
};
//4、把所有工厂都传到Injector中
this.a02ActivityMembersInjector =
A02Activity_MembersInjector.create(provideStudentProvider, sharedPreferencesProvider);
}

@Override
public void inject(A02Activity activity) {
//5、把activity放入到Injector中(接下来结果很明显了...)
a02ActivityMembersInjector.injectMembers(activity);
}

public static final class Builder {
private A02Module a02Module;

private AppComponent appComponent;

private Builder() {}

public A02Component build() {
if (a02Module == null) {
throw new IllegalStateException(A02Module.class.getCanonicalName() + " must be set");
}
if (appCompOnent== null) {
throw new IllegalStateException(AppComponent.class.getCanonicalName() + " must be set");
}
return new DaggerA02Component(this);
}
//2.传入a02Module
public Builder a02Module(A02Module a02Module) {
this.a02Module = Preconditions.checkNotNull(a02Module);
return this;
}
//1.传入appComponent
public Builder appComponent(AppComponent appComponent) {
this.appCompOnent= Preconditions.checkNotNull(appComponent);
return this;
}
}
}

代码虽然变多了,但是,还是很好理解…

小结

不知道大家有没有感到Dagger2的好用,确实是开发时解耦神器啊!

膜拜!

如果有地方看不懂,建议自己尝试敲两遍,然后参考编译器生成的代码,多想想,可能一开始会有点绕,但还是可以看懂的。

代码已托管Github,源码传送门,点我查看

在我的下一篇文中,将会结合源码,学习并加深@Scope注解


推荐阅读
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 标题: ... [详细]
  • 本文介绍了在MFC下利用C++和MFC的特性动态创建窗口的方法,包括继承现有的MFC类并加以改造、插入工具栏和状态栏对象的声明等。同时还提到了窗口销毁的处理方法。本文详细介绍了实现方法并给出了相关注意事项。 ... [详细]
  • Iamtryingtocreateanarrayofstructinstanceslikethis:我试图创建一个这样的struct实例数组:letinstallers: ... [详细]
  • 本文详细介绍了Android中的坐标系以及与View相关的方法。首先介绍了Android坐标系和视图坐标系的概念,并通过图示进行了解释。接着提到了View的大小可以超过手机屏幕,并且只有在手机屏幕内才能看到。最后,作者表示将在后续文章中继续探讨与View相关的内容。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 本文介绍了在处理不规则数据时如何使用Python自动提取文本中的时间日期,包括使用dateutil.parser模块统一日期字符串格式和使用datefinder模块提取日期。同时,还介绍了一段使用正则表达式的代码,可以支持中文日期和一些特殊的时间识别,例如'2012年12月12日'、'3小时前'、'在2012/12/13哈哈'等。 ... [详细]
author-avatar
向陽阿莫_545
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有