Dagger2使用方法及源码解析
1.Dagger2简介
2.Dagger2项目依赖
3.Dagger2基础使用方法
4.Dagger2使用方法拓展
5.Dagger2基础源码分析
.
.
.
1.Dagger2简介 :
Dagger是一种对Java和Android的静态的,编译时依赖注入(Dependency Injection)框架。它主要用来解决很多由于大量使用反射reflection而带来的开发和性能问题。总而言之,Dagger是一套依赖注入的框架,不懂依赖注入的朋友请自行学习。
这里推荐一篇大神的文章:轻松学,浅析依赖倒置(DIP)、控制反转(IOC)和依赖注入(DI)
.
.
2.Dagger2项目依赖
//dagger2相关
implementation 'com.google.dagger:dagger:2.4'
annotationProcessor 'com.google.dagger:dagger-compiler:2.4'
.
.
3.Dagger2基础使用方法
现在我们假设有这样三个类:1.苹果,2.香蕉,3.小刀
小刀可以削苹果,也可以切开香蕉,但我们在使用小刀的时候需要有一个苹果或者香蕉来让我们切一下,所以我们就需要依赖于香蕉和苹果的对象。
在一般未使用Dagger的情况下,我们的代码是这个样子的:
public class Knife
{
private static final String TAG = "aaa";
private Apple apple;
private Bnana bnana;
public void cut()
{
apple = new Apple();
bnana = new Bnana();
Log.d(TAG, "cut: 我要切苹果");
apple.introSelf();
Log.d(TAG, "cut: 我要切香蕉");
apple.introSelf();
}
}
其中Apple类和Bnana类的代码如下:
public class Apple
{
private static final String TAG = "aaa";
public void introSelf()
{
Log.d(TAG, "555,我是苹果");
}
}
public class Bnana
{
private static final String TAG = "aaa";
public void introSelf()
{
Log.d(TAG, "我是香蕉");
}
}
当我们使用依赖注入时(未使用Dagger),Knife类的代码变成了这样
public class Knife
{
private static final String TAG = "CUT";
public void cut(Apple apple, Bnana bnana)
{
Log.d(TAG, "cut: 我要切苹果");
apple.introSelf();
Log.d(TAG, "cut: 我要切香蕉");
apple.introSelf();
}
}
是不是对于Knife对象来说,工作变得更简单了,而且降低了这三个类之间的耦合性。但对于我们coder来说,我们仍然需要new出一个Apple对象和一个Bnana对象,然后传入cut()方法中,工作量并没有显著的减少。
然而,当我们使用dagger时,Knife类的代码有了如下的变化:
public class Knife
{
private static final String TAG = "CUT";
@Inject
Apple apple;
@Inject
Bnana bnana;
@Inject
public Knife() {
}
public void cut()
{
Log.d(TAG, "cut: 我要切苹果");
apple.introSelf();
Log.d(TAG, "cut: 我要切香蕉");
apple.introSelf();
}
}
我们不再需要关心在哪new出这两个对象,只要使用注解标注我们需要注入的对象即可。
下面我们来看看Dagger2中常见的几种注解:
@Inject注解
上面你的代码中我们用了 @Inject 注解,它可以为我们自动注入对Apple对象和Bnana对象的依赖。但这样做的前提是我们的Apple类或者Bnana类的构造函数中需要使用 @Inject 注解来标记。
Apple类现在的代码如下:
public class Apple
{
private static final String TAG = "aaa";
@Inject
public Apple() {
}
public void introSelf()
{
Log.d(TAG, "555,我是苹果");
}
}
这样我们Knife类中的Apple对象便由Dagger2自动为我们注入了,我们在代码中并没有主动的去new一个Apple对象。一切交由Dagger2框架自动完成。
.
.
.
另外一个场景:
现在我们想切一个香蕉吃,但我们手头并没有香蕉,香蕉都生长的热带地区,香蕉不会自己长腿跑到我们手里。此时我们只能通过中间商–水果店来获得香蕉。此时水果店就变成了一个Module
@Module注解
在我们使用第三方文件时,并不被允许更改其类内的文件(让我们在其构造函数上加上@inject注解),此时我们就需要一个中间类来帮我们获取第三方的对象。简单点如下:
@Module
public class DaggerModule {
@Provides
public Bnana getBnana()
{
//简单写法,一般会视情况传入一个接口或者父类
return new Bnana();
}
}
@Provides注解
配合 @Module注解 来使用,表示通过这个方法获取对象实例
@Component
现在我们主要的代码已经差不多了,但我们可以发现我们所有的类都是单独的,只用@inject注解去标记依赖,此时我们需要一个桥梁------一个被 @Component 注解的接口,来将两个对应类型的 @inject 注解联系起来,并且决定是使用 @inject注解标记构造函数的形式来注入依赖,还是使用 @Module 注解和 @Provides 注解来注入依赖。
其实它的规则是当它持有的类的实例中出现 @Inject 注解时(如上面的例子中MainActivity中出现了被@Inject注解的Knife对象),优先扫描 @Module 注解和 @Provides 注解来注入依赖,然后才是扫描该对象的构造函数,查看其是否也使用了 @Inject 注解。
在我们的ManiActivity,我们需要获得Knife对象,所以我们将ManiActivity对象的实例写入 @Component注解的接口中,让dagger为我们注入MainActivity所需的依赖。其中modules可以为多个值,以modules = {Module1.class,Module2.class}的形式写入。在这里虽然我们的MainActivity没有用到Bnana对象(使用Module和provider),但是我们用到了Knife对象,Knife对象需要对Bnana对象的依赖(这里是一个嵌套依赖)。DaggerModule这个类代码如下:
//modules = 自己定义的可以提供目标对象的module类
@Component(modules = {DaggerModule.class})
public interface MainActivityComponent
{
//为传入的MainActivity对象注入所需的依赖knife,并且为knife注入所需要的的依赖
void inject(MainActivity mainActivity);
}
我们的MainActivity的代码如下:
为了对比两种注入方法,我们将切苹果和切香蕉分开写。
public class MainActivity extends AppCompatActivity {
Button apple;
Button bnana;
@Inject
Knife knife;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initOnclike();
//核心代码
MainActivityComponent component = DaggerMainActivityComponent.builder().build();
component.inject(this);
}
private void initOnclike() {
apple.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
knife.cutApple();
}
});
bnana.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
knife.cutBnana();
}
});
}
private void initView() {
apple = findViewById(R.id.eatApple);
bnana = findViewById(R.id.eatBnana);
}
}
到这里,Dagger的简单使用demo就好了,梳理一下依赖注入的两种基本手段:
1.在需要依赖的类中使用 @Inject 注解一个需要被注入的类的对象,然后在该类中使用 @Inject 注解该类的无参构造函数。
2.在Module中提供一个使用 @provider注解一个能返回目标对象的方法,在这里又分两种情况,一种是是这个对象是在参数中传进来的(情况1),另外一种是new一个对象返回的(情况2)。两种方法的区别是当该类的构造方法可以被@injectz注解时,使用第一种或者第二种都可以,但反之只能使用第一种。
情况1:
@Provides
public Bnana getBnana(Bnana bnana)
{
return bnana;
}
情况2:
@Provides
public Bnana getBnana()
{
return new GuaZi();
}
为避免文章篇幅过长,剩下的内容将在下篇文章中涉及。Dagger2使用方法及源码解析(二)