這篇文章會分享一下實際應用中的Dagger2如何使用,以及Dagger2通過apt插件如何給我們生成代碼,以及生成的代碼之間的關聯。
下面說一下模擬的業務場景:
主界面MainActivity通過MainPresenter去請求一個接口,並返回數據。這裏用的是MVP+Retrofit2+RxJava,如果不熟悉可以先不管,因爲不會涉及太多,而這篇內容主要分享的是Dagger2。
如果mvp不清楚的可以借鑑 mvp google 寫法;RxJava和Retrofit後期我也會分享出來(知道的略過),歡迎關注!!!
先看下關於Dagger部分的包目錄結構:
首先我需要一個全局的網絡請求對象IRetrofitRequest放在Application,並且是單例的。所以寫了一個RetrofitModule提供IRetrofitRequest實例。
@Singleton @Component(modules = {AppModule.class, RetrofitModule.class}) public interface AppComponent { IRetrofitRequest request(); Context getContext(); }
其中request();方法返回的IRetrofitRequest對象需要上面代碼塊1:依賴的RetrofitModule類中進行實例化:如下代碼
@Module public class RetrofitModule { @Provides @Singleton public IRetrofitRequest getService() { OkHttpClient httpClient = new OkHttpClient.Builder() .connectTimeout(30, TimeUnit.SECONDS)//設置請求超時時間 .retryOnConnectionFailure(true)//設置出現錯誤進行重新連接 .build(); Retrofit retrofit = new Retrofit.Builder() .baseUrl(UrlConst.URL) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .client(httpClient) .build(); return retrofit.create(IRetrofitRequest.class); } }
代碼塊2:中提供的IRetrofitRequest 實例對象必須要用@Provides標註,該對象是單例的所以用@Singleton標註,這裏爲什麼用這兩個註解標註之後就能實現爲AppComponent提供單例的實例,稍後會進行Dagger2生成的代碼解析。
當然一個Component類可以依賴多個Module,如代碼塊1:中還依賴了AppModule,AppModule中提供了在Component方法名是getContext()的實例對象,如下代碼:
@Module public class AppModule { private Context context; public AppModule(Context context) { this.context = context; } @Provides public Context getContext() { return context; } }
如代碼塊2和3所示,所有的被Component依賴的Module都必須用@Module註解標註。因爲Dagger2需要這些標註通過apt插件自動生成代碼。
在AppComponent中提供的IRetrofitRequest單例對象如何在Application中使用呢?
public class App extends Application { private static AppComponent appComponent; @Override public void onCreate() { super.onCreate(); appComponent = DaggerAppComponent.builder() .appModule(new AppModule(getApplicationContext())) .retrofitModule(new RetrofitModule()) .build(); } public static AppComponent getComponent() { return appComponent; } }
上面代碼中的DaggerAppComponent是Dagger2幫我們自動生成的,只要編譯一下就可以自動生成。
AppComponent需要初始化依賴的兩個Module(AppModule和RetrofitModule),這裏生成的DaggerAppComponent是通過構建者模式進行初始化的。
.appModule(new AppModule(getApplicationContext())) .retrofitModule(new RetrofitModule())
最後創建的AppComponent就提供了IRetrofitRequest全局單例對象,整個app的網絡訪問都可以通過該對象進行調用。
AppComponent後期拓展:
一個全局的變量現在統一都可以放在AppComponent中進行管理,這個demo中有網絡請求的一個單例接口對象,一個是全局的Context對象。後期肯定會有其他的都可以放在AppModule中進行實例化,或者單獨再寫一個Module依賴到AppComponent中。
在MainActivity中如何進行使用:
這裏用的是mvp開發模式,所以需要一個Presenter:MainActivityPresenter,需要傳遞一個參數,用於操作MainActivity界面:MainActivityContract.View,而這個MainActivityPresenter誰來提供呢?當然是Component通過依賴的Module來提供,看看MainActivity的Component和Module。
@ActivityScope @Component(dependencies = AppComponent.class, modules = {MainActivityModule.class}) public interface MainActivityComponent { void inject(MainActivity mainActivity); }
@Module public class MainActivityModule { private MainActivityContract.View view; public MainActivityModule(MainActivityContract.View view) { this.view = view; } @ActivityScope @Provides public MainActivityPresenter getMainActivityPresenter() { return new MainActivityPresenter(view); } }
這裏的代碼塊5和6,你會發現我們在MainActivityModule 裏提供了MainActivityPresenter實例,但是在MainActivityComponent接口裏並沒有寫上提供MainActivityPresenter的方法,另外還多了一個void inject(MainActivity mainactivity),這裏跟AppComponent中的(代碼塊1)有區別是咋回事?等會解釋。
再看MainActivity代碼:
public class MainActivity extends BaseActivity implements MainActivityContract.View { @Inject MainActivityPresenter presenter; @Inject SecondActivityPresenter secondActivityPresenter; @Bind(R.id.textView) TextView textView; @Bind(R.id.textView2) TextView textView2; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); presenter.start(); secondActivityPresenter.set(); } @Override public void providers() {//該方法是BaseActivity中onCreate()中調用的抽象方法 DaggerMainActivityComponent.builder() .mainActivityModule(new MainActivityModule(this)) .appComponent(App.getComponent()) .build() .inject(this); } @OnClick({R.id.textView, R.id.textView2}) public void onClick(View view) { switch (view.getId()) { case R.id.textView: startActivity(new Intent(this, SecondActivity.class)); break; case R.id.textView2: break; default: break; } } @Override public void showSuccess() { T.show(this, "成功"); } @Override public void showFailed() { T.show(this, "失敗"); } }
先解釋一下providers()方法:該方法是BaseActivity中onCreate()中調用的抽象方法。
@Inject MainActivityPresenter presenter;
注意1:但是在整個MainActivity中卻找不到初始化的過程,再看providers()方法中的代碼,跟App中有區別的是,我這裏並沒有寫成
MainActivityComponent mainActivityComponent = DaggerMainActivityComponent.builder() .mainActivityModule(new MainActivityModule(this)) .appComponent(App.getComponent()) .build();
注意2:還有的區別是多了一個
.inject(this);
原因:
1.因爲在App中我並沒有哪個對象的聲明用了@Inject註解進行標註,而且App中的AppComponent實例對象需要給其他Activity或者類使用。
2.在該demo中的MainActivity,我們不需要其他地方用到MainActivityComponent對象,我們只是在MainActivity用到MainPresenter對象,我們可以不用通過MainActivityComponent中的某一個方法獲得MainPresenter對象,我們在用@Inject標註MainActivityPresenter presenter的時候需要把在哪裏聲明的外部類(這裏是MainActivity)注入到MainComponent中,就是上面(代碼塊5)說過的沒有提供返回MainPresenter的方法卻多了一個void inject(MainActivity mainactivity);這裏的返回值是void的inject方法名可以是任意的,但是最好寫成inject(官方寫法)。
最後只要調用了providers()方法,我們的MainPresenter presenter對象就已經被初始化了,這個時候就可以通過presenter.start()去調用網絡接口請求數據了;
只要我們配置了以上的Component,Module,編譯之後Dagger2就會通過apt插件生成一系列代碼。
那麼一系列代碼到底是怎樣的?到底是怎樣工作的呢?
先看下生成的代碼目錄結構:
可以看到生成的代碼包名還是跟自己代碼中的一樣,生成的代碼的類名也有一定的規則。
代碼是根據什麼註解生成的:
- 用@Component註解標註的xxxComponent類會生成DaggerxxxComponent類
- 用@Module註解標註的xxxModule中用@Provides註解標註的每個方法都會生成一個類,這個類是一個工廠模式,提供對象實例,比如:
@Module public class AAModule{ @ActivityScope @Providespublic BB getBB() { return new BB(); } }
getBB()方法就會生成AAModule_GetBBFactory類。
- 如果一個類的構造函數用了@Inject註解標註:例如:
public class CC{ @Inject public CC() { } }
就會生成CC_Factory類。
- 如果一個類中有用@Inject註解標註對象聲明他就會生成:比如demo中的MainActivity;
public class MainActivity extends BaseActivity implements MainActivityContract.View { @Inject MainActivityPresenter presenter; }
Dagger2就會自動生成MainActivity_MembersInjector。
代碼之間的關聯
分析圖箭頭的結尾是DaggerMainActivityComponent,也就是最後暴露給我們的就是DaggerMainActivityComponent這個類,其它幫助我們生成的代碼都可以不用關心。