Dagger2使用入门
Dagger2简介
Dagger2
是Dagger
的升级版,是一个依赖注入框架,现在由Google接手维护。
Github:https://github.com/google/dagger
依赖注入是面向对象编程的一种设计模式,其目的是为了降低程序耦合,这个耦合就是类之间的依赖引起的。
举个例子:我们在写面向对象程序时,往往会在一个类中引用另一个类,从而可以调用引用的类的方法完成某些功能,就像下面这样:
public class ClassA {
...
ClassB b;
...
public ClassA() {
b = new ClassB();
}
public void do() {
...
b.doSomething();
...
}
}
这时就产生了依赖问题,ClassA
依赖于ClassB
,必须借助ClassB
的方法,才能完成一些功能。这样看好像并没有什么问题,但是我们在ClassA
的构造方法里面直接创建了ClassB
的实例,问题就出现在这,在ClassA
里直接创建ClassB
实例,违背了单一职责原则,ClassB
实例的创建不应由ClassA
来完成;其次耦合度增加,扩展性差,如果我们想在实例化ClassB
的时候传入参数,那么不得不改动ClassA
的构造方法,不符合开闭原则。
因此我们需要一种注入方式,将依赖注入到宿主类(或者叫目标类)中,从而解决上面所述的问题。依赖注入有以下几种方式:
通过接口注入
interface ClassBInterface {
void setB(ClassB b);
}
public class ClassA implements ClassBInterface {
ClassB classB;
@override
void setB(ClassB b) {
classB = b;
}
}
通过set方法注入
public class ClassA {
ClassB classB;
public void setClassB(ClassB b) {
classB = b;
}
}
通过构造方法注入
public class ClassA {
ClassB classB;
public void ClassA(ClassB b) {
classB = b;
}
}
通过Java注解
public class ClassA {
//此时并不会完成注入,还需要依赖注入框架的支持,如RoboGuice, Dagger2
@inject ClassB classB;
...
public ClassA() {}
在Dagger2中用的就是最后一种注入方式,通过注解的方式,将依赖注入到宿主类中。
引入Dagger2
api 'com.google.dagger:dagger:2.28.3'
annotationProcessor 'com.google.dagger:dagger-compiler:2.28.3'
使用Dagger2
例子基于mvp模式,在mvp中,最常见的一种依赖关系,就是Activity
持有Presenter
的引用,并在Activity
中实例化这个Presenter
,即Activity
依赖Presenter
,Presenter
又需要依赖View
接口,从而更新UI
public class MainActivity extends AppCompatActivity implements MainView {
private MainPresenter mainPresenter;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//实例化presenter 将view传递给presenter
mainPresenter = new MainPresenter(this);
//调用Presenter方法加载数据
mainPresenter.loadData();
...
}
@Override
public void updateUI() {
Toast.makeText(this, "Dagger2", Toast.LENGTH_SHORT).show();
}
}
public class MainPresenter {
//MainView是个接口
private MainView mView;
MainPresenter(MainView view) {
mView = view;
}
public void loadData() {
//调用model层方法,加载数据
...
//回调方法成功时
mView.updateUI();
}
}
这样Activity
与Presenter
紧紧耦合在了一起,当需要改变Presenter
的构造方式时,需要修改这里的代码。如果用依赖注入的话,是这样的:
public class MainActivity extends AppCompatActivity implements MainView {
@Inject
MainPresenter mainPresenter;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainComponent.builder()
.mainModule(new MainModule(this))
.build()
.inject(this);
//调用Presenter方法加载数据
mainPresenter.loadData();
...
}
@Override
public void updateUI() {
Toast.makeText(this, "Dagger2", Toast.LENGTH_SHORT).show();
}
}
public interface MainView {
void updateUI();
}
public class MainPresenter {
//MainView是个接口
private MainView mView;
@Inject
MainPresenter(MainView view) {
mView = view;
}
public void loadData() {
//调用model层方法,加载数据
...
//回调方法成功时
mView.updateUI();
}
}
@Module
public class MainModule {
private final MainView mView;
public MainModule(MainView view) {
mView = view;
}
@Provides
MainView provideMainView() {
return mView;
}
}
@Component(modules = MainModule.class)
public interface MainComponent {
void inject(MainActivity activity);
}
直接组合方式虽然简单,但是具有耦合性,为了解决这种耦合,可能就会多产生一些辅助类,让这种直接的依赖关系,变为间接,降低耦合。跟大多数设计模式一样,为了达到高内聚低耦合,往往会有很多接口与类,Daager2
也是如此,虽然看似复杂了些,不过这在软件工程中是值得的。
先看MainActivity
里的代码,之前是直接声明MainPresenter
,现在在声明的基础上加了一个注解@Inject
,表明MainPresenter
是需要注入到MainActivity
中,即MainActivity
依赖于MainPresenter
,这里要注意的是,使用@Inject
时,不能用private
修饰符修饰类的成员属性。
然后我们在MainPresenter
的构造函数上同样加了@Inject
注解。这样MainActivity
里的mainPresenter
与他的构造函数建立了某种联系。这种联系可以这样理解,当看到某个类被@Inject
标记时,就会到它的构造方法中,如果这个构造方法也被@Inject
标记的话,就会自动初始化这个类,从而完成依赖注入。
然后,他们之间并不会凭空建立起联系,肯定需要一个桥梁,将他们连接起来,也就是下面要介绍的Component
。
Component
是一个接口或者抽象类,用@Component
注解标注(这里先不管括号里的modules
),我们在这个接口中定义了一个inject()
方法,参数是Mainactivity
。然后rebuild
一下项目,会生成一个以Dagger
为前缀的Component
类,这里是DaggerMainComponent
,然后在MainActivity
里完成注册:
DaggerMainComponent.builder()
.mainModule(new MainModule(this))
.build()
.inject(this);
此时Component
就将@Inject
注解的mainPresenter
与其构造函数联系了起来。
完成Presenter
的注入过程,发现还有一个MainModule
类,MainModlue
是一个注解类,用@Module
注解标注,主要用来提供依赖。刚才通过@Inject
就可以完成依赖,为什么这里还要用到Module
类来提供依赖?之所以有Module
类主要是为了提供那些没有构造函数的类的依赖,这些类无法用@Inject
标注,比如第三方类库,系统类,以及上面示例的View
接口。
我们在MainModule
类里声明了MainView
成员属性,在构造方法里将外界传进来的view
赋值给mView
,并通过一个@Provides
标注的以provide
开头的方法,将这个view
返回,这个以provide
开头的方法就是提供依赖的,我们可以创建多个方法来提供不同的依赖。那么这个类究竟是怎么作用的?可以想到上面提到的@Component
注解括号里的参数:
@Component(modules = MainModule.class)
public interface MainComponent {
void inject(MainActivity activity);
}
所以Module
要发挥作用,还是要依靠于Component
类,一个Component
类可以包含多个Module类,用来提供依赖。我们接着看下面这段代码:
DaggerMainComponent.builder()
.mainModule(new MainModule(this))
.homeModulw(new HomeModule(this))
.build()
.inject(this);
这里通过new MainModule(this)
将view
传递到MainModule
里,然后MainModule
里的provideMainView()
方法返回这个View
,当去实例化MainPresenter
时,发现构造函数有个参数,此时会在Module
里查找提供这个依赖的方法,将该View
传递进去,这样就完成了presenter
里View
的注入。
Dagger2注入总结
我们来重新理一遍上面的注入过程,首先弄清楚以下几个概念:
@Inject
带有此注解的属性或构造方法将参与到依赖注入中,Dagger2
会实例化有此注解的类@Module
带有此注解的类,用来提供依赖,里面定义一些用@Provides
注解的以provide
开头的方法,这些方法就是提供所需的依赖,Dagger2
会在该类中寻找实例化某个类所需要的依赖@Component
用来将@Inject
和@Module
联系起来的桥梁,从@Module
中获取依赖并将依赖注入给@Inject
接着我们重新回顾一下上面的注入过程:首先MainActivity
需要依赖MainPresenter
,因此,我们在MainActivity
中用@Inject
对MainPresenter
进行标注,表明这是要注入的类。然后,我们对MainPresenter
的构造函数也添加注解@Inject
,此时构造函数里有一个参数MainView
,因为MainPresenter
需要依赖MainView
,所以我们定义了一个类,叫做MainModule
,提供一个方法provideMainView
,用来提供这个依赖,这个MainView
是通过MainModule
的构造函数注入进来的,接着我们需要定义Component
接口类,并将Module
包含进来,即
@Component(modules = MainModule.class)
public interface MainComponent {
void inject(MainActivity activity);
}
Dagger2注入原理
Dagger2
与其他依赖注入框架不同,它是通过APT
即注解处理器(Annotation Processing Tool)插件在编译阶段生成相应的注入代码,执行Build Project
,Dagger2
会在app/build/generated/ap_generated_sources/debug/out
目录下生成对应的工厂类:
MainPresenter_Factory.java
MainModule_ProvideMainViewFactory.java
DaggerMainComponent.java
MainActivity_MembersInjector.java
构造方法 @Inject
类MainPresenter_Factory
是类MainPresenter
的构造方法使用@Inject
标注后构建生成的工厂类:
public class MainPresenter {
//MainView是个接口
private MainView mView;
@Inject
MainPresenter(MainView view) {
mView = view;
}
}
public final class MainPresenter_Factory implements Factory<MainPresenter> {
private final Provider<MainView> viewProvider;
public MainPresenter_Factory(Provider<MainView> viewProvider) {
this.viewProvider = viewProvider;
}
@Override
public MainPresenter get() {
return newInstance(viewProvider.get());
}
public static MainPresenter_Factory create(Provider<MainView> viewProvider) {
return new MainPresenter_Factory(viewProvider);
}
public static MainPresenter newInstance(MainView view) {
return new MainPresenter(view);
}
}
@Provides
类MainModule_ProvideMainViewFactory
是类MainModule
的provideMainView()
方法使用@Provides
标注后构建生成的工厂类:
@Module
public class MainModule {
private final MainView mView;
public MainModule(MainView view) {
mView = view;
}
@Provides
MainView provideMainView() {
return mView;
}
}
public final class MainModule_ProvideMainViewFactory implements Factory<MainView> {
private final MainModule module;
public MainModule_ProvideMainViewFactory(MainModule module) {
this.module = module;
}
@Override
public MainView get() {
return provideMainView(module);
}
public static MainModule_ProvideMainViewFactory create(MainModule module) {
return new MainModule_ProvideMainViewFactory(module);
}
public static MainView provideMainView(MainModule instance) {
return Preconditions.checkNotNull(instance.provideMainView(), "Cannot return null from a non-@Nullable @Provides method");
}
}
这里不难发现一种对应关系,在MainModule
中定义的@Provides
修饰的方法都会对应的生成一个对应工厂类,这里是MainModule_ProvideMainViewFactory
@Component
类DaggerMainComponent
是类MainComponent
使用@Component
标注后构建生成的辅助类,@Component
是连接@Module
和@Inject
的桥梁,主要作用是执行inject
方法,为MainPresenter
构造方法赋值参数MainView
@Component(modules = MainModule.class)
public interface MainComponent {
void inject(MainActivity activity);
}
public final class DaggerMainComponent implements MainComponent {
private final MainModule mainModule;
private DaggerMainComponent(MainModule mainModuleParam) {
this.mainModule = mainModuleParam;
}
public static Builder builder() {
return new Builder();
}
private MainPresenter getMainPresenter() {
return new MainPresenter(MainModule_ProvideMainViewFactory.provideMainView(mainModule));
}
@Override
public void inject(MainActivity activity) {
injectMainActivity(activity);
}
private MainActivity injectMainActivity(MainActivity instance) {
MainActivity_MembersInjector.injectMainPresenter(instance, getMainPresenter());
return instance;
}
public static final class Builder {
private MainModule mainModule;
private Builder() {
}
public Builder mainModule(MainModule mainModule) {
this.mainModule = Preconditions.checkNotNull(mainModule);
return this;
}
public MainComponent build() {
Preconditions.checkBuilderRequirement(mainModule, MainModule.class);
return new DaggerMainComponent(mainModule);
}
}
}
引用注入 @Inject
类MainActivity_MembersInjector
是类MainActivity
的mainPresenter
成员变量使用@Inject
标注后构建生成的辅助类,主要作用是初始化mainPresenter
public class MainActivity extends AppCompatActivity implements MainView {
@Inject
MainPresenter mainPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainComponent.builder()
.mainModule(new MainModule(this))
.build()
.inject(this);
//调用Presenter方法加载数据
mainPresenter.loadData();
}
@Override
public void updateUI() {
Toast.makeText(this, "AppBlog.CN", Toast.LENGTH_SHORT).show();
}
}
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
private final Provider<MainPresenter> mainPresenterProvider;
public MainActivity_MembersInjector(Provider<MainPresenter> mainPresenterProvider) {
this.mainPresenterProvider = mainPresenterProvider;
}
public static MembersInjector<MainActivity> create(
Provider<MainPresenter> mainPresenterProvider) {
return new MainActivity_MembersInjector(mainPresenterProvider);
}
@Override
public void injectMembers(MainActivity instance) {
injectMainPresenter(instance, mainPresenterProvider.get());
}
@InjectedFieldSignature("me.yezhou.dagger2.MainActivity.mainPresenter")
public static void injectMainPresenter(MainActivity instance, MainPresenter mainPresenter) {
instance.mainPresenter = mainPresenter;
}
}
调用流程
(1)inject调用
MainActivity
DaggerMainComponent.builder()
.mainModule(new MainModule(this))
.build()
.inject(this);
DaggerMainComponent
@Override
public void inject(MainActivity activity) {
injectMainActivity(activity);
}
private MainActivity injectMainActivity(MainActivity instance) {
MainActivity_MembersInjector.injectMainPresenter(instance, getMainPresenter());
return instance;
}
(2)构建MainPresenter
,传入MainView
DaggerMainComponent
private MainPresenter getMainPresenter() {
return new MainPresenter(MainModule_ProvideMainViewFactory.provideMainView(mainModule));
}
(3)通过MainModule
构建MainView
MainModule_ProvideMainViewFactory
public static MainView provideMainView(MainModule instance) {
return Preconditions.checkNotNull(instance.provideMainView(), "Cannot return null from a non-@Nullable @Provides method");
}
MainModule
@Provides
MainView provideMainView() {
return mView;
}
(4)初始化MainActivity
中的mainPresenter
MainActivity_MembersInjector
@InjectedFieldSignature("me.yezhou.dagger2.MainActivity.mainPresenter")
public static void injectMainPresenter(MainActivity instance, MainPresenter mainPresenter) {
instance.mainPresenter = mainPresenter;
}
版权声明:
作者:Joe.Ye
链接:https://www.appblog.cn/index.php/2023/03/29/getting-started-with-dagger2/
来源:APP全栈技术分享
文章版权归作者所有,未经允许请勿转载。
共有 0 条评论