Dagger2學習筆記4(@Singleton 與@ Scope 實現全局單例與作用域單例)

Dagger2學習筆記1(基礎概念學習)
Dagger2學習筆記2(學習Dagger2的簡單使用)
Dagger2學習筆記3(各個注解學習)

上篇文章中學習了Dagger2中各個注解的作用及如何使用, 其中涉及到兩個特殊的注解, @Singleton 和@ Scope, 接下來我們將學習怎么使用它們做到全局單例.
上篇文章我們學習到Singleton是繼承自Scope, 所以可以看做是Scope的代表實現. Scope的作用是保證依賴對象在作用范圍內單例, 提供局部范圍的單例,所謂局部范圍就是它的生命周期范圍
舉個栗子:

public class Dog {
    private String name;

    public Dog(String name){
        this.name = name;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                '}' + "hashcode = "+hashCode();
    }
}

@Module
public class MainModule {

    @Provides
    Dog provideDog(){
        return new Dog("bob");
    }

}
@Component(modules = MainModule.class)
public interface MainComponent {
    void inject(MainActivity mainActivity);
}
public class MainActivity extends AppCompatActivity {

    @Inject
    Dog dog1;

    @Inject
    Dog dog2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DaggerMainComponent.builder().mainModule(new MainModule()).build().inject(this);
        ((TextView)findViewById(R.id.tv1)).setText(dog1.toString());
        ((TextView)findViewById(R.id.tv2)).setText(dog2.toString());
    }
}

以上代碼中, 沒有使用@Singleton 修飾, 在MainActivity 中對 Dog進行兩次依賴注入, 查看輸出結果

image.png

可以看出得到的是兩個Dog對象, 也就是每依賴注入一次都會從新調用一次provideDog方法.
接下來我們使用@Singleton注解進行. 修改其中部分代碼(Module的依賴提供方法上以及Component的類名上)

@Module
public class MainModule {
    
    @Singleton
    @Provides
    Dog provideDog(){
        return new Dog("bob");
    }
}
@Singleton
@Component(modules = MainModule.class)
public interface MainComponent {
    void inject(MainActivity mainActivity);
}

運行結果如下:


image.png

我們可以看到兩個對象輸出的hash值相同, 已經實現了一部分的單例, but, 你以為這就實現了全局單例了么? 我們再寫一個新的頁面測試下!


public class SecondActivity extends AppCompatActivity {

    @Inject
    Dog dog1;
    @Inject
    Dog dog2;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        DaggerMainComponent.builder().mainModule(new MainModule()).build().inject(this);
        ((TextView)findViewById(R.id.tv1)).setText(dog1.toString());
        ((TextView)findViewById(R.id.tv2)).setText(dog2.toString());
    }
}
image.png

我們發現, 雖然provideDog使用@Singleton注解, 但是在不同的頁面,拿到的依賴對象仍然不是同一個, 這也就是我們之說的局部范圍的單例, 使用@Singeton注解或者定制的@Scope的, 只在同一個activity(或者fragment)的一個生命周期中保持單例. 而平時我們希望一些對象能夠在整個應用的生命周期中只存在一個, 也就是說實現全局意義上真正的單例該怎么做呢?
DaggerMainComponent在兩個activity中被各自被實例化一次, 導致產生了兩個不同的對象, 所以我們需要做到讓Component能夠實現單例, Android中, 我們知道在整個App生命周期中都只有一個Appclication實例,所以在Application中獲得一個唯一的component實例, 用它來提供我們需要的單例:
代碼如下:

  1. 自定義Application, 并且提供一個唯一的baseComponent類
public class BaseAppclication extends Application {

    private BaseComponent baseComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        baseComponent = DaggerBaseComponent.builder().baseModule(new BaseModule()).build();
    }

    public BaseComponent getBaseComponent(){
        return baseComponent;
    }
}
  1. 把dog類的依賴提供提取到BaseModule中
@Module
public class BaseModule {

    @Singleton
    @Provides
    Dog provideDog(){
        return new Dog("bob");
    }
}
  1. BaseComponent中不再需要寫inject方法, 因為這個component是用來讓別的component來依賴的, 只需要告訴別的component他可以提供哪些類型的依賴即可, 這個例子中 我們提供一個全局Dog的依賴
@Singleton
@Component(modules = BaseModule.class)
public interface BaseComponent {
    Dog provideDog();
}
  1. 在我們自己的Component, 使用dependencies依賴于baseComponent, (在@Component注解參數中, 可以依賴多個module和component, 根據自己的業務需求定義即可.)
@ActivityScope
@Component(dependencies = BaseComponent.class)
public interface MainComponent {
    void inject(MainActivity mainActivity);
    void inject(SecondActivity mainActivity);
}

這一步需要注意的是, 這個component也需要進行單例修飾, 但是當我們使用@Singleton注解時, 編譯會報一個Error:(14, 1) 錯誤: This @Singleton component cannot depend on scoped components, 如果依賴的component中也使用了@singleton時, 被依賴的地方就不能使用了,于是我自定義了一個Scope: ActivityScope

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}

好了, 現在準備工作都做好了, 在頁面中重新進行依賴注入工作,


public class MainActivity extends AppCompatActivity {

   @Inject
   Dog dog1;

   @Inject
   Dog dog2;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
       DaggerMainComponent.builder().baseComponent(((BaseAppclication)getApplication()).getBaseComponent()).build().inject(this);
       ((TextView)findViewById(R.id.tv1)).setText(dog1.toString());
       ((TextView)findViewById(R.id.tv2)).setText(dog2.toString());
   }

   public void click(View v){
       startActivity(new Intent(this, SecondActivity.class));
   }
}

這個Component在build的時候需要提供依賴的baseComponent, 此時我們使用已經在baseApplication中已經提供的唯一的baseModule, SecondActivity中代碼和上邊差不多, 不貼了. 運行查看結果:

image.png

哈哈, 我們發現已經成功的實現了單例!!

下篇文章中會寫到關于Lazy和Provider的使用.
Dagger2學習筆記5(關于Lazy,Provide的使用)
本文至此, End!~~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容