簡書地址: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