Dagger2之 @Singleton

單例@Singleton

在開始寫文章之前,先來了解一些與后邊的內(nèi)容相關(guān)的注意事項(xiàng)(看文章前要有這些概念,剛開始不明白沒事,等看完后續(xù)的內(nèi)容再回來看就知道什么意思了):

  • 1、Component的inject方法接收父類型參數(shù),而調(diào)用時(shí)傳入的是子類型對(duì)象則無法注入,也就是說無法使用多態(tài)方式進(jìn)行注入。
  • 2、component關(guān)聯(lián)的modules中不能有重復(fù)的provide
  • 3、module的provide方法使用了scope,那么component就必須使用同一個(gè)注解
  • 4、module的provide方法沒有使用scope,那么component和module是否加注解都無關(guān)緊要,可以通過編譯
  • 5、component的dependencies與component自身的scope不能相同,即組件之間的scope不同
  • 6、Singleton的組件不能依賴其他的scope的組件,只能其他scope的組件依賴Singleton的組件。
  • 7、沒有scope的component不能依賴有scope的component
  • 8、一個(gè)component不能同時(shí)有多個(gè)scope(Subcomponent除外)
  • 9、@Singleton的生命周期依附于component,同一個(gè)module有provideXX()提供一個(gè)實(shí)例,且被@Singleton標(biāo)注,針對(duì)不同的component,創(chuàng)建的實(shí)例不同。

了解Singleton

先通過一個(gè)簡單的例子了解Singleton:
對(duì)于MainActivity中代碼:

public class MainActivity extends AppCompatActivity {

    @Inject
    OkHttpClient mOkHttpClient1;

    @Inject
    OkHttpClient mOkHttpClient2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        DaggerUserComponet.create().inject(this);
        Log.e("itydl","MainActivity--->"+mOkHttpClient1);
        Log.e("itydl","MainActivity--->"+mOkHttpClient2);
    }
}

應(yīng)該有個(gè)Module類提供OkHttpClient 實(shí)例:
創(chuàng)建UserModel:

@Module
public class UserModule {

    @Singleton
    @Provides
    public OkHttpClient provideSingletonOkhttpClient(){
        return new OkHttpClient().newBuilder().build();
    }
}

這里對(duì)provideXXX()方法加入了@Singleton注解,然后點(diǎn)進(jìn)去這個(gè)類:

@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}

它是定義的Scope(待會(huì)講)。對(duì)應(yīng)的Componet也必須定義為Scope:

@Singleton
@Component(modules = {UserModule.class})
public interface UserComponet {
    void inject(MainActivity mainActivity);
}

這樣客戶端需要依賴,Module通過UserComponet 像其提供了依賴。運(yùn)行程序:

okhttp3.OkHttpClient@3f9d7b7
okhttp3.OkHttpClient@3f9d7b7

發(fā)現(xiàn)兩個(gè)實(shí)例是一樣的。這樣就對(duì)單例Singleton有了一個(gè)了解。但是它里面還存在很多很多的坑,繼續(xù)往下一點(diǎn)點(diǎn)找出來吧。

挖掘Singleton存在的坑

修改所有的代碼如下:

MainActivity:
public class MainActivity extends AppCompatActivity {

    @Inject
    UserManager mManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        DaggerUserComponet.create().inject(this);

        mManager.register();

    }
}

它里面需要一個(gè)UserManager 依賴,然后調(diào)用注冊(cè)方法。
對(duì)于UserManager類:

public class UserManager {

    private ApiServer mApiServer;
    
    @Inject
    public UserManager(ApiServer mApiServer){
        this.mApiServer = mApiServer;
    }

    public void register(){
        mApiServer.register();
    }
}

在這個(gè)類里面提供了注冊(cè)方法,真正的注冊(cè)是通過調(diào)用ApiServer 的register();他需要ApiServer 的實(shí)例,通過構(gòu)造方法注入這個(gè)實(shí)例。
看一下ApiServer 類:

public class ApiServer {

    OkHttpClient mOkHttpClient;

    @Inject
    public ApiServer(OkHttpClient okHttpClient){
        this.mOkHttpClient = okHttpClient;
    }

    /**
     * 往服務(wù)端保存用戶信息
     */
    public void register() {
        Log.e("Howard","ApiServer--->register()"+mOkHttpClient);
    }

}

在ApiServer 的egister()方法中,肯定需要OkHttpClient 實(shí)例進(jìn)行訪問網(wǎng)絡(luò)的功能,這個(gè)實(shí)例又通過ApiServer的構(gòu)造方法進(jìn)行了注入。
然后Module提供依賴:

@Module
public class UserModule {

    @Singleton
    @Provides
    public OkHttpClient provideSingletonOkhttpClient(){
        return new OkHttpClient().newBuilder().build();
    }

    @Provides
    public ApiServer provideApiServer(OkHttpClient okHttpClient){
        ApiServer apiServer = new ApiServer(okHttpClient);
        return apiServer;
    }

     @Provides
     public UserManager provideUserManager(ApiServer mApiServer){
         return new UserManager(mApiServer);
     }

}

這個(gè)類專門提供依賴的,provideUserManager方法提供MainActivity類需要的UserManager實(shí)例,然后這個(gè)構(gòu)造方法中需要注入ApiServer 實(shí)例,直接在這個(gè)類里面提供了provideApiServer方法,然后返回出去。而這個(gè)方法創(chuàng)建ApiServer實(shí)例的時(shí)候又需要一個(gè)OkHttpClient 實(shí)例,同樣在這里提供了provideSingletonOkhttpClient方法用于創(chuàng)建OkHttpClient 的。然后看一下Component里面的代碼:

@Singleton
@Component(modules = {UserModule.class})
public interface UserComponet {
    void inject(MainActivity mainActivity);
}

然后運(yùn)行程序:

ApiServer--->register()okhttp3.OkHttpClient@3f9d7b7

打印了okhttp3的實(shí)例。

然后,假設(shè)現(xiàn)在有這樣的需求:在MainActivity中有注冊(cè)的功能,如果還有登錄的功能,且希望使用的OkhttpClient是同一個(gè)實(shí)例,該怎么去處理?
還有登錄的功能:創(chuàng)建LoginActivity,在它這里也要用到用戶管理類,調(diào)用用戶管理的登錄方法:

public class LoginActivity extends AppCompatActivity {
    
    @Inject
    UserManager mManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        
        DaggerUserComponet.create().inject(this);
        
        mManager.login();
        
    }
}

這個(gè)時(shí)候編譯階段就報(bào)錯(cuò)。DaggerUserComponet.create().inject(this);存在問題。

@Singleton
@Component(modules = {UserModule.class})
public interface UserComponet {
    void inject(MainActivity mainActivity);
}

因?yàn)関oid inject(MainActivity mainActivity);它里面接收的是MainActivity類型,顯然注入LoginActivity 類型是不允許的。
我們可能天真的認(rèn)為,使用多態(tài)接收就可以了:

@Singleton
@Component(modules = {UserModule.class})
public interface UserComponet {
    void inject(Activity mainActivity);
}

運(yùn)行發(fā)現(xiàn)報(bào)空指針,是因?yàn)闆]有完成注入。這里應(yīng)征了開頭的第一條Component的inject方法接收父類型參數(shù),而調(diào)用時(shí)傳入的是子類型對(duì)象則無法注入,也就是說無法使用多態(tài)方式進(jìn)行注入。
那么需要再寫一個(gè)與Login相關(guān)的連接器:

@Singleton
@Component(modules = UserModule.class)
public interface LoginCompoent {
    void inject(LoginActivity loginActivity);
}

LoginCompoent 也需要加入@Singleton進(jìn)行注解,因?yàn)樗P(guān)聯(lián)的UserModule里面provideSingletonOkhttpClient是通過單例@Singleton注解的。這里關(guān)聯(lián)UserModule的原因是,它里面已經(jīng)提供了創(chuàng)建UserManager和創(chuàng)建OkHttpClient的方法。
然后看一下UserManager類,里面多了一個(gè)login();方法:

    public void login() {
        mApiServer.login();
    }

調(diào)用ApiServer類的login();方法:

    public void login(){
        Log.e("Howard","ApiServer--->login()"+mOkHttpClient);
    }

也是打印了OkHttpClient實(shí)例。
然后在LoginActivity中進(jìn)行注入:
DaggerLoginCompoent.create().inject(this);
打印結(jié)果:

ApiServer--->register()okhttp3.OkHttpClient@2c4dbeb6
ApiServer--->login()okhttp3.OkHttpClient@5634554

跟我們想象的差別很大啊,我們明明對(duì)UserModel的provideSingletonOkhttpClient()方法加入了@Singleton注解為單例,為何這里是兩個(gè)實(shí)例呀?
解釋:Dagger2里面的單例不同意java中的單例,這是兩個(gè)不同的概念。在Dagger2里面的單例依附于Compoent,不同的Compoent就算關(guān)聯(lián)了相同的Module且里面提供了共同所需要的實(shí)例,也是創(chuàng)建了不同的實(shí)例對(duì)象(地址不同)。

在一個(gè)項(xiàng)目中,我們希望提供的OkHttp是一個(gè)實(shí)例,而不是像上面那樣,有多個(gè)Compoent就創(chuàng)建多個(gè)OkHttp實(shí)例,該怎么辦呢?那就是下一篇文章的事了,自定義Scope。

最后編輯于
?著作權(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),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • Dagger2 入門 2016-12-21 更新:添加@Subcomponent注解以及Lazy與Provider...
    fxzou閱讀 28,772評(píng)論 77 331
  • 幾個(gè)問題 看到Dagger2這個(gè)詞的時(shí)候,相信很多人會(huì)有很多的疑問如下:Dagger2如何使用在例子中?Injec...
    codeHoward閱讀 7,253評(píng)論 4 10
  • 部分內(nèi)容參考自:[Android]使用Dagger 2依賴注入 - DI介紹(翻譯)[Android]使用Dagg...
    AItsuki閱讀 48,145評(píng)論 66 356
  • 本文的分析基于dagger2的2.7版本。 谷歌開發(fā)維護(hù)的Dagger2出來有很長時(shí)間了,目前在很多開源項(xiàng)目上也能...
    sososeen09閱讀 13,767評(píng)論 31 108
  • 文章翻譯自Dagger官網(wǎng),翻譯水平有限,見諒。 引入 引用官網(wǎng)的引入說明,上面的部分都好理解,就是簡單的comp...
    one_cup閱讀 693評(píng)論 0 0

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