Dagger2系列——實例解析

這篇文章會分享一下實際應用中的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這個類,其它幫助我們生成的代碼都可以不用關心。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章