1.背景
1.1 前言
使用RxJava+Retrofit+MVP+Dagger2开发已经是主流了,MVP 模式网上有很多很好的文章,在此不再多述。
1.2 Dagger2的简单理解
Dagger2简单来说就是依赖注入/管理实例的。实际我们可以理解为:Dagger2代替我们之前直接new出对象,Dagger2内部提供一个类似工厂模式,帮助我们创建/管理实例。这样做的好处就是我们在使用实例的时候,并不是在类中直接实例化,这样就降低了耦合性,同时Dagger2也提供了一些注解来方便我们更好的管理这个实例。
2.开发之路
2.1 Dagger2的配置
2.1.1 在Project的bulid.gradle下添加android-apt插件
// 添加android-apt插件
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
2.1.2 在Module的bulid.gradle下应用插件
// 应用插件
apply plugin: 'com.neenbedankt.android-apt'
2.1.3 在Module的bulid.gradle下添加依赖
// dagger 2 的配置
compile 'com.google.dagger:dagger:2.5'
compile 'com.google.dagger:dagger-compiler:2.5'
// 添加java 注解库
provided 'org.glassfish:javax.annotation:10.0-b28'
2.2 Dagger2的简单使用使用
使用Dagger2前,我们先大概了解下Dagger2常见的注解及其功能,我们在MVP模式开发中,Dagger2常用在Activity/Fragment中注入Presenter对象:
@Inject: 常用在字段,构造方法上
1) 用在字段上,表示需要注入该字段的对象;
@Inject
HomePresenter mHomePresenter;
2) 在构造方法上, 表示通过构造方法提供的具体对象
@Inject
public HomePresenter() {}
@Module: 注解在类上 , 一般和@Provides一起使用, 主要是为@Component提供需要的对象,相当于一个容器的作用
@Provides: 注解在@Module注解的类方法上, 提供无参实例对象或者有参实例的参数实例
@Module //提供需要的对象
public class HomeModule {
private Context mContext;
public HomeModule(Context context){
mContext = context;
}
@Provides
Context providesContext(){
// 提供上下文对象
return mContext;
}
}
@Component: 注解在类上. 一般结合@Component的modules/dependencies属性一起使用.是Dagger2的沟通桥梁
@Component(modules = HomeModule.class)
public interface HomeComponent {
void inject(HomeActivity homeActivity);
}
下面先说最简单的无参实例的Dagger2注入:
2.2.1 无参实例的Dagger2注入
无参实例的Dagger2注入方式有两种:区别是在@Module注解的类中是否使用@Provides提供注入实例,先看使用@Provides的方式一
①方式一
首先创建Module,提供实例化对象
@Module
public class HomeModule {
@Provides// 提供实例化对象,以provide开头
HomePresenter provideHomePresenter(){
return new HomePresenter();
}
}
构建桥梁Component
@Component(modules = HomeModule.class)
public interface HomeComponent {
void inject(HomeActivity homeActivity);
}
在需要注入的类中进行注入,此处为HomeActivity
注解标注要注入的对象
@Inject
HomePresenter mHomePresenter;
此时,我们需要Make Module ‘app’,如图:
这时会在build -> generated -> source -> apt -> debug ->…找到以Dagger开头的类,生成此类的话,就说明Dagger2编译实例成功。在HomeActivity中便可调用以下代码注入对象
DaggerHomeComponent.builder().homeModule(new HomeModule()).build().inject(this);
至此,无参Dagger2注入大功告成。
②方式二
下面我们看下无参实例不使用@Provides的方式二:
在方式一的基础上进行修改
修改@Module注解的类
@Module
public class HomeModule {
//去掉@Provides注解的方法
}
在需要被注入的类的构造中上注解@Inject,以表示要注入这个类的实例
public class HomePresenter implements IHomePresenter{
// @Inject 注解在构造方法上,用来提供实例
@Inject
public HomePresenter() {}
}
其实,@Provides注解,最经常用到的地方是提供注入有参实例时的参数,下面我们就看一下有参实例的Dagger2注入:
2.2.2 有参实例的Dagger2注入
NewsPresenter的构造中需要传入NewsActivity实现的接口
public class NewsPresenter implements INewsPresenter{
private INewsView mView;
// @Inject 注解在构造方法上,用来提供实例
@Inject
public NewsPresenter(INewsView view) {
this.mView = view;
}
}
在NewsModule中我们发现构造中具有参数,所以我们要通过注解提供这个参数
@Module
public class NewsModule {
private INewsView mView;
public NewsModule(INewsView mView) {
this.mView = mView;
}
@Provides
INewsView providesINewsView(){
// 提供构造中需要的参数
return mView;
}
}
NewsComponent搭建注入的桥梁
@Component(modules = NewsModule.class)
public interface NewsComponent {
void inject(NewsActivity newsActivity);
}
在NewsActivity中注解NewsPresenter的成员变量
@Inject
NewsPresenter newsPresenter;
Make Module之后,在NewsActivity中注入NewsPresenter实例的代码:
DaggerNewsComponent.builder().newsModule(new NewsModule(this)).build().inject(this);
以上就是Dagger2的基本运用。
2.3 Dagger2在项目中的使用
2.3.1 Dagger2注入单例对象
在项目中,有时我们需要注入一个单例的对象如果我们使,首先声明,用@Inject注入两次实例,结果是指向不同的对象的,如下
在FirstActivity注入一个单例Person
public class Person {
public String name = "Mr.zhang";
public String age = "26";
}
在FirstActivity中
@Inject
Person person1;
@Inject
Person person2;
这里我们可以通过Dagger2的@Singleton注解,首先需要在@Module注解的类中的提供实例的方法上@Singleton注解
@Module
public class PersonModule {
@Provides
@Singleton
Person providePerson() {
return new Person();
}
}
然后再PersonComonent上进行@Singleton
@Singleton
@Component(modules = PersonModule.class)
public interface PersonComonent {
void inject(FirstActivity firstActivity);
}
完成以上两步,即可将HomePresenter单例化
2.3.2 @Scope自定义作用域注解——注入与Activity同生命周期的实例
其实@Singleton也属于@Scope自定义作用域注解的一种,只是它是java写好的,用来提供一个单例实例。那么在开发中我们常常需要一个实例在某个或者某几个Activity/Fragment中实现单例,这时我们就需要@Scope自定义作用域了。
首先看在一个Activity/Fragment实现单例,举一个简单的例子:在SecondActivity中注入一个User的javabean实例
public class User {
private String username;
private int password;
public User(String username, int password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public int getPassword() {
return password;
}
}
我们需要一个@Scope自定义作用域注解
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface PreActivity {
}
其中
RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
这样,我们就可以用@PreActivity注解了,比如在UserModule中
@Module
public class UserModule {
private String mUsername;
private int mPassword;
public UserModule(String username,int pasword) {
this.mUsername = username;
this.mPassword = pasword;
}
@Provides
String provideUsername(){
return mUsername;
}
@Provides
int providePassword(){
return mPassword;
}
@Provides
@PreActivity
User providesUser(String username,int pasword) {
return new User(username,pasword);
}
}
已经给实例注解上了,下面就是要看注入哪里了,也就是Component中:
@PreActivity
@Component(modules = UserModule.class)
public interface UserComonent {
void inject(SecondActivity secondActivity);
}
在SecondActivity中进行注入User实例
@Inject
User user;
Make Module之后,就会出现DaggerUserComonent,在SecondActivity中调用
DaggerUserComonent.builder().userModule(new UserModule("123",456)).build().inject(this);
这样就大功完成。我们可以在SecondActivity注入两个User实例,即:
@Inject
User user1;
@Inject
User user2;
Toast一下,可以看出这两个User实例的引用地址是同一个的,也就是在SecondActivity的生命周期中,实现了User单例。下面实现在SecondActivity和ThirdActivity两个Activity中实现User单例,其实很简单,修改UserComonent:
@PreActivity
@Component(modules = UserModule.class)
public interface UserComonent {
void inject(SecondActivity secondActivity);
void inject(ThirdActivity thirdActivity);
}
这样就表示:User和SecondActivity及ThirdActivity都搭建了桥梁,Make Module一下,在ThirdActivity中注入
@Inject
User user;
DaggerPersonComonent.builder().personModule(new PersonModule()).build().inject(this);
可以记录一下,SecondActivity和ThirdActivity中的User,发现都是指向一个引用地址。这里你可以试一下,在另外一个Activity中注入一个没有@PreActivity注解的User实例,会发现,这个User实例指向不同的引用
2.3.3 Dagger2注入全局对象
在项目开发中,我们经常需要一些全局的对象,比如Retrofit实例/DaoSession实例等等,并且希望它的生命周期和Application一样。这个时候我们就需要定义一个ApplicationComponent了。另外还有一个好处就是可以让一个ActivityComponent依赖ApplicationComponent,从而可以利用ApplicationComponent向下提供的实例。
先写一个@Scope作用域注解
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface PreApplication {
}
再写一个MyApplicationModule,提供一个Context 实例:
@Module
public class MyApplicationModule {
private Context mContext;
public MyApplicationModule(Context context) {
mContext = context;
}
@Provides
@PreApplication
Context provideApplicationContext() {
return mContext;
}
}
再写一个MyApplicationComponent,向下提供Context实例:
@PreApplication
@Component(modules = MyApplicationModule.class)
public interface MyApplicationComponent {
// 向对其依赖的Component提供Context对象
Context proContext();
}
既然MyApplicationComponent已经向下提供了一个Context对象,如何利用呢?因为下层的Component依赖于MyApplicationComponent,所以先将MyApplicationComponent在MyApplication实例化
public class MyApplication extends Application {
public static MyApplicationComponent sAppComponent;
@Override
public void onCreate() {
super.onCreate();
sAppComponent = DaggerMyApplicationComponent.builder().myApplicationModule(new MyApplicationModule(this)).build();
}
}
前奏已经写完,接下来就是下层组件利用MyApplicationComponent提供的Context对象了,下面进行在ListActivity中注入一个MyAdapter对象
public class MyAdapter {
private Context mContext;
private int mLayoutId;
private List<String> mData;
public MyAdapter(Context context, int layoutId, List<String> data) {
this.mContext = mContext;
this.mLayoutId = mLayoutId;
this.mData = mData;
}
}
MyAdapter的构造中需要三个参数,下面看下MyAdapterModule怎么提供的这三个参数
@Module
public class MyAdapterModule {
private int mLayoutId;
private List<String> mData;
public MyAdapterModule(int layoutId, List<String> data) {
this.mLayoutId = layoutId;
this.mData = data;
}
@Provides
int provideLayoutId(){
return mLayoutId;
}
@Provides
List<String> provideList(){
return mData;
}
@Provides
@PreActivity // 注意:声明其所构造的对象单例
MyAdapter provideMyAdapterContext( Context context, int layoutId, List<String> data){
return new MyAdapter(context,layoutId,data);
}
}
从MyAdapterModule可以看出,我们只使用了@Provides提供了两个参数,Context并没有提供。(注意需要添加@PreActivity声明其所构造的对象单例)
接着就是下层桥梁:MyAdapterComponent
@PreActivity //对应MyAdapterModule的注解
@Component(dependencies = MyApplicationComponent.class,modules = MyAdapterModule.class)
public interface MyAdapterComponent {
void inject(ListActivity listActivity);
}
从这里可以看到组件间的依赖关系,在ListActivity进行注入:
DaggerMyAdapterComponent.builder()
.myApplicationComponent(MyApplication.sAppComponent)
.myAdapterModule(new MyAdapterModule(R.layout.item,data))
.build()
.inject(this);
这样就完成了组件间依赖注入。
奉上本文的Demo地址:https://github.com/Mr-zhang0101/Dagger2Demo.git
感谢:
http://blog.csdn.net/lisdye2/article/details/51942511
http://blog.csdn.net/u012702547/article/details/52213706