單例@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。