App框架實現———dagger2

簡書地址: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依賴注入框架(Dependency Injection)

Dagger2目錄.png

下文中的dagger2依賴注入用DI簡寫來代替。json

Dagger2 關鍵字

@Inject

用來標記字段,表示這個字段須要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;
    }
    ......
}
@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標記的字段沒法找到準確的方法來提供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.png

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

相關文章
相關標籤/搜索