Wonder4 Life

使用 Dagger 库 基础篇

Dagger 是一个依赖注入的库,采用编译时生成代码的方式实现,现在的版本是 2.11。

Dagger 涉及的概念很多,为了避免过早的陷入细节,这篇文章先通过一个例子,介绍 Dagger 库基本的 @Inject@Component@Module 几个知识点。

下面我们就看看如何用 Dagger 造个凳子(图片来源于 Muji 官网):

榫卯

注:这个 Muji 家的凳子和我们经常看到的条凳很相似,相关背景可以看知乎上的问题。选用这个凳子纯粹是因为该款式结构简单,便于拆分。

我们在这篇文章中将凳子作为一个整体考虑,直接创建凳子对象。

传统方法创建被依赖对象

首先,创建被依赖的类 Stool :

1
2
3
4
5
public class Stool {
public Stool() {
}
}

MainActivity 需要(依赖)这个对象,所以有一个 Stool 成员变量,并且我们在适当时机 new 一个对象:

1
2
3
4
5
6
7
8
9
10
11
public class MainActivity extends AppCompatActivity {
Stool mStool;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mStool = new Stool();
}
}

这样写很符合习惯,但是造成了紧耦合。关于这部分知识,请参考这篇文章

那么接下来演示下,如何用 Dagger 最简单的方式创建一个对象。

使用 Dagger 创建被依赖对象 (@Inject 方式)

首先,在 app 的 build.gradle 加入 Dagger 库 :

1
2
3
4
5
dependencies {
...
compile 'com.google.dagger:dagger:2.11'
annotationProcessor 'com.google.dagger:dagger-compiler:2.11'
}

在被依赖的类 Stool 的构造函数上加上 @Inject 注解,表示我可以提供这个对象:

1
2
3
4
5
6
public class Stool {
@Inject
public Stool() {
}
}

相应的,MainActivity 的 Stool 成员变量也要加上 @Inject 注解,表示我需要这个对象:

1
2
3
4
5
6
7
8
9
10
public class MainActivity extends AppCompatActivity {
@Inject
Stool mStool;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}

如果这样就结束了,那就太简单了。但实际上完成上述步骤,只是有了需求者和提供者,还需要有一个运送者负责传递。这个任务是由 Dagger 最重要的 Component 接口完成

1
2
3
4
5
6
@Component()
public interface MainActivityComponent {
void inject(MainActivity activity);
}

在接口前加上 @Component() 标注,告诉 Dagger 这是一个 Component 接口。同时,接口方法任意命名(通常约定俗成是 inject ),只不过需要传入要注入的目的地:MainActivity 对象。

然后,Dagger 预编译器会根据这个接口自动加上 Dagger- 前缀,生成一个实现类 DaggerMainActivityComponent。

如果没有生成对应的代码,需要在 Android Studio 点击 Build -> Rebuild Project。

上述步骤搭好了架子,最后还需要手动输入,决定注入的时机。这里就在 onCreate 注入 Stool 对象,再次修改 MainActivity,添加DaggerMainActivityComponent.create().inject(this)

1
2
3
4
5
6
7
8
9
10
11
public class MainActivity extends AppCompatActivity {
@Inject
Stool mStool;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainActivityComponent.create().inject(this);
}
}

自动生成的类 DaggerMainActivityComponent 采用了 Builder 模式(关于这部分分析,可以参考接下来讲述原理的文章)。所以通常的教程会看到 DaggerMainActivityComponent.builder().build() 的方式创建 Component 对象,实际上 create() 封装了 builder().build() 方法,所以用 DaggerMainActivityComponent.create() 创建 Component 对象效果相同。

如上就是采用 Dagger 最基本的方式创建并注入一个凳子对象。涉及的知识点包括:@Inject 注解和 @Component 接口。

使用 Dagger 创建被依赖对象 (@Module 方式 + static 方法)

接下来我们用 @Module 方式创建对象,引入 @Module 源于有些时候无法在构造函数添加 @Inject 注解。

这次,被依赖的类 Stool 构造函数没有 @Inject 注解:

1
2
3
4
5
6
public class Stool {
public Stool() {
}
}

然后添加 StoolModule 类,用于创建 Stool 依赖对象:

1
2
3
4
5
6
7
8
9
10
11
import dagger.Module;
import dagger.Provides;
@Module
public class StoolModule {
@Provides
static Stool provideStool() {
return new Stool();
}
}

@Module 标注说明这个类可以提供依赖,@Provides@Module 中使用,说明标注的方法可以返回依赖。

由于采用了 Module 方式创建被依赖对象,Component 接口也需要做出些修改:

1
2
3
4
@Component(modules = StoolModule.class)
public interface MainActivityComponent {
void inject(MainActivity activity);
}

@Component 注解中,定位到之前定义的 StoolModule 类 (modules = StoolModule.class),表示采用这个类提供依赖对象。

此处 MainActivity 保持原样:

1
2
3
4
5
6
7
8
9
10
11
public class MainActivity extends AppCompatActivity {
@Inject
Stool mStool;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainActivityComponent.create().inject(this);
}
}

可以看到,通过引入 @Module ,并在 @Component 指定,完全可以平滑的替代在构造函数前加 @Inject 的方式创建依赖对象。

这回涉及的知识点包括:@Module 注解和 @Component 接口指定 Module 。

但是,在自动生成的 DaggerMainActivityComponent 类中发生了些改变,采用 Module 方式会在其中增加一个 @Deprecated 方法:

1
2
@Deprecated
public Builder stoolModule(StoolModule stoolModule)

这个方法在这个例子中没有什么作用。因为我们在 StoolModule 中定义的是一个静态方法,并不需要传递一个 StoolModule 对象:

1
2
@Provides
static Stool provideStool()

所以,会增加 @Deprecated 标注。这个标注会出现的原因,可以参看官方文档:Unused Modules and Component Dependencies

  • 如果 Module 的方法是 static , Dagger 并不需要这个对象,可以直接调用静态方法
  • 如果一个 Module 未和 Component 关联起来 (例如,我们没有在 MainActivity 的成员变量前加 @Inject 注解 ),也没有必要创建这个 Module 对象。

使用 Dagger 创建被依赖对象 (@Module 方式 + 非 static 方法)

这里我们将 Module 改为非 static 方法创建 Stool 依赖对象:

1
2
3
4
5
6
7
8
9
10
11
import dagger.Module;
import dagger.Provides;
@Module
public class StoolModule {
@Provides
Stool provideStool() {
return new Stool();
}
}

此时 MainActivity 可以保持原样,也可以采用 Builder 模式手动创建并传递 Module 对象。如果没有传递, DaggerMainActivityComponent 在调用 build() 方法时会自动创建:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class MainActivity extends AppCompatActivity {
@Inject
Stool mStool;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainActivityComponent.create().inject(this);
//DaggerMainActivityComponent.builder()
// .stoolModule(new StoolModule()).build().inject(this);
}
}

关于这部分原理,可以参考接下来的文章。

综上,Dagger 中频繁出现的几个知识点就介绍完了。在后续的文章中,会进一步完善这个程序,并分析 Dagger 工作的原理。

Reference

Dagger Document

Dependency injection with Dagger 2 - the API