App框架实现———dagger2
2017-12-22 16:30
225 查看
简书地址:http://www.jianshu.com/p/519dc63e5297
github地址:https://github.com/jiahongfei/ArrowEngine
由于公司App架构需要调整,所以最近在封装App框架,首先给上github地址请点击这里。
该框架的组成
目前只是个最简单的封装版本,后续会对这个框架进行维护,希望大家多多支持。
下文中的dagger2依赖注入用DI简写来代替。
用来标记字段,表示这个字段需要DI来提供实例。
用来标记构造方法,表示这个类可以用来提供DI实例。也就是提供
标记的构造方法有参数,那么参数由DI实例来提供。
如下代码例子:
@Module
用来标记类,这个类用来提供DI实例。也就是给
@Provides
用来标记方法,在
有两种方式可以提供DI实例,第一种
@Module和@Provides如下代码
@Component
它是
如下代码例子:
@Qualifier
限定符:用于标记方法,如果
在
如下代码例子:
@Named(“name”)
使用场景完全和
@Singleton
作用域:用于标记类或者方法,从字面意思看大家肯定会想到单例模式,一个类如果加上这个注解这个类就变成了单例?答案是否定的,也就是这个注解不会使类变成单例这点千万要记住,他只是一个标记,表示这个类的实现是单例的。 在App中只存在一个,具体的单例代码还是需要自己写。
如下代码例子:
@Scope
自定义作用域注解:他的作用也是使程序员可以直观的看到这个类的作用域,其他没有任何作用。
步骤1:查找Module中是否存在创建该类的方法。
步骤2:若存在创建类方法,查看该方法是否存在参数
步骤2.1:若存在参数,则按从步骤1开始依次初始化每个参数
步骤2.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
步骤3:若不存在创建类方法,则查找Inject注解的构造函数,
看构造函数是否存在参数
步骤3.1:若存在参数,则从步骤1开始依次初始化每个参数
步骤3.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
解耦是
总结一句话,
2.方便Mock数据
在开发前期往往都是客户端对着接口文档进行开发业务逻辑,这就涉及到读取本地的Mock数据。我们做的是将
3.方便单元测试
由于依赖对象都是通过DI注入到类中的,所以通过
参考文献:
http://www.jianshu.com/p/1d42d2e6f4a5
http://www.jianshu.com/p/65737ac39c44
http://www.jianshu.com/p/cd2c1c9f68d4
github地址:https://github.com/jiahongfei/ArrowEngine
由于公司App架构需要调整,所以最近在封装App框架,首先给上github地址请点击这里。
该框架的组成
MVP+Dagger2+RxJava+Retrofit+OkHttp+RxCache+单元测试(Junit+Mockito)
目前只是个最简单的封装版本,后续会对这个框架进行维护,希望大家多多支持。
本篇文章首先介绍Dagger2依赖注入框架(Dependency Injection)
下文中的dagger2依赖注入用DI简写来代替。
Dagger2 关键字
@Inject用来标记字段,表示这个字段需要DI来提供实例。
用来标记构造方法,表示这个类可以用来提供DI实例。也就是提供
@Inject修饰的字段的实例。
标记的构造方法有参数,那么参数由DI实例来提供。
@Inject标记类的构造方法不能进行重载。也就是只能有一个构造方法。
如下代码例子:
public class BaseActivity<T extends IPresenter> extends AppCompatActivity { ...... //用来标记字段,表示mPresenter需要DI来提供实例 @Inject protected T mPresenter; ...... } public class DataRepositoryManager implements IDataRepositoryManager { ...... private Retrofit retrofit; private RxCache rxCache; //标记构造方法,表示DataRepositoryManager类可以提供DI实例 //retrofit和rxCache需要DI来提供实例 //@Inject标记构造方法之后,这个类只能有一个构造方法,不能重载 @Inject public DataRepositoryManager(Retrofit retrofit, RxCache rxCache){ this.retrofit = retrofit; this.rxCache = rxCache; } ...... }
@Module
用来标记类,这个类用来提供DI实例。也就是给
@Inject标记的字段或者
@Inject标记的构造方法参数提供实例。(只有标记
@Provides的方法才能提供DI实例,请见下文)
@Provides
用来标记方法,在
@Module标记的类中,只有
@Provides标记的方法才能提供DI实例。其他方法都是普通方法。
有两种方式可以提供DI实例,第一种
@Inject标记的构造方法,第二种在
@Module标记的类中,只有
@Provides标记的方法
@Module和@Provides如下代码
@Singleton //这个标记表示这个类中的方法可以提供DI实例 @Module public class DataRepositoryModule { ...... @Singleton //这个标记表示这个方法可以提供DI实例,也就是提供RxCache。 //提供上文中DataRepositoryManager构造方法的RxCache参数 //同时这个方法的参数也是需要DI实例 @Provides RxCache providerRxCache(Application application, File cacheDir, @Nullable RxCacheConfig rxCacheConfig) { RxCache.Builder builder = new RxCache.Builder(); if (null != rxCacheConfig) { rxCacheConfig.configRxCache(application, builder); } return builder.persistence(cacheDir, new GsonSpeaker()); } ...... }
@Component
它是
@Module和
@Inject之间沟通的桥梁,他将
@Module类中
@Provides标记的方法返回的实例赋值给
@Inject标记的字段或者
@Inject标记构造方法的参数。
如下代码例子:
@Singleton //modules表示DI实例是由ApplicationModule.class, DataRepositoryModule.class, AppDelegateConfig.class类中的@Provides方法提供 @Component(modules = {ApplicationModule.class, DataRepositoryModule.class, AppDelegateConfig.class}) public interface AppComponent { //这个方法必须写,表示AppDelegateManager类中需要的DI实例由ApplicationModule.class, DataRepositoryModule.class, AppDelegateConfig.class类中的@Provides方法提供 void inject(AppDelegateManager delegate); //由于@Component标记的类可以依赖"dependencies"(也可以理解为类的继承),所以想要暴露的方法就要在这个类中定义,这些方法必须都是"modules"提供的方法,如下方法可以在依赖的类中使用 Application getApplication(); RxCache getRxCache(); Retrofit getRetrofit(); ...... } @ActivityScope //LoginComponent继承了AppComponent.class中提供的方法,也就是上面代码中,声明的方法 @Component(dependencies = AppComponent.class, modules = LoginModule.class) public interface LoginComponent { void inject(LoginActivity loginActivity); }
@Qualifier
限定符:用于标记方法,如果
@Provides标记的方法返回值相同必须要通过
@Qualifier定义的注解来区分使用哪个方法返回DI实例,否则编译不通过。
在
@Inject标记的字段或者
@Inject标记的构造方法的参数,用
@Qualifier定义的注解来区分需要的DI实例。
如下代码例子:
//限定符用来定义注解,这个注解提供本地Mock数据 @Qualifier @Documented @Retention(RetentionPolicy.RUNTIME) public @interface MockData { } @Singleton @Component(modules = {ApplicationModule.class, DataRepositoryModule.class, AppDelegateConfig.class}) public interface AppComponent { void inject(AppDelegateManager delegate); ...... //如下两个方法的返回值类型相同,如果不用@Qualifier进行标记,是无法通过编译的, //原因是由@Inject标记的字段无法找到准确的方法来提供DI实例。 IDataRepositoryManager providerRepositoryManager(); @MockData IDataRepositoryManager providerMockRepositoryManager(); ...... } @Singleton @Module public class DataRepositoryModule { ...... //如下两个方法的返回值类型相同,如果不用@Qualifier进行标记,是无法通过编译的, //原因是由@Inject标记的字段无法找到准确的方法来提供 bf70 DI实例。 @Singleton @Provides public IDataRepositoryManager providerRepositoryManager(Retrofit retrofit, RxCache rxCache) { return new DataRepositoryManager(retrofit, rxCache); } @Singleton @Provides @MockData public IDataRepositoryManager providerMockRepositoryManager(Application application, @Nullable DataRepositoryModule.MockDataConfig mockDataConfig) { return new MockDataRespsitoryManager(application,mockDataConfig); } ...... } public class LoginInteractorImpl extends BaseModel implements LoginContract.LoginInteractor { private ServiceApi serviceApi; //下面构造方法的参数由DI来提供,@MockData限定符表示选择上面代码片段的providerMockRepositoryManager方法来提供DI实例, //如果去掉@MockData限定符表示使用上面代码片段的providerRepositoryManager来提供DI实例 @Inject public LoginInteractorImpl(@MockData IDataRepositoryManager repositoryManager) { super(repositoryManager); serviceApi = repositoryManager.getRepositoryDataService(ServiceApi.class); } @Override public Observable<ApiResponse<UserLogin>> getUserLogin(String phone, String smsCode, String blockBox, String extendParam) { return serviceApi.getUserLogin(phone,smsCode,blockBox,extendParam); } ...... }
@Named(“name”)
使用场景完全和
@Qualifier一样,但是他是通过“()”中的字符串来区分的。
@Singleton
作用域:用于标记类或者方法,从字面意思看大家肯定会想到单例模式,一个类如果加上这个注解这个类就变成了单例?答案是否定的,也就是这个注解不会使类变成单例这点千万要记住,他只是一个标记,表示这个类的实现是单例的。 在App中只存在一个,具体的单例代码还是需要自己写。
如下代码例子:
//只是标记这个类要被实现成单例,需要自己保证类的对象在App声明周期内只有一个 @Singleton @Component(modules = {ApplicationModule.class, DataRepositoryModule.class, AppDelegateConfig.class}) public interface AppComponent { void inject(AppDelegateManager delegate); ...... } //用这个类来保证AppComponent在App声明周期只有一个实例,单例 public class AppDelegateManager { private AppComponent appComponent; private static final AppDelegateManager sAppDelegateManager = new AppDelegateManager(); public static final AppDelegateManager getInstance() { return sAppDelegateManager; } protected AppDelegateManager() { } public final void init(Application application, AppDelegateConfig appDelegateConfig) { appComponent = DaggerAppComponent .builder() .applicationModule(new ApplicationModule(application)) .dataRepositoryModule(new DataRepositoryModule()) .appDelegateConfig(appDelegateConfig) .build(); appComponent.inject(this); } public AppComponent getAppComponent() { return appComponent; } } //在自定义的Application类中初始化AppDelegateManager,来保证在App声明周期内只有一个AppComponent实例 public class CustomApplication extends Application implements IApp { ...... @Override public void onCreate() { super.onCreate(); AppDelegateConfig appDelegateConfig = new AppDelegateConfig .Builder() .setBaseUrl("http://www.weather.com.cn/adat/sk/") .setCacheDir(getCacheDir()) .builder(); AppDelegateManager.getInstance().init(this, appDelegateConfig); } @Override public AppComponent getAppComponent() { return AppDelegateManager.getInstance().getAppComponent(); } ...... }
@Scope
自定义作用域注解:他的作用也是使程序员可以直观的看到这个类的作用域,其他没有任何作用。
@Component依赖关系中
@Scope注解必须不相同否则会报错。
// 用来标识Activity作用域 @Scope @Documented @Retention(RetentionPolicy.RUNTIME) public @interface ActivityScope { } @Singleton @Component(modules = {ApplicationModule.class, DataRepositoryModule.class, AppDelegateConfig.class}) public interface AppComponent { ...... void inject(AppDelegateManager delegate); ...... } //LoginComponent类依赖AppComponent类他必须用@ActivityScope来标记进行区分,否则编译报错 @ActivityScope @Component(dependencies = AppComponent.class, modules = LoginModule.class) public interface LoginComponent { void inject(LoginActivity loginActivity); }
Dagger2依赖注入的过程
这里引用牛晓伟大神文章中的一段话步骤1:查找Module中是否存在创建该类的方法。
步骤2:若存在创建类方法,查看该方法是否存在参数
步骤2.1:若存在参数,则按从步骤1开始依次初始化每个参数
步骤2.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
步骤3:若不存在创建类方法,则查找Inject注解的构造函数,
看构造函数是否存在参数
步骤3.1:若存在参数,则从步骤1开始依次初始化每个参数
步骤3.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
插图
Dagger2总结:
1.解耦解耦是
dagger2最大的好处,类不可能是单独存在的否则他将毫无意义,这样就会出现在这个类中会有很多的类进行组合使用来完成业务逻辑,在这个类中就会有很多的
new class,假如
AClass这个类在,
BClass、CClass、DClass…….N多个类中都
new了一个实例,这时为了满足业务逻辑
AClass的构造方法需要修改,增加参数或者修改参数,这回是灾难性的后果,在每个
new的地方都需要修改一遍N多个类,如果不小心就会出现各种奇怪的bug。更加复杂的是对象的相互依赖,
dagger2就是为了解耦,创建对象在同一的类中只有一份,然后将对象注入到需要的类中,这样如果修改了只会在一个地方进行 修改达到解耦的效果。
总结一句话,
dagger2省略了N多个
new class,只需要在一个地方创建对象。
2.方便Mock数据
在开发前期往往都是客户端对着接口文档进行开发业务逻辑,这就涉及到读取本地的Mock数据。我们做的是将
mock数据写成
json文件保存到本地,切换传入
Model(MVP中的M)类中的数据获取方式来读取本地
mock数据,数据获取方式
DataRepository就是通过依赖注入(DI)注入到
Model中,可以统一切换很方便。
3.方便单元测试
由于依赖对象都是通过DI注入到类中的,所以通过
Mockito.mock对象非常方便,单元测试稍后文章会讲到。
参考文献:
http://www.jianshu.com/p/1d42d2e6f4a5
http://www.jianshu.com/p/65737ac39c44
http://www.jianshu.com/p/cd2c1c9f68d4
相关文章推荐
- Android实战:手把手实现“捧腹网”APP(二)-----捧腹APP原型设计、实现框架选取
- android实现高仿新版今日头条app1(框架部分)
- android MVP + dagger2 + Retrofit + Rxjava+okhttp android基础项目框架搭建(3)--完美实现
- Android实战:手把手实现“捧腹网”APP(二)-----捧腹APP原型设计、实现框架选取
- 信息系统开发平台OpenExpressApp:【OpenTest】 之 框架实现说明
- 从今天开始学习iOS开发(iOS 7版)--实现一款App之Foundation框架的使用
- 信息系统开发平台OpenExpressApp:【OpenTest】 之 框架实现说明
- 信息系统开发平台OpenExpressApp:【OpenTest】 之 如何实现自动化测试框架
- Android开发ViewPager和Fragment结合使用实现新闻类app基本框架(一)
- 信息系统开发平台OpenExpressApp:【OpenTest】 之 如何实现自动化测试框架
- 如何实现手机app制作,常用开源框架汇总
- ReactMix框架,让你实现一套js代码,基于ReactNative在H5,App都能完美跑起来,Write Once,Run Anywhere
- QuickTV:一款基于AndroidQuick快速开发框架实现的直播APP
- 一分钟实现版本更新功能——APP版本更新框架
- 从今天开始学习iOS开发(iOS 7版)--实现一款App之Foundation框架的使用
- iOS开发之旅(1):实现一个APP界面框架
- 利用CTS框架实现APP的自动化白盒测试
- AngularJS框架的ng-app指令与自动加载实现方法分析
- iOS开发之旅(2):实现一个APP界面框架之搜索功能