接着上一篇: http://blog.csdn.net/niubitianping/article/details/60878104
一、單例@Singleton
需要實例的類如果是單例的,需要在Component接口和Module類的方法使用@Singleton。
栗子: 新建一個SingleClass.java
public class SingleClass {
//內容可以爲空,僅做測試
}
1.1 Component接口添加@Singleton
在Component接口添加@Singleton
註解
@Singleton
@Component(modules = LoginModule.class)
public interface LoginComponent {
void inject(TestActivity activity);
}
1.2 Module類方法添加@Singleton
在Module類裏面實例SingleClass的方法添加@Singleton
註解
@Module
public class LoginModule {
private Context mContext; //供給LoginStore使用
public LoginModule(Context mContext) {
this.mContext = mContext;
}
@Provides
@Singleton //這裏添加單例註解
SingleClass provideSingleClass(){
return new SingleClass();
}
...(之前的代碼縮略)
}
測試使用,在TestActivity中@Inject
兩個Singleclass
public class TestActivity extends Activity {
@Inject
SingleClass singleClassOne;
@Inject
SingleClass singleClassTwo;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//DaggerLoginComponent.create().inject(this);
//當Module需要構造方法傳參的時候,使用builder的方式初始化Dagger。
DaggerLoginComponent.builder()
.loginModule(new LoginModule(this)) //loginModule這個方法是構建之後纔有的
.build()
.inject(this);
Log.e("@@", "singleClassOne: "+singleClassOne);
Log.e("@@", "singleClassTwo: "+singleClassTwo);
}
}
這時候可以看到輸出的兩個類的內存地址似乎一樣的,證明這是同一個類,即單例類:
03-05 20:53:28.268 30247-30247/com.tpnet.dagger2test E/@@: singleClassOne: com.tpnet.dagger2test.five.SingleClass@c612337
03-05 20:53:28.268 30247-30247/com.tpnet.dagger2test E/@@: singleClassTwo: com.tpnet.dagger2test.five.SingleClass@c612337
ps注意: @Singleton 依賴於Component,如果兩個不同的Component使用同一個Module來是喲個@Singleton創建單例,將會不是單例。
二、作用域@Scope
@Singleton
可以看到他的源碼爲:
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}
這裏看到有一個@Scope
,這個是用來給依賴劃定作用域。
2.1 問題發掘
在單例的栗子中,我新建一個TwoActivity和GetInfoComponent,
GetInfoComponent.java
@Singleton
@Component(modules = LoginModule.class)
public interface GetInfoComponent {
void inject(TwoActivity activity);
}
TwoActivity.java
public class TwoActivity extends Activity {
@Inject
LoginCtrl loginCtrl;
@Inject
SingleClass singleClass;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerGetInfoComponent.builder()
.loginModule(new LoginModule(this))
.build()
.inject(this);
Log.e("@@", "TwoActivity: "+singleClass);
}
}
把TestActivity修改爲:
可以看到怎麼SingleClass這個地址怎麼不對呢,不是單例嗎?
03-06 14:55:29.556 7020-7020/com.tpnet.dagger2test E/@@: provideLoginCtrlOne被調用:
03-06 14:55:29.556 7020-7020/com.tpnet.dagger2test E/@@: TestActivity: com.tpnet.dagger2test.six.SingleClass@421bd648
03-06 14:55:29.596 7020-7020/com.tpnet.dagger2test E/@@: provideLoginCtrlOne被調用:
03-06 14:55:29.596 7020-7020/com.tpnet.dagger2test E/@@: TwoActivity: com.tpnet.dagger2test.six.SingleClass@421c4558
繼續看下去
2.2 Scope分類
我們的應用有三個範圍:
- @Singleton: Singleton是Dagger已經定義好的,Application級別的Scope,只要application存活依賴就存活。
- @UserScope: 只要用戶會話是激活狀態依賴就存活(在單個應用程序中啓動)。重要的是:這個scope存活時間不會超過application本身。每一個新的app實例都會創建一個新的@UserScope(甚至app不同的啓動間用戶會話沒有關閉)。
- @ActivityScope: Activity範圍,依賴於Activity,只要Activity界面存活依賴就存活。例如Activity銷燬了,inject的component裏面的module裏面實例的對象都沒了。
2.3 自定義Scope
利用Application級別的單例來實現單例,接下來解決上面的問題:
1. 新建AppModule.java
在這裏提供SingleClass類。所以LoginModule裏面提供Singleclass的方法需要去掉了。
@Module
public class AppModule {
@Singleton
@Provides
SingleClass providerSingleClass(){
return new SingleClass();
}
}
2. 新建AppComponent.java
@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
//把AppModule裏面的SingleClass橋接出來
SingleClass singleClass();
}
3. 自定義Scope
創建一個ActivityScope.java,作爲自定義的Scope
,用來標識LoginComponent和GetInfoComponent。
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}
4. 添加Component依賴
在LoginComponent和GetInfoComponent添加AppComponent的依賴,並且去掉@Singleton
,添加自定義的ActivityScope
註解
LoginComponent.java
@ActivityScope
@Component(modules = LoginModule.class,dependencies = AppComponent.class)
public interface LoginComponent {
void inject(TestActivity activity);
}
GetInfoComponent.java
@ActivityScope
@Component(modules = LoginModule.class,dependencies = AppComponent.class)
public interface GetInfoComponent {
void inject(TwoActivity activity);
}
5. 創建BaseApplication
完成上面的步驟之後,make 一下module,使得dagger生成DaggerAppComponent。
然後新建一個BaseApplication,用來注入AppComponent:
public class BaseApplication extends Application {
AppComponent mAppComponent;
@Override
public void onCreate() {
super.onCreate();
mAppComponent = DaggerAppComponent.create();
}
public AppComponent getAppComponent(){
return mAppComponent;
}
}
然後在manifest添加BaseApplication。
6. 在Activity注入
TestActivity的注入改爲;
DaggerLoginComponent.builder()
.loginModule(new LoginModule(this)) //loginModule這個方法是構建之後纔有的
//添加appComponent,參數從BaseApplication獲取
.appComponent((((BaseApplication)getApplication()).getAppComponent()))
.build()
.inject(this);
TwoActivity的注入改爲:
DaggerGetInfoComponent.builder()
.loginModule(new LoginModule(this))
//添加appComponent,參數從BaseApplication獲取
.appComponent(((BaseApplication)getApplication()).getAppComponent())
.build()
.inject(this);
最後運行看到結果,兩個Activity裏面實例的SingleClass的類相同:
03-06 16:10:29.937 21284-21284/com.tpnet.dagger2test E/@@: provideLoginCtrlOne被調用:
03-06 16:10:29.937 21284-21284/com.tpnet.dagger2test E/@@: TestActivity: com.tpnet.dagger2test.six.SingleClass@421c4530
03-06 16:10:31.438 21284-21284/com.tpnet.dagger2test E/@@: provideLoginCtrlOne被調用:
03-06 16:10:31.438 21284-21284/com.tpnet.dagger2test E/@@: TwoActivity: com.tpnet.dagger2test.six.SingleClass@421c4530
自定義Scope可以看看外國大牛的文章:
http://frogermcs.github.io/dependency-injection-with-dagger-2-custom-scopes/
三、@SubComponent
顧名思義,子@Component。類似@Component的dependencies。 @SubComponent可以在@Component裏面進行返回獲取。
dependencies不會繼承範圍,@Subcomponent
會。@Subcomponent
同時具備兩種不同作用域的scope。
來個栗子:
3.1 創建子SubComponent
創建SingleClassTwo:
public class SingleClassTwo {
//製作演示,可以爲空方法
}
創建自定義Scope, UserScope.java:
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface UserScope {
}
創建ChildModule.java
@Module
public class ChildModule {
@UserScope
@Provides
SingleClassTwo provideSingleClass(SingleClassOne singleClass){
Log.e("@@", "provideSingleClass: singleClass是否爲空:"+ (singleClass == null) );
return new SingleClassTwo();
}
}
創建ChildComponent.java:
@UserScope //自定義Scope修飾
@Subcomponent(modules = ChildModule.class)
public interface ChildComponent {
//這裏的ThreeActivity在下面創建
void inject(ThreeActivity activity);
}
ps注意: 如果父的Component使用了@Singleton, 子@Subcomponent 不能使用@Singleton。 就是SubComponent的範圍 < 父Component的範圍,需要自定義Scope,這裏定義了一個UserScope。
3.2 創建父Component
創建SingleClassOne:
public class SingleClassOne {
//製作演示,可以爲空方法
}
創建FatherModule.java
@Module
public class FatherModule {
@Singleton
@Provides
SingleClassOne providerSingleClass(){
return new SingleClassOne();
}
}
創建FatherComponent.java:
@Singleton
@Component(modules = FatherModule.class)
public interface FatherComponent {
//父Component可以獲取SubComponent
ChildComponent getChildComponent();
}
3.3 使用
上面的代碼創建完畢之後,make一下module,然後創建一個ThreeActivity.java
public class ThreeActivity extends Activity{
@Inject
SingleClassTwo singleClassTwo;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerOneComponent.create();
}
}
運行ThreeActivity之後,沒看到輸出 singleClass是否爲空 ,點解?
因爲還沒有注入ChildComponent,把ThreeActivity的onCreate改爲下面的代碼:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerOneComponent.create()
.getChildComponent()
.inject(this);
}
即可看到輸出:
03-06 18:37:55.216 3414-3414/? E/@@: provideSingleClass: singleClass是否爲空:false
四、懶加載Lazy和Provider
在上面的栗子中,在ThreeActivity.java的onCreate添加一句代碼:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerFatherComponent.create()
.getChildComponent()
.inject(this);
Log.e("@@", "還沒有進行創建singleClassTwo");
}
可以看到下面的輸出,證明了在inject的時候已經調用了ChildModule的provideSingleClass方法,簡單說就是已經實例化了:
03-06 18:48:26.222 14898-14898/? E/@@: provideSingleClass: singleClass是否爲空:false
03-06 18:48:26.222 14898-14898/? E/@@: 還沒有進行創建singleClassTwo
4.1 Lazy
用Lazy標識的變量,在inject的時候並沒有實例化,而是在t.get()的時候才實例化並返回。來個栗子:
創建Test.java
public class Test {
}
創建TestModule.java
@Module
public class TestModule {
@Provides Test provideTest(){
Log.e("@@", "調用了provideTest方法");
return new Test();
}
}
創建TestComponent.java
@Component(modules = TestModule.class)
public interface TestComponent {
void inject(TestActivity activity);
}
make moudle,然後創建TestActivity.java
public class TestActivity extends Activity{
@Inject Test test;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerTestComponent.builder().build().inject(this);
Log.e("@@", "已經注入完成");
}
}
這時候運行可以看到輸出:
03-06 19:09:46.301 5187-5187/com.tpnet.dagger2test E/@@: 調用了provideTest方法
03-06 19:09:46.301 5187-5187/com.tpnet.dagger2test E/@@: 已經注入完成
上面的輸出這是正常的,現在來修改一下TestActivtiy:
public class TestActivity extends Activity{
// @Inject Test test;
@Inject Lazy<Test> test;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerTestComponent.builder().build().inject(this);
Log.e("@@", "已經注入完成");
Log.e("@@", "開始第一次get: " + test.get().toString());
Log.e("@@", "開始第二次get: " + test.get().toString());
}
}
可以看到輸出爲下面的內容,表示inject的時候還沒有進行TestModule裏面的provideTest方法,是get的時候才實例化了,而且多次get得到的是同一個Test對象。
03-06 19:14:27.425 9846-9846/com.tpnet.dagger2test E/@@: 已經注入完成
03-06 19:14:27.425 9846-9846/com.tpnet.dagger2test E/@@: 調用了provideTest方法
03-06 19:14:27.425 9846-9846/com.tpnet.dagger2test E/@@: 開始第一次get: com.tpnet.dagger2test.eight.Test@421ae290
03-06 19:14:27.425 9846-9846/com.tpnet.dagger2test E/@@: 開始第二次get: com.tpnet.dagger2test.eight.Test@421ae290
4.2 Provider
使用這個類修飾的變量,也是懶加載,但是它每次get都會重新執行一遍provides實例化的方法。
修改一下上面的TestActivity:
public class TestActivity extends Activity{
// @Inject Test test;
//@Inject Lazy<Test> test;
@Inject Provider<Test> test;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerTestComponent.builder().build().inject(this);
Log.e("@@", "已經注入完成");
Log.e("@@", "開始第一次get: " + test.get().toString());
Log.e("@@", "開始第二次get: " + test.get().toString());
}
}
可以看到輸出爲以下,表明了懶加載,每次get都會重新調用provides方法,產生新的對象:
03-06 19:18:09.852 13486-13486/com.tpnet.dagger2test E/@@: 已經注入完成
03-06 19:18:09.852 13486-13486/com.tpnet.dagger2test E/@@: 調用了provideTest方法
03-06 19:18:09.852 13486-13486/com.tpnet.dagger2test E/@@: 開始第一次get: com.tpnet.dagger2test.eight.Test@421af130
03-06 19:18:09.852 13486-13486/com.tpnet.dagger2test E/@@: 調用了provideTest方法
03-06 19:18:09.852 13486-13486/com.tpnet.dagger2test E/@@: 開始第二次get: com.tpnet.dagger2test.eight.Test@421af520
4.3 懶加載注意
Provide不是規定死了每次get都是新的對象。 例如單例,每次get還是同一個對象。在一的栗子的ThreeActivity進行修改:
public class ThreeActivity extends Activity{
//@Inject SingleClassTwo singleClassTwo;
@Inject Provider<SingleClassTwo> singleClassTwo;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DaggerFatherComponent.create()
.getChildComponent()
.inject(this);
Log.e("@@", "還沒有進行創建singleClassTwo");
Log.e("@@", "get地址1: "+singleClassTwo.get().toString());
Log.e("@@", "get地址2: "+singleClassTwo.get().toString());
}
}
可以看到輸出內容爲以下,兩次get的對象還是同一個:
03-06 19:22:27.944 17698-17698/? E/@@: 還沒有進行創建singleClassTwo
03-06 19:22:27.944 17698-17698/? E/@@: provideSingleClass: singleClass是否爲空:false
03-06 19:22:27.944 17698-17698/? E/@@: get地址1: com.tpnet.dagger2test.seven.SingleClassTwo@421b0860
03-06 19:22:27.944 17698-17698/? E/@@: get地址2: com.tpnet.dagger2test.seven.SingleClassTwo@421b0860
五、總結Dagger2注意點
@Component接口的inject方法接收的參數和傳遞的參數類型必須一致,不能定義爲MainActivty,然後傳遞Activity,會導致注入失敗,對象爲空
@Component
接口關聯的modules中不能有重複的provide類型@Module
類的provide方法使用了Scope,那麼對應的@Component
接口也必須使用同一個註解。@Component
的dependencies與@Component
自身的Scope不能相同。需要自定義Scope@Singleton
的組件不能依賴其他Scope組件,只能其他的Scope組件依賴Singleton組件。沒有Scope的
@Component
接口不能依賴有Scope的@Component
接口一個
@Component
接口不能同時有多個@Scope
(Subcomponent除外)@Singleton
的生命週期依賴於@Component
,@Component
的注入類銷燬了,@Singleton
實例化的類也會沒了。@SubComponent
的Scope範圍不能大於他的父Component。懶加載Provider根據
@Provides
方法實現的不同,get到的對象可能是同一個,可能是不同。