简书地址:http://www.jianshu.com/p/519dc63e5297git
github地址:https://github.com/jiahongfei/ArrowEngine程序员
因为公司App架构须要调整,因此最近在封装App框架,首先给上github地址请点击这里。
该框架的组成MVP+Dagger2+RxJava+Retrofit+OkHttp+RxCache+单元测试(Junit+Mockito)
目前只是个最简单的封装版本,后续会对这个框架进行维护,但愿你们多多支持。github
下文中的dagger2依赖注入用DI简写来代替。json
用来标记字段,表示这个字段须要DI来提供实例。架构
用来标记构造方法,表示这个类能够用来提供DI实例。也就是提供@Inject
修饰的字段的实例。 标记的构造方法有参数,那么参数由DI实例来提供。app
@Inject
标记类的构造方法不能进行重载。也就是只能有一个构造方法。框架
以下代码例子:ide
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; } ...... }
用来标记类,这个类用来提供DI实例。也就是给@Inject
标记的字段或者@Inject
标记的构造方法参数提供实例。(只有标记@Provides
的方法才能提供DI实例,请见下文)函数
用来标记方法,在@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()); } ...... }
它是@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); }
限定符:用于标记方法,若是@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标记的字段没法找到准确的方法来提供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); } ...... }
使用场景彻底和@Qualifier
同样,可是他是经过“()"中的字符串来区分的。
做用域:用于标记类或者方法,从字面意思看你们确定会想到单例模式,一个类若是加上这个注解这个类就变成了单例?答案是否认的,也就是这个注解不会使类变成单例这点千万要记住,他只是一个标记,表示这个类的实现是单例的。 在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(); } ...... }
自定义做用域注解:他的做用也是使程序员能够直观的看到这个类的做用域,其余没有任何做用。 @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); }
步骤1:查找Module中是否存在建立该类的方法。
步骤2:若存在建立类方法,查看该方法是否存在参数
步骤2.1:若存在参数,则按从**步骤1**开始依次初始化每一个参数 步骤2.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
步骤3:若不存在建立类方法,则查找Inject注解的构造函数, 看构造函数是否存在参数
步骤3.1:若存在参数,则从**步骤1**开始依次初始化每一个参数 步骤3.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
解耦是dagger2
最大的好处,类不多是单独存在的不然他将毫无心义,这样就会出如今这个类中会有不少的类进行组合使用来完成业务逻辑,在这个类中就会有不少的new class
,假如AClass
这个类在,BClass、CClass、DClass
.......N多个类中都new
了一个实例,这时为了知足业务逻辑AClass
的构造方法须要修改,增长参数或者修改参数,这回是灾难性的后果,在每一个new
的地方都须要修改一遍N多个类,若是不当心就会出现各类奇怪的bug。更加复杂的是对象的相互依赖,dagger2
就是为了解耦,建立对象在同一的类中只有一份,而后将对象注入到须要的类中,这样若是修改了只会在一个地方进行 修改达到解耦的效果。
总结一句话,dagger2
省略了N多个new class
,只须要在一个地方建立对象。
在开发前期每每都是客户端对着接口文档进行开发业务逻辑,这就涉及到读取本地的Mock数据。咱们作的是将mock
数据写成json
文件保存到本地,切换传入Model
(MVP中的M)类中的数据获取方式来读取本地mock
数据,数据获取方式DataRepository
就是经过依赖注入(DI)注入到Model
中,能够统一切换很方便。
因为依赖对象都是经过DI注入到类中的,因此经过Mockito.mock
对象很是方便,单元测试稍后文章会讲到。
参考文献:
http://www.jianshu.com/p/1d42d2e6f4a5