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

Dagger2.1不是Dagger2

一、前言在学习androidarchitecturecomponents(简称acc)时下载了google官方demo,demo里有一部分是关于dagger+mvvm(mvp)+a

一、前言

在学习android architecture components(简称acc)时下载了google官方demo,demo里有一部分是关于dagger+mvvm(mvp)+acc的,本以为阅读起来没有压力但结果却是一脸懵逼,是的,dagger的写法以及注解完全陌生。难道dagger更新了?本着落后就要挨打的原则去google了一番,发现国内资料通篇都是dagger原本的用法,并没有找到我需要的。最后终于在国外网站找到了一篇dagger2.1新用法的介绍,配合dagger官网终于了解了一二,这也坚定了我从头写一篇Dagger文章的决心。

本文适合人群:1、从未了解过dagger,2、仅简单使用API但不了解为何这样使用,3、可以很好的使用并理解dagger,仅需了解新版本(可以直接跳到第四部分)

二、 Dependency Injection(DI)

翻译成中文也就是依赖注入,我们先用一个很简单的例子来了解一下

class Clothes {
final int Defense = 5;
}
class Pants {
final int Defense = 10;
}
class hero {
void printDefense() {
Clothes clothes = new Clothes();
Pants pants = new Pants();
System.out.print("您的角色拥有防御值: "+clothes.Defense + pants.Defense);
}
}

这是一段很简单的代码,初始化角色时,拥有衣服裤子两件装备。但仔细查看,在hero类里创建了clothes和pants两个类,造成了耦合,当你修改clothes或pants时很可能影响到hero类。所以我们通常会将上述代码优化如下:

class hero {
private Clothes clothes;
private Pants pants;
public hero(Clothes clothes, Pants pants) {
this.clothes = clothes;
this.pants = pants;
}
void printDefense() {
System.out.print("您的角色拥有防御值: "+clothes.Defense + pants.Defense);
}
}

《Dagger2.1不是Dagger2》

没错放到构造方法里,这就是’依赖注入’了,hero依赖了clothes和pants,同样的,set方法也可以进行依赖注入。大家是不是在不知不觉中也使用过类似的方式呢:)

对比一下两种方式,前者的缺点如下:

1、clothes和pants无法重用,降低了代码的重用性

2、增加了代码的耦合性,一旦clothes和pants更改可能需要修改hero类很多地方

3、很难进行单元测试

好的,接下来我们就开始用依赖注入的方法创建hero

public static void main(String[] args){
Clothes clothes = new Clothes();
Pants pants = new Pants();
Hero hero = new Hero(clothes, pants);
hero.printDefense();
}

这样子看起来没什么大问题,但是当依赖多了,或者依赖也依赖了其他依赖,就会相当臃肿。有点绕吗?没关系我们看代码:


public static void main(String[] args) {
Color red = new Color();
Clothes clothes = new Clothes(red);
Pants pants = new Pants();
Shoes shoes = new Shoes();
Hat hat = new Hat();
....
Hero hero = new Hero(clothes, pants, shoes, hat, ....);
hero.printDefense();
}

上面的代码中Clothes又依赖了Color,说不定Color又可能会依赖些什么,加上下面的Pants、Shoes、hat……等等,构造我们的Hero需要太多依赖,这样我们使用hero时就非常不方便,主方法就会越来越难以维护。

三、Dagger基本使用

这样,Dagger就孕育而生了,他是一个DI框架,会让我们的依赖注入工作显得非常轻松。自动生成依赖,而我们只需要添加注解即可。

我们用Dagger的方式来重写上面的代码:


class Clothes {
final int Defense = 5;
@Inject
public Clothes() {
}
}
class Pants {
final int Defense = 10;
@Inject
public Pants() {
}
}
class Hero {
private static final String TAG = "Hero";
private Clothes clothes;
private Pants pants;
@Inject
public Hero(Clothes clothes, Pants pants) {
this.clothes = clothes;
this.pants = pants;
}
public void printDefense() {
Log.e(TAG, "您的角色拥有防御值: " + (clothes.Defense + pants.Defense));
}
}

是的,就是这么简单,只需要添加@Inject注解,Dagger就能帮我们自动构建Hero对象,当我们使用时就像这样:

Component
interface HeroComponent {
Hero getHero();
}
public static void main(String[] args){
// Clothes clothes = new Clothes();
// Pants pants = new Pants();
// Hero hero = new Hero(clothes, pants);
// hero.printDefense();
HeroComponent compOnent= DaggerHeroComponent.create();
Hero hero = component.getHero();
hero.printDefense();
}

我们来梳理一下:

1、首先创建一个component接口并添加@component注解,添加获取Hero的方法

2、给Hero构造方法添加@Inject注解,表示需要自动生成该类

3、给依赖类添加@Inject,表示改类也需要自动生成,如此递归添加直到所有依赖都能自动生成

4、构建项目(必须先构建,Dagger会帮你生成一些辅助类),使用名为‘Dagger+YourComponent’的类创建Hero对象

5、愉快的使用吧!

这样一来无论Hero需要多少依赖,我们都可以很简单的生成,不会造成类的臃肿和耦合,使用Hero时不用关心依赖从哪里来,怎么来的,就算Hero或依赖构建方式改变,也不需要修改调用的main函数,很好的解耦。

这时有些童鞋就会问了,如果我需要使用类似Retrofit、OkHttp之类的三方库,而我们却无法去库里添加@Inject注解,该怎么办?没关系,Dagger同样为大家准备了对策:@Module、@Provides

@Module:类似一个仓库,提供Retrofit之流的实例

@Provides:仓库里具体产品的标志,表示该产品对外提供

@Module
public class MyModule {
@Provides
public Retrofit ProvideRetrofit() {
return new Retrofit.Builder()
.baseUrl("www.google.com")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.client(setupClient())
.build();
}
}

这样就写好了一个提供Retrofit的方法,我们还需要将module提交到component:

Component(modules = MyModule.class)
interface HeroComponent {
Hero getHero();
Retrofit getRetrofit();
}

这样,我们就可以愉快的使用Retrofit了,使用方式和Hero一样,我就不累述了。

关于其他API大家可以查阅资料,本文重在思路介绍。

建议初学者在阅读以下内容前先了解更多Dagger2的使用,推荐文章:给初学者的Dagger2

因为以下内容仅适合使用过Dagger2的童鞋了!!!

四、Dagger&Android

打起精神,重点来了!!

在讲解重点之前,我们先来看一段代码:

((MyApplication) getApplication())
.getAppComponent()
.myActivity(new MyActivityModule(userId))
.build()
.inject(this);

在熟悉使用过Dagger2的coder眼中,这段代码几乎贯穿了整个应用程序,每一个activity都会调用同样的代码,相信大家也想尽办法去优化该段代码,包括将其放入Base中或者做了各种各样的封装,可能都不太尽人意(至少笔者没有找到最优解-_-)。

没错!官方这次升级完美的解决了该问题,以前的实现方式我就不多说了,直接讲述新版本方案。

我先按流程走一遍,让大家熟悉熟悉:

1、老规矩,gradle添加依赖

implementation 'com.google.dagger:dagger-android:2.15'
implementation 'com.google.dagger:dagger-android-support:2.15' // if you use the support libraries
annotationProcessor 'com.google.dagger:dagger-android-processor:2.15'
annotationProcessor 'com.google.dagger:dagger-compiler:2.15'

笔者写的时候最新版为2.15,大家使用时可以更换为官方最新版。

2、创建AppComponent

@Component(modules = {
AndroidSupportInjectionModule.class,
AppModule.class,
ActivityBuilder.class})
public interface AppComponent extends AndroidInjector {
@Override
void inject(MyApplication app);
@Component.Builder
interface Builder {
@BindsInstance
Builder application(Application application);
AppComponent build();
}
}

@Component.Builder:自定义component构造器.如果看过Dagger2源码的童鞋应该知道component是用Builder模式创建的,而这里提供了一个自定义构造器。目前先不管,你甚至可以删除这个Builder都没有影响,Dagger会自动生成,这里我只是想展示一下这个注解

继承了AndroidInjector类,重写了他的Inject方法,这个方法必须写,初始化AppComponent用。

@Component(modules = {
AndroidSupportInjectionModule.class,
AppModule.class,
MainActivityModule.class})

Component注解里添加了需要使用到的Module

AndroidSupportInjectionModule.class:这是Dagger2.1初始化必须的module,由Dagger内部完成,用于提供各种AndroidInjector,我们只需要固定加上即可

AppModule.class:用于Application需要初始化提供的全局module,比如刚刚提到的Retrofit、Okhttp等全局对象

ActivityBuilder.class:这就是我们Activity所需的Module了

前两者我们先不关注,重点看看ActivityBuilder,这和我们以前的使用方式就不大相同了,先来看看他的实现。

3、创建ActivityBuilder

@Module
public abstract class ActivityBuilder {
@Binds
@IntoMap
@ActivityKey(MainActivity.class)
abstract AndroidInjector.Factory bindMainActivity(MainActivityComponent.Builder builder);
}

这里是所有Activity(Fragment等)的Component.builder的集合,需要添加@Binds @IntoMap @ActivityKey注解,暂且不管他们的具体作用,只需要了解@Binds就类似于@Provides就可以了。这个类的作用就是让Dagger知道我们所有的使用地。

4、创建MainActivityComponent

@Subcomponent(modules = {MainActivityModule.class})
public interface MainActivityComponent extends AndroidInjector {
@Subcomponent.Builder
public abstract class Builder extends AndroidInjector.Builder {
}
}

需要使用@Subcomponent注解,所以不需要再像以前写Inject之类的方法,modules和以前一样

值得一说的就是内部类Builder,是固定写法,和第三步配套使用。

5、创建MainActivityModule

@Module
public class MainActivityModule {
@Provides
MainView provideMainView(MainActivity mainActivity){
return mainActivity;
}
@Provides
MainPresenter provideMainPresenter(MainView mainView, ApiService apiService) {
return new MainPresenter(mainView, apiService);
}
}

这个也和以前一样,提供需要的依赖

不要忘记在Appmodule里添加subComponent注释,如果Activity多了这里就会添加非常多的XXComponent

@Module(subcompOnents= {MainActivityComponent.class,XX,XX,XX.....})
public abstract class AppModule {
}

6、初始化Application

public class MyApplication extends Application implements HasActivityInjector {
@Inject
DispatchingAndroidInjector dispatchingAndroidInjector;
@Override
public void onCreate() {
super.onCreate();
DaggerAppComponent.builder().application(this).build().inject(this);
}
@Override
public AndroidInjector activityInjector() {
return dispatchingAndroidInjector;
}
}

继承HasActivityInjector,重写activityInjector方法,返回一个DispatchingAndroidInjector

然后在oncreate里初始化

7、MainActivity使用

public class MainActivity extends AppCompatActivity implements MainView {
@Inject
MainPresenter presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
presenter.loadMain();
}
@Override
public void onMainLoaded() {
((TextView)findViewById(R.id.hw)).setText("hello Dagger2.1");
}
}

调用AndroidInjection.inject(this)即可,无须再调用一长串Dagger链。

到这里,初步使用就已经结束了,看到这里可能会觉得‘尼玛,好像更麻烦了’,‘绕来绕去头都大了’的感慨,没错,确实是更麻烦了,不过别着急,前面我只是希望大家看看最基础的用法,然后再介绍更简洁的方式,不然大家很可能只会用而不知道其中的来历。

优化一:继承DaggerApplication、DaggerActivity等类

大家应该还记得刚刚提到的Application类的实现,需要:

1、继承HasActivityInjector,2、重写activityInjector方法,3、返回一个DispatchingAndroidInjector

其实不仅是Application,在Activity、Fragment里只要有subcomponent就必须要实现类似的类,举个栗子:

一个Activity里有一个Fragment,fragment需要依赖注入,这时候Activity就必须

1、继承HasFragmentInjector, HasSupportFragmentInjector

2、重写supportFragmentInjector、fragmentInjector方法

3、返回DispatchingAndroidInjector、DispatchingAndroidInjector

这样的话将会非常麻烦而且重复,但只需要继承了DaggerApplication、DaggerActivity,一切都变得简单了,Dagger2.1都帮你搞定了。这里放出优化后的Application:

public class MyApplication extends DaggerApplication {
@Override
protected AndroidInjector applicationInjector() {
AppComponent appCompOnent= DaggerAppComponent.builder().application(this).build();
appComponent.inject(this);
return appComponent;
}
}

对比上面的是不是简单了很多,其实Activity会更明显,而且每个Activity节省的代码加起来是不可计数的,就留给大家自己去尝试了。

优化二:使用注解@ContributesAndroidInjector

这是Dagger2.1最强大的注释,会帮助你节省数以万计的代码,可能你看到之前的介绍还并不希望升级/使用Dagger2.1,但是用了他之后你就会爱上他。

还记得我们的ActivityBuilder吗?多了@Binds @IntoMap @ActivityKey注解,可能之前我们根本没有用到过,并用他们关联到Component.Builder,同样是重复的工作。利用@ContributesAndroidInjector可以省略这一切,于是我们的ActivityBuilder就变成了这样:

@Module
public abstract class ActivityBuilder {
@ContributesAndroidInjector(modules = MainActivityModule.class)
abstract MainActivity bindMainActivity();
@ContributesAndroidInjector(modules = {SecondActivityModule.class, SecondFragmentProvider.class})
abstract SecondActivity bindSecondActivity();
}

而我们的MainActivityComponent类就可以删掉了,是的没错,直接删掉吧!Dagger会帮我们自动生成,这样每个Activity只需要对应一个Module即可,还记得以前每个Activity对应一个Component和一个Module吗?

AppModule也可以不需要列出所有Module了,直接删掉

五、结语

通过优化后,你的代码已经非常简洁了,大家可以尝试用新版的Dagger来写,然后对比以前的方式,你一定会迫不及待使用新版的。

本文完整代码已经提交到GitHub

分为两个分支,master中使用了初始版,complete中使用了简洁的方式,并利用Dagger实现了MVP模式以及绑定Fragment的方式,大家一定要下载阅读,这样更有利于理解。

当然如果觉得有用的话记得点一下star,这是对作者的最大鼓励。

地址:https://github.com/mrqatom/DaggerInjection

下期预告:@Binds详细讲解、Dagger2.1源码讲解

参考文档:

https://medium.com/@iammert/new-android-injector-with-dagger-2-part-1-8baa60152abe

https://medium.com/@harivigneshjayapalan/dagger-2-for-android-beginners-dagger-2-part-i-f2de5564ab25

https://google.github.io/dagger/android


推荐阅读
author-avatar
溟尛樂_840
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有