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進行兩次依賴注入, 查看輸出結果
可以看出得到的是兩個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);
}
運行結果如下:
我們可以看到兩個對象輸出的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());
}
}
我們發現, 雖然provideDog使用@Singleton注解, 但是在不同的頁面,拿到的依賴對象仍然不是同一個, 這也就是我們之說的局部范圍的單例, 使用@Singeton注解或者定制的@Scope的, 只在同一個activity(或者fragment)的一個生命周期中保持單例. 而平時我們希望一些對象能夠在整個應用的生命周期中只存在一個, 也就是說實現全局意義上真正的單例該怎么做呢?
DaggerMainComponent在兩個activity中被各自被實例化一次, 導致產生了兩個不同的對象, 所以我們需要做到讓Component能夠實現單例, Android中, 我們知道在整個App生命周期中都只有一個Appclication實例,所以在Application中獲得一個唯一的component實例, 用它來提供我們需要的單例:
代碼如下:
- 自定義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;
}
}
- 把dog類的依賴提供提取到BaseModule中
@Module
public class BaseModule {
@Singleton
@Provides
Dog provideDog(){
return new Dog("bob");
}
}
- BaseComponent中不再需要寫inject方法, 因為這個component是用來讓別的component來依賴的, 只需要告訴別的component他可以提供哪些類型的依賴即可, 這個例子中 我們提供一個全局Dog的依賴
@Singleton
@Component(modules = BaseModule.class)
public interface BaseComponent {
Dog provideDog();
}
- 在我們自己的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中代碼和上邊差不多, 不貼了. 運行查看結果:
哈哈, 我們發現已經成功的實現了單例!!
下篇文章中會寫到關于Lazy和Provider的使用.
Dagger2學習筆記5(關于Lazy,Provide的使用)
本文至此, End!~~