文章索引
dagger2 循序漸進(jìn)學(xué)習(xí)(一)依賴(lài)注入基礎(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í)比羅列一大堆的理論名稱(chēng)解釋要來(lái)的實(shí)在。所以接下來(lái)每篇文章主要以一個(gè)馬上能應(yīng)用起來(lái)的小實(shí)例去解釋一個(gè)個(gè)知識(shí)點(diǎn)福稳,文章在精不在多涎拉,希望我寫(xiě)的文字能達(dá)到這樣的效果吧!
dagger2 知識(shí)點(diǎn)
一的圆、@Inject
先來(lái)說(shuō)下上一篇文章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) {
}
}
有沒(méi)有發(fā)現(xiàn)
presenter注入的類(lèi)并沒(méi)有遵循依賴(lài)倒置的原則,就是說(shuō)沒(méi)有依賴(lài)presenter的接口類(lèi)略板,如果我們改成其接口類(lèi)之后運(yùn)行就會(huì)報(bào)錯(cuò)
那這是為什么呢毁枯,可以看出原因就在@Inject
依賴(lài)注入中第一個(gè)并且是最重要的就是 @Inject 注解。JSR-330標(biāo)準(zhǔn)中的一部分叮称,標(biāo)記那些應(yīng)該被依賴(lài)注入框架提供的依賴(lài)种玛。在Dagger 2中有3種不同的方式來(lái)提供依賴(lài):
構(gòu)造器注入,
@Inject標(biāo)注在構(gòu)造器上其實(shí)有兩層意思瓤檐。
①告訴Dagger2可以使用這個(gè)構(gòu)造器構(gòu)建對(duì)象赂韵。如 LoginPresenter 類(lèi)構(gòu)造方法上的@Inject
②注入構(gòu)造器所需要的參數(shù)的依賴(lài)。 如 Pot 類(lèi)挠蛉,構(gòu)造上的Rose會(huì)被注入祭示。又如 LoginPresenter 類(lèi)構(gòu)造方法上的@Inject實(shí)現(xiàn)①的功能外,它本身還需要ILoginContract.ILoginView的依賴(lài)谴古,此時(shí)的注入實(shí)際就在dagger2框架中 LoginModule中提供的质涛,只是什么時(shí)候怎么提供的我們暫時(shí)不得而知
構(gòu)造器注入的局限:如果有多個(gè)構(gòu)造器稠歉,我們只能標(biāo)注其中一個(gè),無(wú)法標(biāo)注多個(gè)汇陆。
屬性注入
如 LoginActivity 類(lèi)怒炸,標(biāo)注在屬性上。被標(biāo)注的屬性不能使用 private 修飾毡代,否則無(wú)法注入阅羹。
屬性注入也是Dagger2中使用最多的一個(gè)注入方式。
看到這里應(yīng)該明白了LoginPresenter是如何注入到LoginActivity中的了:就是1在LoginActivity屬性上標(biāo)注@Inject 教寂,2在LoginPresenter的構(gòu)造方法上同時(shí)標(biāo)注@Inject捏鱼,編譯過(guò)程中,dagger2就知道了LoginActivity的presenter屬性需要注入酪耕,接下來(lái)就去尋找哪里可以提供导梆,其中一項(xiàng)就是就是找有沒(méi)有相應(yīng)類(lèi)的構(gòu)造器用@Inject標(biāo)注的,如果有就用這個(gè)構(gòu)造器構(gòu)造后注入activity因妇。所以當(dāng)我們把LoginActivity中的屬性改成ILoginPresenter接口類(lèi)型的時(shí)候问潭,dagger2就找不到了他的構(gòu)造器了,所以就報(bào)錯(cuò)了婚被。
方法注入
標(biāo)注在public方法上,Dagger2會(huì)在構(gòu)造器執(zhí)行之后立即調(diào)用這個(gè)方法梳虽。
方法注入和屬性注入基本上沒(méi)有區(qū)別址芯, 那么什么時(shí)候應(yīng)該使用方法注入呢?
比如該依賴(lài)需要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è)注解本身并沒(méi)有作用,它需要依賴(lài)于注入框架才具有意義砰左,用來(lái)標(biāo)記需要被注入框架注入的方法匿醒,屬性,構(gòu)造缠导。
而Dagger2則是用 Component 來(lái)完成依賴(lài)注入的廉羔, @Component 可以說(shuō)是Dagger2中最重要的一個(gè)注解。
@Componentpublic interface MainActivityComponent { void inject(MainActivity activity);}
以上是定義一個(gè)Component的方式僻造。使用接口定義憋他,并且 @Component 注解孩饼。
命名方式推薦為: 目標(biāo)類(lèi)名+Component ,在編譯后Dagger2就會(huì)為我們生成 DaggerXXXComponent 這個(gè)類(lèi)竹挡,它是我們定義的 xxxComponent 的實(shí)現(xiàn)镀娶,在目標(biāo)類(lèi)中使用它就可以實(shí)現(xiàn)依賴(lài)注入了。
Component中一般使用兩種方式定義方法此迅。
void inject(目標(biāo)類(lèi) obj); Dagger2會(huì)從目標(biāo)類(lèi)開(kāi)始查找@Inject注解汽畴,自動(dòng)生成依賴(lài)注入的代碼,調(diào)用inject可完成依賴(lài)的注入耸序。
Object getObj(); 如: LoginPresenter getLoginPresentert();Dagger2會(huì)到LoginPresenter類(lèi)中找被@Inject注解標(biāo)注的構(gòu)造器忍些,自動(dòng)生成提供LoginPresenter依賴(lài)的代碼,這種方式一般為其他Component提供依賴(lài)坎怪。(一個(gè)Component可以依賴(lài)另一個(gè)Component罢坝,后面會(huì)說(shuō))
Component和Inject的關(guān)系如下:
Dagger2框架以Component中定義的方法作為入口,到目標(biāo)類(lèi)中尋找JSR-330定義的@Inject標(biāo)注搅窿,生成一系列提供依賴(lài)的Factory類(lèi)和注入依賴(lài)的Injector類(lèi)嘁酿。
而Component則是聯(lián)系Factory和Injector,最終完成依賴(lài)的注入男应。
三闹司、@Module和@Provides
使用@Inject標(biāo)記構(gòu)造器提供依賴(lài)是有局限性的,比如說(shuō)我們需要注入的對(duì)象是第三方庫(kù)提供的沐飘,我們無(wú)法在第三方庫(kù)的構(gòu)造器上加上@Inject注解游桩。
或者,我們使用依賴(lài)倒置的時(shí)候耐朴,因?yàn)樾枰⑷氲膶?duì)象是抽象的借卧,@Inject也無(wú)法使用,因?yàn)槌橄蟮念?lèi)并不能實(shí)例化比如咱們需要ILoginPresenter的接口類(lèi)的依賴(lài)筛峭,會(huì)出現(xiàn)文中最開(kāi)始的那個(gè)錯(cuò)誤铐刘;
這個(gè)時(shí)候就需要Module了。
先清除LoginPresenter中的@Inject
并把LoginAcitivity中的依賴(lài)改成接口類(lèi)型
@Module標(biāo)記在LoginModule類(lèi)上面影晓,@Provodes標(biāo)記在方法上镰吵,表示可以通過(guò)這個(gè)方法獲取依賴(lài)。
在@Component中指定Module
這樣就完了俯艰,ok了捡遍。測(cè)測(cè),沒(méi)有問(wèn)題竹握;
@Module和@Provides的作用:
@Module需要和@Provide是需要一起使用的時(shí)候才具有作用的画株,并且@Component也需要指定了該Module的時(shí)候。
@Module是告訴Component,可以從這里獲取依賴(lài)對(duì)象谓传。Component就會(huì)去找被@Provide標(biāo)注的方法蜈项,相當(dāng)于構(gòu)造器的@Inject,可以提供依賴(lài)续挟。
還有一點(diǎn)要說(shuō)的是紧卒,@Component可以指定多個(gè)@Module的,如果需要提供多個(gè)依賴(lài)的話(huà)诗祸。
并且Component也可以依賴(lài)其它Component存在跑芳。
如此便解決上面提到的問(wèn)題;回顧一下這四個(gè)最重要的注解的用法直颅。接下來(lái)我們研究個(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的接口類(lèi)實(shí)例以往我們經(jīng)常會(huì)寫(xiě)一大堆代碼和模式來(lái)實(shí)現(xiàn)其單例械荷,在dagger中這一切變得簡(jiǎn)單共耍。下面開(kāi)始擼代碼:
相關(guān)的第三方庫(kù)的依賴(lài)就不寫(xiě)了;
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);
}
}
再來(lái)個(gè)module
@Module
public class AppModule {
private final Application application;
public AppModule(Application application) {
this.application = application;
}
@Provides
public Application provideApplication() {
return application;
}
}
那么再來(lái)看我們的application類(lèi)中
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è)先不說(shuō)痹兜,其他的不用我多說(shuō),相信前面都看懂了的同學(xué)颤诀,這幾個(gè)類(lèi)完全能看懂佃蚜。
@Singleton顧名思義,就是單例啊着绊,但是這個(gè)單例可不是怎么用都是單例。通過(guò)測(cè)試熟尉,我們發(fā)現(xiàn)當(dāng)重新build一個(gè)Component的時(shí)候归露,這個(gè)單例也是新的,所以準(zhǔn)確的說(shuō) 斤儿,它的作用只是保證依賴(lài)在@Component中是唯一的剧包,可以理解為“局部單例”。所以在application中實(shí)例化Component往果,其他地方用到它的時(shí)候就需要先在application獲取appComponent進(jìn)行注操作疆液。
通過(guò)這個(gè)方法,當(dāng)然如果也可以把他定義為static方法
public AppComponent getAppComponent() {
return appComponent;
}
接下來(lái)我們遵循循序漸進(jìn)的原則dagger2還有部分知識(shí)點(diǎn)會(huì)在后面的文章繼續(xù)和大家分享陕贮,相信通過(guò)循序漸進(jìn)的邊學(xué)邊實(shí)踐的方式堕油,學(xué)習(xí)起來(lái)更扎實(shí),不像一口吞個(gè)胖子樣!掉缺!