文章索引
dagger2 循序漸進(jìn)學(xué)習(xí)(一)依賴注入基礎(chǔ)知識(shí)(包會(huì))
dagger2 循序漸進(jìn)學(xué)習(xí)(二)
dagger2 循序漸進(jìn)學(xué)習(xí)(三) 實(shí)例1,application中的應(yīng)用
前兩篇dagger2的文章介紹了其基本的使用方法,但其使用中還有很多重要的細(xì)節(jié)需要我們掌握,我認(rèn)為在實(shí)例中學(xué)習(xí)比羅列一大堆的理論名稱解釋要來的實(shí)在。所以接下來每篇文章主要以一個(gè)馬上能應(yīng)用起來的小實(shí)例去解釋一個(gè)個(gè)知識(shí)點(diǎn),文章在精不在多,希望我寫的文字能達(dá)到這樣的效果吧!
dagger2 知識(shí)點(diǎn)
一、@Inject
先來說下上一篇文章dagger2 循序漸進(jìn)學(xué)習(xí)(二) ,文中的activity
public class LoginActivity extends AppCompatActivity implements ILoginContract.ILoginView{
@Inject//這里加注解
LoginPresenter presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
}
@Override
public void loginok() {
}
@Override
public void loginErro() {
}
}
文中的presenter:
public class LoginPresenter implements ILoginContract.ILoginPresenter {
ILoginContract.ILoginView view;
@Inject//這里注解
public LoginPresenter(ILoginContract.ILoginView view) {
this.view = view;
}
@Override
public void login(String name, String pwd) {
}
}
有沒有發(fā)現(xiàn)
presenter注入的類并沒有遵循依賴倒置的原則,就是說沒有依賴presenter的接口類,如果我們改成其接口類之后運(yùn)行就會(huì)報(bào)錯(cuò)
那這是為什么呢,可以看出原因就在@Inject
依賴注入中第一個(gè)并且是最重要的就是 @Inject 注解。JSR-330標(biāo)準(zhǔn)中的一部分,標(biāo)記那些應(yīng)該被依賴注入框架提供的依賴。在Dagger 2中有3種不同的方式來提供依賴:
構(gòu)造器注入,
@Inject標(biāo)注在構(gòu)造器上其實(shí)有兩層意思。
①告訴Dagger2可以使用這個(gè)構(gòu)造器構(gòu)建對(duì)象。如 LoginPresenter 類構(gòu)造方法上的@Inject
②注入構(gòu)造器所需要的參數(shù)的依賴。 如 Pot 類,構(gòu)造上的Rose會(huì)被注入。又如 LoginPresenter 類構(gòu)造方法上的@Inject實(shí)現(xiàn)①的功能外,它本身還需要ILoginContract.ILoginView的依賴,此時(shí)的注入實(shí)際就在dagger2框架中 LoginModule中提供的,只是什么時(shí)候怎么提供的我們暫時(shí)不得而知
構(gòu)造器注入的局限:如果有多個(gè)構(gòu)造器,我們只能標(biāo)注其中一個(gè),無法標(biāo)注多個(gè)。
屬性注入
如 LoginActivity 類,標(biāo)注在屬性上。被標(biāo)注的屬性不能使用 private 修飾,否則無法注入。
屬性注入也是Dagger2中使用最多的一個(gè)注入方式。
看到這里應(yīng)該明白了LoginPresenter是如何注入到LoginActivity中的了:就是1在LoginActivity屬性上標(biāo)注@Inject ,2在LoginPresenter的構(gòu)造方法上同時(shí)標(biāo)注@Inject,編譯過程中,dagger2就知道了LoginActivity的presenter屬性需要注入,接下來就去尋找哪里可以提供,其中一項(xiàng)就是就是找有沒有相應(yīng)類的構(gòu)造器用@Inject標(biāo)注的,如果有就用這個(gè)構(gòu)造器構(gòu)造后注入activity。所以當(dāng)我們把LoginActivity中的屬性改成ILoginPresenter接口類型的時(shí)候,dagger2就找不到了他的構(gòu)造器了,所以就報(bào)錯(cuò)了。
方法注入
標(biāo)注在public方法上,Dagger2會(huì)在構(gòu)造器執(zhí)行之后立即調(diào)用這個(gè)方法。
方法注入和屬性注入基本上沒有區(qū)別, 那么什么時(shí)候應(yīng)該使用方法注入呢?
比如該依賴需要this對(duì)象的時(shí)候,使用方法注入可以提供安全的this對(duì)象,因?yàn)?strong>方法注入是在構(gòu)造器之后執(zhí)行的。
比如google mvp dagger2中,給View設(shè)置Presenter的時(shí)候可以這樣使用方法注入。
/**
* Method injection is used here to safely reference {@code this} after the object is created.
* For more information, see Java Concurrency in Practice.
*/
@Inject
void setupListeners() {
mTasksView.setPresenter(this);
}
二、@Component
@Inject 注解是JSR-330中定義的注解,在 javax.inject 包中。
這個(gè)注解本身并沒有作用,它需要依賴于注入框架才具有意義,用來標(biāo)記需要被注入框架注入的方法,屬性,構(gòu)造。
而Dagger2則是用 Component 來完成依賴注入的, @Component 可以說是Dagger2中最重要的一個(gè)注解。
@Componentpublic interface MainActivityComponent { void inject(MainActivity activity);}
以上是定義一個(gè)Component的方式。使用接口定義,并且 @Component 注解。
命名方式推薦為: 目標(biāo)類名+Component ,在編譯后Dagger2就會(huì)為我們生成 DaggerXXXComponent 這個(gè)類,它是我們定義的 xxxComponent 的實(shí)現(xiàn),在目標(biāo)類中使用它就可以實(shí)現(xiàn)依賴注入了。
Component中一般使用兩種方式定義方法。
void inject(目標(biāo)類 obj); Dagger2會(huì)從目標(biāo)類開始查找@Inject注解,自動(dòng)生成依賴注入的代碼,調(diào)用inject可完成依賴的注入。
Object getObj(); 如: LoginPresenter getLoginPresentert();Dagger2會(huì)到LoginPresenter類中找被@Inject注解標(biāo)注的構(gòu)造器,自動(dòng)生成提供LoginPresenter依賴的代碼,這種方式一般為其他Component提供依賴。(一個(gè)Component可以依賴另一個(gè)Component,后面會(huì)說)
Component和Inject的關(guān)系如下:
Dagger2框架以Component中定義的方法作為入口,到目標(biāo)類中尋找JSR-330定義的@Inject標(biāo)注,生成一系列提供依賴的Factory類和注入依賴的Injector類。
而Component則是聯(lián)系Factory和Injector,最終完成依賴的注入。
三、@Module和@Provides
使用@Inject標(biāo)記構(gòu)造器提供依賴是有局限性的,比如說我們需要注入的對(duì)象是第三方庫提供的,我們無法在第三方庫的構(gòu)造器上加上@Inject注解。
或者,我們使用依賴倒置的時(shí)候,因?yàn)樾枰⑷氲膶?duì)象是抽象的,@Inject也無法使用,因?yàn)槌橄蟮念惒⒉荒軐?shí)例化比如咱們需要ILoginPresenter的接口類的依賴,會(huì)出現(xiàn)文中最開始的那個(gè)錯(cuò)誤;
這個(gè)時(shí)候就需要Module了。
先清除LoginPresenter中的@Inject
并把LoginAcitivity中的依賴改成接口類型
@Module標(biāo)記在LoginModule類上面,@Provodes標(biāo)記在方法上,表示可以通過這個(gè)方法獲取依賴。
在@Component中指定Module
這樣就完了,ok了。測(cè)測(cè),沒有問題;
@Module和@Provides的作用:
@Module需要和@Provide是需要一起使用的時(shí)候才具有作用的,并且@Component也需要指定了該Module的時(shí)候。
@Module是告訴Component,可以從這里獲取依賴對(duì)象。Component就會(huì)去找被@Provide標(biāo)注的方法,相當(dāng)于構(gòu)造器的@Inject,可以提供依賴。
還有一點(diǎn)要說的是,@Component可以指定多個(gè)@Module的,如果需要提供多個(gè)依賴的話。
并且Component也可以依賴其它Component存在。
如此便解決上面提到的問題;回顧一下這四個(gè)最重要的注解的用法。接下來我們研究個(gè)小實(shí)例。
實(shí)例
這個(gè)實(shí)例就是我們經(jīng)常要實(shí)現(xiàn)的application中retrofit的應(yīng)用。為了節(jié)省資源提高性能,要求我們?cè)谑褂胷etrofit時(shí)候,整個(gè)app中retrofit是單例的,然后再用他create相應(yīng)的api的接口類實(shí)例以往我們經(jīng)常會(huì)寫一大堆代碼和模式來實(shí)現(xiàn)其單例,在dagger中這一切變得簡(jiǎn)單。下面開始擼代碼:
相關(guān)的第三方庫的依賴就不寫了;
api的請(qǐng)求接口是這樣?jì)饍旱?/p>
public interface ApiService {
@GET("/users/{user}/repos")
Observable<ArrayList<String>> getRepoData(@Path("user") String user);
}
再上個(gè)module
@Module
public class ServiceModule {
@Provides
public OkHttpClient provideOkHttpClient() {
OkHttpClient okHttpClient ;
OkHttpClient.Builder builder= new OkHttpClient.Builder();
okHttpClient=builder.readTimeout(60 * 1000, TimeUnit.MILLISECONDS)
.connectTimeout(60 * 1000, TimeUnit.MILLISECONDS)
.build();
return okHttpClient;
}
@Provides
public Retrofit provideRetrofit(Application application, OkHttpClient okHttpClient){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(application.getString(R.string.api_host))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create()) // 添加Rx適配器
.addConverterFactory(GsonConverterFactory.create()) // 添加Gson轉(zhuǎn)換器
.client(okHttpClient)
.build();
return retrofit;
}
@Singleton
@Provides
protected ApiService provideGitHubService(Retrofit retrofit) {
return retrofit.create(ApiService.class);
}
}
再來個(gè)module
@Module
public class AppModule {
private final Application application;
public AppModule(Application application) {
this.application = application;
}
@Provides
public Application provideApplication() {
return application;
}
}
那么再來看我們的application類中
public class App extends Application {
private AppComponent appComponent;
@Override
public void onCreate() {
super.onCreate();
appComponent=
DaggerAppComponent.builder()
.appModule(new AppModule(this))
.serviceModule(new ServiceModule())
.build();
}
public AppComponent getAppComponent() {
return appComponent;
}
}
眼尖的同學(xué)看到了@Singleton,這個(gè)先不說,其他的不用我多說,相信前面都看懂了的同學(xué),這幾個(gè)類完全能看懂。
@Singleton顧名思義,就是單例啊,但是這個(gè)單例可不是怎么用都是單例。通過測(cè)試,我們發(fā)現(xiàn)當(dāng)重新build一個(gè)Component的時(shí)候,這個(gè)單例也是新的,所以準(zhǔn)確的說 ,它的作用只是保證依賴在@Component中是唯一的,可以理解為“局部單例”。所以在application中實(shí)例化Component,其他地方用到它的時(shí)候就需要先在application獲取appComponent進(jìn)行注操作。
通過這個(gè)方法,當(dāng)然如果也可以把他定義為static方法
public AppComponent getAppComponent() {
return appComponent;
}
接下來我們遵循循序漸進(jìn)的原則dagger2還有部分知識(shí)點(diǎn)會(huì)在后面的文章繼續(xù)和大家分享,相信通過循序漸進(jìn)的邊學(xué)邊實(shí)踐的方式,學(xué)習(xí)起來更扎實(shí),不像一口吞個(gè)胖子樣!!