Android快速依賴注入框架Dagger2使用2

接着上一篇: 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到的對象可能是同一個,可能是不同。

發佈了106 篇原創文章 · 獲贊 380 · 訪問量 75萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章