dagger2 循序漸進(jìn)學(xué)習(xí)(包會(huì))(三) 實(shí)例1,application中的應(yīng)用

文章索引
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í)比羅列一大堆的理論名稱解釋要來(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)

Paste_Image.png

presenter注入的類(lèi)并沒(méi)有遵循依賴倒置的原則,就是說(shuō)沒(méi)有依賴presenter的接口類(lèi),如果我們改成其接口類(lèi)之后運(yùn)行就會(huì)報(bào)錯(cuò)

Paste_Image.png

那這是為什么呢,可以看出原因就在@Inject
依賴注入中第一個(gè)并且是最重要的就是 @Inject 注解。JSR-330標(biāo)準(zhǔn)中的一部分,標(biāo)記那些應(yīng)該被依賴注入框架提供的依賴。在Dagger 2中有3種不同的方式來(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ù)的依賴。 如 Pot 類(lèi),構(gòu)造上的Rose會(huì)被注入。又如 LoginPresenter 類(lèi)構(gòu)造方法上的@Inject實(shí)現(xiàn)①的功能外,它本身還需要ILoginContract.ILoginView的依賴,此時(shí)的注入實(shí)際就在dagger2框架中 LoginModule中提供的,只是什么時(shí)候怎么提供的我們暫時(shí)不得而知

Paste_Image.png

構(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ò)了。

方法注入

Paste_Image.png

標(biāo)注在public方法上,Dagger2會(huì)在構(gòu)造器執(zhí)行之后立即調(diào)用這個(gè)方法。

方法注入和屬性注入基本上沒(méi)有區(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è)注解本身并沒(méi)有作用,它需要依賴于注入框架才具有意義,用來(lái)標(biāo)記需要被注入框架注入的方法,屬性,構(gòu)造。
而Dagger2則是用 Component 來(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)依賴注入了。
Component中一般使用兩種方式定義方法。
void inject(目標(biāo)類(lèi) obj); Dagger2會(huì)從目標(biāo)類(lèi)開(kāi)始查找@Inject注解,自動(dòng)生成依賴注入的代碼,調(diào)用inject可完成依賴的注入。
Object getObj(); 如: LoginPresenter getLoginPresentert();Dagger2會(huì)到LoginPresenter類(lèi)中找被@Inject注解標(biāo)注的構(gòu)造器,自動(dòng)生成提供LoginPresenter依賴的代碼,這種方式一般為其他Component提供依賴。(一個(gè)Component可以依賴另一個(gè)Component,后面會(huì)說(shuō))

Component和Inject的關(guān)系如下:


Dagger2框架以Component中定義的方法作為入口,到目標(biāo)類(lèi)中尋找JSR-330定義的@Inject標(biāo)注,生成一系列提供依賴的Factory類(lèi)和注入依賴的Injector類(lèi)。
而Component則是聯(lián)系Factory和Injector,最終完成依賴的注入。

三、@Module和@Provides

使用@Inject標(biāo)記構(gòu)造器提供依賴是有局限性的,比如說(shuō)我們需要注入的對(duì)象是第三方庫(kù)提供的,我們無(wú)法在第三方庫(kù)的構(gòu)造器上加上@Inject注解。
或者,我們使用依賴倒置的時(shí)候,因?yàn)樾枰⑷氲膶?duì)象是抽象的,@Inject也無(wú)法使用,因?yàn)槌橄蟮念?lèi)并不能實(shí)例化比如咱們需要ILoginPresenter的接口類(lèi)的依賴,會(huì)出現(xiàn)文中最開(kāi)始的那個(gè)錯(cuò)誤;

這個(gè)時(shí)候就需要Module了。
先清除LoginPresenter中的@Inject

Paste_Image.png

并把LoginAcitivity中的依賴改成接口類(lèi)型

Paste_Image.png

@Module標(biāo)記在LoginModule類(lèi)上面,@Provodes標(biāo)記在方法上,表示可以通過(guò)這個(gè)方法獲取依賴。

Paste_Image.png

在@Component中指定Module


Paste_Image.png

這樣就完了,ok了。測(cè)測(cè),沒(méi)有問(wèn)題;
@Module和@Provides的作用:
@Module需要和@Provide是需要一起使用的時(shí)候才具有作用的,并且@Component也需要指定了該Module的時(shí)候。

@Module是告訴Component,可以從這里獲取依賴對(duì)象。Component就會(huì)去找被@Provide標(biāo)注的方法,相當(dāng)于構(gòu)造器的@Inject,可以提供依賴。

還有一點(diǎn)要說(shuō)的是,@Component可以指定多個(gè)@Module的,如果需要提供多個(gè)依賴的話。

并且Component也可以依賴其它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ù)的依賴就不寫(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ō) ,它的作用只是保證依賴在@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è)胖子樣??!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容