Dagger2简介 配置 使用 MVP案例

Posted 白乾涛

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Dagger2简介 配置 使用 MVP案例相关的知识,希望对你有一定的参考价值。

简介

compile ‘com.google.dagger:dagger:2.11‘//2017-9-17最新版本为【2.11】
annotationProcessor ‘com.google.dagger:dagger-compiler:2.11‘//dagger-compiler为编译时期生成代码等相关的类库
Dagger2 是一个android依赖注入框架,由谷歌开发,最早的版本Dagger1 由Square公司开发。
依赖注入框架主要用于模块间解耦,提高代码的健壮性和可维护性。
Dagger 这个库的取名不仅仅来自它的本意“匕首”,同时也暗示了它的原理。Jake Wharton 在对 Dagger 的介绍中指出,Dagger 即 DAG-er,这里的 DAG 即数据结构中的 DAG:有向无环图(Directed Acyclic Graph)。也就是说,Dagger 是一个基于有向无环图结构的依赖注入库,因此Dagger的使用过程中不能出现循环依赖。

Android开发从一开始的MVC框架,到MVP,到MVVM,不断变化。现在MVVM的data-binding还在实验阶段,传统的MVC框架Activity内部可能包含大量的代码,难以维护,现在主流的架构还是使用MVP的方式。但是 MVP 框架也有可能在Presenter中集中大量的代码,引入DI框架Dagger2 可以实现 Presenter 与 Activity 以及 Presenter 和其它业务逻辑之间的解耦,提高模块化和可维护性。

依赖注入就是将调用者需要的另一个对象实例不在调用者内部实现,而是通过一定的方式从外部传入实例,解决了各个类之间的耦合。
那么这个外部,到底指的是哪里,如果指的是另一个类,那么,另一个类内部不就耦合了?能不能有一种方式,将这些构造的对象放到一个容器中,具体需要哪个实例时,就从这个容器中取就行了。这样,类的实例和使用就不在有联系了,而是通过一个容器将他们联系起来,实现了解耦。这个容器,便是Dagger2。

Dagger2的原理是:在【编译期】生成相应的依赖注入代码。这也是和其他依赖注入框架不同的地方,其他框架是在【运行时】通过【反射】获取【注解内容】,影响了运行效率。

GitHub介绍

A fast dependency injector for Android and Java.    适用于Android和Java的快速依赖注入器。

【About Google‘s Fork】
Dagger 2 is a compile-time evolution approach to dependency injection. Taking the approach started in Dagger 1.x to its ultimate conclusion, Dagger 2.x eliminates all reflection, and improves code clarity by removing the traditional ObjectGraph/Injector in favor of user-specified @Component interfaces.
Dagger 2是接近依赖注入的一种编译时进化。采用Dagger 1.x开始的方式达成最终结论,Dagger 2.x消除了所有的反射,并通过删除传统的ObjectGraph / Injector来改善代码清晰度,并以用户指定的@Component接口来取代。
evolution  [??v??lu??n, ?iv?-]  n. 演变; 进化; 发展;
approach [??pro?t?] vt.接近,走近,靠近;  vt.接近; 着手处理; 使移近;  n. 方法; 途径; 接近;
in favor of	赞成[支持](某人或某事物); 以…取代; (支票) 以某人[某部门]为受款人;
This github project represents the Dagger 2 development stream. The earlier project page (Square, Inc‘s repository) represents the earlier 1.0 development stream. Both versions have benefitted from strong involvement from Square, Google, and other contributors.
这个github项目代表了Dagger 2开发流。较早的项目页面(Square,Inc的存储库)代表较早的1.0开发流。这两个版本都受益于Square、Google和其他贡献者的强烈参与

Dagger is currently in active development, primarily internally at Google, with regular pushes to the open-source community. Snapshot releases are auto-deployed to sonatype‘s central maven repository on every clean build with the version HEAD-SNAPSHOT.
Dagger 目前正在积极发展,主要是内部在谷歌,定期push到开源社区。快照版本自动部署到sonatype的中央maven存储库,每个干净的版本与版本HEAD-SNAPSHOT。

Dagger 2‘s main documentation website can be found here.    Dagger 2的主要文档网站可以在这里找到。

【Documentation】
You can find the dagger documentation here which has extended usage instructions and other useful information. Substantial usage information can be found in the API documentation.
您可以在这里找到扩展使用说明和其他有用信息的匕首文档。可以在API文档中找到实质的使用信息。

You can also learn more from the original proposal, this talk by Greg Kick, and on the [email protected] mailing list.
您还可以从原始提案中了解更多信息,Greg Kick的此演讲以及**邮件列表。

【Installation】
You will need to include the dagger-2.x.jar in your application‘s runtime. In order to activate code generation and generate implementations to manage your graph you will need to include dagger-compiler-2.x.jar in your build at compile time.
您可能需要将dagger-2.x.jar包含在应用程序的运行时中。为了激活代码生成器并生成实现以管理图形,您将需要在编译时将dagger-compiler-2.x.jar包含在构建中。

gradle配置

1、在项目的build.gradle中添加:
PS:在新版本中千万不要加这些,日了狗了,加上去之后反而不会生成DaggerXXXComponent。
dependencies {
    classpath ‘com.android.tools.build:gradle:2.3.1‘//每个Android项目默认都会带的
    //PS:在新版本中千万不要加这些,日了狗了,加上去之后反而不会生成DaggerXXXComponent。
    classpath ‘com.neenbedankt.gradle.plugins:android-apt:1.4‘//这个是需要我们手动添加的,apt是用于自动生成代码来进行依赖注入的
}
android-apt是Gradle编译器的插件,根据其官方文档,主要两个目的:
  • 编译时使用该工具,最终打包时不会将该插件打入到apk中。
  • 能够根据设置的源路径,在编译时期生成相应代码。
2、在module的build.gradle中添加:
apply plugin: ‘com.android.application‘//每个Android项目默认都会带的。在build.gradle的第一行
//PS:在新版本中千万不要加这些,日了狗了,加上去之后反而不会生成DaggerXXXComponent。
apply plugin: ‘com.neenbedankt.android-apt‘//apt支持
3、在module的build.gradle中添加:
compile ‘com.google.dagger:dagger:2.x‘
annotationProcessor ‘com.google.dagger:dagger-compiler:2.x‘//dagger-compiler为编译时期生成代码等相关的类库
在android-apt的文档中,也推荐使用这种方式。因为,编译时期生成代码的类库在运行期并不需要,那么将其分为两个库:运行类库dagger 和 编译器生成代码类库dagger-compiler。那么在打包时,就不需要将dagger-compiler打入其中,减小APK 的大小。

4、其他配置
If you‘re using classes in dagger.android you‘ll also want to include:
compile ‘com.google.dagger:dagger-android:2.x‘
compile ‘com.google.dagger:dagger-android-support:2.x‘ // if you use the support libraries
annotationProcessor ‘com.google.dagger:dagger-android-processor:2.x‘

If you‘re using a version of the Android gradle plugin below 2.2, see https://bitbucket.org/hvisser/android-apt .

If you‘re using the Android Databinding library, you may want to increase the number of errors that javac will print. When Dagger prints an error, databinding compilation will halt and sometimes print more than 100 errors, which is the default amount for javac. For more information, see Issue 306.
如果您正在使用Android Databinding库,您可能需要增加javac将打印的错误数量。当Dagger打印错误时,数据绑定编译将停止,有时会打印超过100个错误,这是javac的默认值。 有关详细信息,请参阅问题306。
gradle.projectsEvaluated {
  tasks.withType(JavaCompile) {
    options.compilerArgs << "-Xmaxerrs" << "500" // or whatever number you want
  }
}

Dagger1和Dagger2

Dagger1可以说是如今Android上最流行的依赖注入框架。它是由Square公司受到Guice启发创建的。
基本特点:
  • 多个注入点:依赖,通过injected
  • 多种绑定方法:依赖,通过provided
  • 多个modules:实现某种功能的绑定集合
  • 多个对象图: 实现一个范围的modules集合
Dagger1是在编译的时候实行绑定,不过也用到了反射机制。但这个反射不是用来实例化对象的,而是用于图的构成。Dagger会在运行的时候去检测是否一切都正常工作,所以使用的时候会付出一些代价:偶尔会无效和调试困难。

Dagger2
Dagger2是Dagger1的分支,由谷歌公司接手开发。Dagger2是受到AutoValue项目的启发。刚开始,Dagger2解决问题的基本思想是:利用生成和写的代码混合达到看似所有的产生和提供依赖的代码都是手写的样子。
如果我们将Dagger2和1比较,他们两个在很多方面都非常相似,但也有很重要的区别,如下:
  • 再也没有使用反射:图的验证、配置和预先设置都在编译的时候执行。
  • 容易调试和可跟踪:完全具体地调用提供和创建的堆栈
  • 更好的性能:谷歌声称他们提高了13%的处理性能
  • 代码混淆:使用派遣方法,就如同自己写的代码一样
当然所有这些很棒的特点都需要付出一个代价,那就是缺乏灵活性,例如:Dagger2没用反射所以没有动态机制。

什么是JSR-330?
为了最大程度的提高代码的复用性、测试性和维护性,java的依赖注入为注入类中的使用定义了一整套注解(和接口)标准。Dagger1和Dagger2(还有Guice)都是基于这套标准,给程序带来了稳定性和标准的依赖注入方法。

一个简单的示例

通过Dagger2的目的是将程序分为三个部分:
  • - 实例化部分:对象的实例化。类似于容器,将类的实例放在容器里。 
  • - 调用者:需要实例化对象的类。 
  • - 沟通桥梁:利用Dagger2中的一些API 将两者联系。
实例化部分Module:
@Module//作为实例对象的容器
public class MainModule {
    String name;
	public MainModule(String name) {
		this.name = name;
	}
	@Provides//标注能够提供实例化对象的方法。方法名字可以随意,但建议以provider开头
	Person providerPerson() {//先判断Module中是否有提供该对象实例化的方法(根据返回值类型及@Provides注解来判断),如果有则返回
		return new Person(name);//如果没有,则查找该类的构造方法,是否有带有@Inject的构造方法。如果存在,则使用此构造方法创建对象后返回
        //如果都没有,则无法为使用者注入该对象的实例
	}
}
沟通桥梁部分Component:
@Component(modules = MainModule.class)  //作为桥梁,沟通调用者和依赖对象库
public interface MainComponent {
	void inject(MainActivity activity);//定义注入的方法。方法名随意,但建议以inject开头
}
调用者Actvity:
public class MainActivity extends AppCompatActivity {
	@Inject Person person;//标注需要注入的对象
	
	@Override
	protected void onCreate(@Nullable Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		DaggerMainComponent.builder().mainModule(new MainModule("白乾涛")).build().inject(this);
		Log.i("bqt", person.name);//包青天。如果去掉providerPerson上的【@Provides】注解,则会调用Person具有@Inject的构造方法
	}
}
看一下Person类:
public class Person {
	public String name;
	@Inject
	public Person() {
        name = "默认的名字";
	}
	public Person(String name) {
		this.name = name;
	}
}
逻辑如下: 
  • 根据@Inject注解,查找需要依赖注入的对象。
  • 先判断Module中是否有提供该对象实例化的方法(即:根据返回值类型及是否@Provides注解来判断),如果有则返回
  • 如果没有,则查找该类的构造方法,是否有带有@Inject的构造方法。如果存在,则使用此构造方法创建对象后返回
  • 如果都没有,则无法为使用者注入该对象的实例

一个MVP模式下的完整案例

V:Activity接口及Activity

public interface IMainView {
	void showToast(String src);
}
public class MainActivity extends AppCompatActivity implements IMainView {
	@Inject IMainPresenter mainPresenter;//注意:如果是通过Module中@Provides注解标注的方法来生成对象,这里可以声明为IMainPresenter
	// 否则,必须声明为MainPresenter,因为此时框架是去查MainPresenter中使用@Inject标注的构造方法,而不是接口中的***
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		DaggerMainComponent.builder()
				.mainModule(new MainModule(this, "白乾涛"))
				.build()
				.inject(this);
		mainPresenter.login("123");
	}
	
	@Override
	public void showToast(String src) {
		Toast.makeText(this, src, Toast.LENGTH_SHORT).show();
	}
}

P:Presenter接口及Presenter

public interface IMainPresenter {
	void login(String password);
}
public class MainPresenter implements IMainPresenter {
	private IMainView mainView;
	private String name;
	@Inject MainModel mainModel;//注意:如果是通过Module中@Provides注解标注的方法来生成对象,这里可以声明为IMainModel
	// 否则,必须声明为MainModel,因为此时框架是去查MainModel中使用@Inject标注的构造方法,而不是接口中的***
	
	public MainPresenter(IMainView mainView, String name) {
		this.mainView = mainView;
		this.name = name;
		Log.i("bqt", "【构造MainPresenter】");
		DaggerMainModelComponent.builder()
				.mainModelModule(new MainModelModule())
				.build()
				.inject(this);
	}
	
	@Override
	public void login(String password) {
		String info = mainModel.login(name, password);
		if (mainView != null) mainView.showToast(info);
		Log.i("bqt", info);
	}
}

MainModule 和 MainComponent

@Module
public class MainModule {
	private IMainView mainView;
	private String name;
	
	public MainModule(IMainView mainView, String name) {
		this.mainView = mainView;
		this.name = name;
		Log.i("bqt", "【构造MainModule】");
	}
	
	@Provides
	IMainPresenter provideMainPresenter() {
		return new MainPresenter(mainView, name);
	}
}
@Component(modules = MainModule.class)
public interface MainComponent {
	void inject(MainActivity activity);//这里必须指定要注入到哪个类里面,参数声明必须是MainActivity而不能是IMainView
}

M:Model相关的4个类

我把这些东西全部放在了MainPresenter类里面,不然文件膨胀太严重了!
//*******************************************以下是MVP中M相关的类***********************************************
interface IMainModel {//在这个案例中,抽象出的M接口完全没有存在的价值了
	String login(String name, String password);
}

class MainModel implements IMainModel {
	@Override
	public String login(String name, String password) {
		return (password == null || password.equals("")) ? "请登录" : "登录成功,你的名字为:" + name;
	}
	
	@Inject
	public MainModel() {
		Log.i("bqt", "【构造MainModel】");
	}
}

@Component(modules = MainModelModule.class)
interface MainModelComponent {
	void inject(MainPresenter mainPresenter);
}

@Module
class MainModelModule {
}
2017-9-17




以上是关于Dagger2简介 配置 使用 MVP案例的主要内容,如果未能解决你的问题,请参考以下文章

当 Dagger2 应用在 MVP 框架中

Android单排上王者系列之Dagger2注入原理解析

Dagger2注入原理解析

Android二手交易平台,dagger2+mvp+Bmob后台云搭建

MVP+Dagger2+Rxjava+Retrofit+GreenDao 开发的小应用,包括新闻图片视频3个大模块,代码封装良好

[Android] 转-RxJava+MVP+Retrofit+Dagger2+Okhttp大杂烩