在Android中Dagger2主要目的是解耦,一般結(jié)合MVP達(dá)到完全解耦的效果。
(1)什么是Dagger2?
Dagger是為Android和Java平臺(tái)提供的一個(gè)完全靜態(tài)的,在編譯時(shí)進(jìn)行依賴(lài)注入的框架,原來(lái)是由Square公司維護(hù),現(xiàn)在由Google維護(hù)。
(2)存在的意義
我們?cè)赼ctivity中有可能會(huì)用到很多很多的類(lèi),這些類(lèi)要在activity中進(jìn)行實(shí)例化,這樣就導(dǎo)致我們的activity非常依賴(lài)這么多的類(lèi),這樣的程序耦合非常嚴(yán)重,不便于維護(hù)和擴(kuò)展,有什么辦法可以不去依賴(lài)這些類(lèi)呢,這時(shí)候就需要有一個(gè)容器,將這些類(lèi)放到這個(gè)容器里并實(shí)例化,我們activity在用到的時(shí)候去容器里面取就可以了,我們從依賴(lài)類(lèi)到依賴(lài)這個(gè)容器,實(shí)現(xiàn)了解耦,這就是我所理解的依賴(lài)注入,即所謂控制反轉(zhuǎn)。
(3)注解之@Inject
主要有兩個(gè)作用,一個(gè)是使用在構(gòu)造函數(shù)上,通過(guò)標(biāo)記構(gòu)造函數(shù)讓Dagger2來(lái)使用(Dagger2通過(guò)Inject標(biāo)記可以在需要這個(gè)類(lèi)實(shí) 例的時(shí)候來(lái)找到這個(gè)構(gòu)造函數(shù)并把相關(guān)實(shí)例new出來(lái))從而提供依賴(lài),另一個(gè)作用就是標(biāo)記在需要依賴(lài)的變量讓Dagger2為其提供依賴(lài)。
(4)注解之@Module
用Module標(biāo)注的類(lèi)是專(zhuān)門(mén)用來(lái)提供依賴(lài)的。有的人可能有些疑惑,看了上面的@Inject,需要在構(gòu)造函數(shù)上標(biāo)記才能提供依賴(lài),那么如果我們需要提供的類(lèi)構(gòu)造函數(shù)無(wú)法修改怎么辦,比如一些jar包里的類(lèi),我們無(wú)法修改源碼。這時(shí)候就需要使用Module了。Module可以給不能修改源碼的類(lèi)提供依賴(lài),當(dāng)然,能用Inject標(biāo)注的通過(guò)Module也可以提供依賴(lài)
(5)注解之@Provides
用Provide來(lái)標(biāo)注一個(gè)方法,該方法可以在需要提供依賴(lài)時(shí)被調(diào)用,從而把預(yù)先提供好的對(duì)象當(dāng)做依賴(lài)給標(biāo)注了@Inject的變量賦值。provide主要用于標(biāo)注Module里的方法
(6)注解之@Component
Component一般用來(lái)標(biāo)注接口,被標(biāo)注了Component的接口在編譯時(shí)會(huì)產(chǎn)生相應(yīng)的類(lèi)的實(shí)例來(lái)作為提供依賴(lài)方和需要依賴(lài)方之間的橋梁,把相關(guān)依賴(lài)注入到其中。
(7)添加依賴(lài)
implementation 'com.google.dagger:dagger:2.22.1'
annotationProcessor 'com.google.dagger:dagger-compiler:2.22.1'
(8)@Inject和@Component使用
我們先看一下未注入Dagger的代碼
public class MainActivity extends BaseActivity {
private DaggerTest daggerTest;
@SuppressLint("CheckResult")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
daggerTest = new DaggerTest();
findViewById(R.id.bt_1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
daggerTest.print();
}
});
}
}
public class DaggerTest {
/**
* 打印測(cè)試數(shù)據(jù)
*/
public void print(){
Log.d("aaa", "做一個(gè)小測(cè)試");
}
}
在MainActivity中創(chuàng)建一個(gè)DaggerTest對(duì)象,這樣的話MainActivity和DaggerTest就產(chǎn)生了依賴(lài)關(guān)系,這就產(chǎn)生了耦合。下面將使用Dagger來(lái)解耦:
第一步 在MainActivity中使用@Inject注解
public class MainActivity extends BaseActivity {
@Inject
DaggerTest daggerTest;
@SuppressLint("CheckResult")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.bt_1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
daggerTest.print();
}
});
}
}
第二步 在DaggerTest的構(gòu)造方法中使用@Inject注解(構(gòu)造方法不可以是private)
public class DaggerTest {
@Inject
public DaggerTest(){
}
/**
* 打印測(cè)試數(shù)據(jù)
*/
public void print(){
Log.d("aaa", "做一個(gè)小測(cè)試");
}
}
第三步 新建一個(gè)TestComponent接口,并用@Component修飾
Component是連接注入類(lèi)和目標(biāo)類(lèi)的橋梁。
@Component
public interface TestComponent {
void inject(MainActivity mainActivity);
}
Component會(huì)查找目標(biāo)類(lèi)中用Inject注解標(biāo)注的屬性,查找到相應(yīng)的屬性后會(huì)接著查找該屬性對(duì)應(yīng)的用Inject標(biāo)注的構(gòu)造函數(shù)
第四步 將目標(biāo)類(lèi)注入到Component中
我們?cè)贛ainActivity中的onCreate方法中添加以下代碼
DaggerTestComponent.create().inject(this);
這種方式的局限性:
- DaggerTest構(gòu)造方法不可以帶有參數(shù);
- DaggerTest不可以存在多個(gè)構(gòu)造方法;
(9)解決第(8)項(xiàng)構(gòu)造方法中不能帶參數(shù)的問(wèn)題
要想構(gòu)造方法中可以帶有參數(shù),需要@Module和@Provides的支持。
第一步 在MainActivity中使用@Inject注解
public class MainActivity extends BaseActivity {
@Inject
DaggerTest daggerTest;
@SuppressLint("CheckResult")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.bt_1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
daggerTest.print();
}
});
}
}
第二步 在DaggerTest的構(gòu)造方法中使用@Inject注解
public class DaggerTest {
@Inject
public DaggerTest(String s){
}
/**
* 打印測(cè)試數(shù)據(jù)
*/
public void print(){
Log.d("aaa", "做一個(gè)小測(cè)試");
}
}
第三步 新建Model
@Module
public class TestModel {
@Provides
public DaggerTest provideDaggerTest(){
return new DaggerTest("a");
}
}
Model類(lèi)使用@Module修飾,@Provides修飾任意方法并提供對(duì)象的實(shí)例。
第四步 新建一個(gè)TestComponent接口,用@Component修飾,并指定某Model
@Component(modules = TestModel.class)
public interface TestComponent {
void inject(MainActivity mainActivity);
}
第五步 將目標(biāo)類(lèi)注入到Component中
我們?cè)贛ainActivity中的onCreate方法中添加以下代碼
DaggerTestComponent.builder()
.testModel(new TestModel())
.build()
.inject(this);
當(dāng)然TestModel的構(gòu)造方法可以帶有參數(shù)
DaggerTestComponent.builder()
.testModel(new TestModel("我傳遞了一個(gè)數(shù)據(jù)"))
.build()
.inject(this);
這種方式的局限性:
- DaggerTest不可以存在多個(gè)構(gòu)造方法;
(10)注入類(lèi)擁有多個(gè)構(gòu)造方法的情況
使用@Named可以使構(gòu)造方法分類(lèi),我就不分步驟了,直接貼一下代碼:
public class MainActivity extends BaseActivity {
@Named("type1")
@Inject
DaggerTest daggerTest1;
@Named("type2")
@Inject
DaggerTest daggerTest2;
@Named("type3")
@Inject
DaggerTest daggerTest3;
@SuppressLint("CheckResult")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerTestComponent.builder()
.testModel(new TestModel("我傳遞了一個(gè)數(shù)據(jù)"))
.build()
.inject(this);
findViewById(R.id.bt_1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
daggerTest1.print();
daggerTest2.print();
daggerTest3.print();
}
});
}
}
public class DaggerTest {
private String printStr = "";
public DaggerTest(){
}
public DaggerTest(String s){
printStr = s;
}
public DaggerTest(String s, String ss){
printStr = s + ss;
}
/**
* 打印測(cè)試數(shù)據(jù)
*/
public void print(){
if(!TextUtils.isEmpty(printStr)){
Log.d("aaa", printStr);
}else{
Log.d("aaa", "做一個(gè)小測(cè)試");
}
}
}
@Module
public class TestModel {
private String test;
public TestModel(String s){
test = s;
}
@Named("type1")
@Provides
public DaggerTest provideDaggerTest1(){
return new DaggerTest();
}
@Named("type2")
@Provides
public DaggerTest provideDaggerTest2(){
return new DaggerTest(test);
}
@Named("type3")
@Provides
public DaggerTest provideDaggerTest3(){
return new DaggerTest(test, "1");
}
}
@Component(modules = TestModel.class)
public interface TestComponent {
void inject(MainActivity mainActivity);
}
(11)自定義注解替代@Named
@Qualifier注解可以解決注入類(lèi)擁有多個(gè)構(gòu)造方法時(shí)依賴(lài)沖突的問(wèn)題,下面我們自定義一個(gè)注解@CustomName。
@Qualifier
@Documented
@Target({ ElementType.FIELD, ElementType.METHOD })
@Retention(RUNTIME)
public @interface CustomName {
String value() default "";
}
我們只要將(10)中的@Named換成@CustomName即可。
(12)單例模式
使用@Singleton注解實(shí)現(xiàn)單例。
第一步 在Component中添加@Singleton
@Singleton
@Component(modules = TestModel.class)
public interface TestComponent {
void inject(MainActivity mainActivity);
}
第二步 在Model中的被@Provides修飾的方法上添加@Singleton
@Singleton
@Provides
public DaggerTest provideDaggerTest1(){
return new DaggerTest();
}
以上兩部可以實(shí)現(xiàn)局部單例。
當(dāng)我們從各種Activity中都使用了以下代碼
DaggerTestComponent.builder()
.testModel(new TestModel("我傳遞了一個(gè)數(shù)據(jù)"))
.build()
.inject(this);
那么DaggerTest只在各個(gè)Activity中是單例的,不同Activity中的DaggerTest的hashcode不同?,F(xiàn)在我們將局部單例改造成全局單例:
- 自定義Application
public class MyApp extends Application {
private static TestComponent testComponent;
@Override
public void onCreate() {
super.onCreate();
testComponent = DaggerTestComponent.builder()
.testModel(new TestModel("我傳遞了一個(gè)數(shù)據(jù)"))
.build();
}
public static TestComponent getTestComponent() {
return testComponent;
}
}
public static TestComponent getTestComponent() {
return testComponent;
}
-
在Activity引用Activity中的Component對(duì)象
MyApp.getTest().inject(this);(13)自定義Scope
自定義Scope比較簡(jiǎn)單,現(xiàn)在我們想定義一個(gè)@MyScope注解,代碼如下
@Scope
@Documented
@Retention(RUNTIME)
public @interface MyScope {
}
而@Singleton則是@Scope的默認(rèn)實(shí)現(xiàn),所以也可以寫(xiě)成
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton{
}
(14)延遲注入(Lazy)
有時(shí)我們想注入的依賴(lài)在使用時(shí)再完成初始化,加快加載速度,就可以使用注入Lazy<T>。只有在調(diào)用 Lazy<T> 的 get() 方法時(shí)才會(huì)初始化依賴(lài)實(shí)例注入依賴(lài)。
@Inject
Lazy<DaggerTest> daggerTest1;
daggerTest1.get().print(String.valueOf(daggerTest1.hashCode()));
(15)多個(gè)依賴(lài)的情況
假設(shè)有A,B兩個(gè)類(lèi),在A中新建一個(gè)B對(duì)象,那么A和B就產(chǎn)生了依賴(lài),我們稱(chēng)之為:A依賴(lài)于B。
結(jié)合上面講解到的知識(shí),我們很容易就會(huì)想到如何使用Dagger來(lái)解耦:
第一步 在A中使用@Inject修飾B的成員變量
@Inject
A a;
第二步 使用@Module和@Provides新建一個(gè)Model
@Module
public class BModel {
@Provides
public DaggerTest provideDaggerB(){
return new B();
}
}
第三步 使用@Component定義接口,這個(gè)接口作為A和B形成依賴(lài)關(guān)系的橋梁
@Component(modules = BModel.class)
public interface ABComponent {
void inject(A a);
}
第四步 在A中使用以下代碼,即可完成A和B的依賴(lài)關(guān)系。
ABComponent abComponent = DaggerABComponent.builder()
.testModel(new BModel())
.build()
.inject(this);
以上四個(gè)步驟比較簡(jiǎn)單,但是這時(shí)又來(lái)了一個(gè)C類(lèi),在C類(lèi)中新建一個(gè)B對(duì)象,那么C和B就產(chǎn)生了依賴(lài),我們稱(chēng)之為C依賴(lài)于B。
我們可以在@Component新增接口可以實(shí)現(xiàn)
@Component(modules = BModel.class)
public interface ABComponent {
void inject(A a);
void inject(C c);
}
(16)一個(gè)橋梁可以引用多個(gè)modules
@Component(modules = {TestModel.class, Test2Model.class})
public interface TestComponent {
void inject(MainActivity mainActivity);
}
(17)Component的依賴(lài)關(guān)系
Component就是橋梁,如果某橋梁想要使用另一個(gè)橋梁的方法時(shí),可通過(guò)依賴(lài)實(shí)現(xiàn)。
第一步 使用@Inject聲明成員變量
@Inject
DaggerTest2 daggerTest2;
第二步 新建Modules
@Module
public class Test2Model {
@Provides
public DaggerTest2 provideDaggerTest1(){
return new DaggerTest2();
}
}
第三步 橋梁
@Component(modules = {TestModel.class})
public interface TestComponent {
void inject(MainActivity mainActivity);
DaggerTest getDaggerTest();
}
@Component(modules = Test2Model.class, dependencies = TestComponent.class)
public interface Test2Component {
void test(DemoActivity mainActivity);
}
第四步 產(chǎn)生依賴(lài),并調(diào)用依賴(lài)橋梁的方法
DaggerTest daggerTest = DaggerTestComponent.create().getDaggerTest();
daggerTest.print("aaaa");
DaggerTest2Component.builder()
.testComponent(DaggerTestComponent.create())
.test2Model(new Test2Model())
.build()
.test(this);
(18)可參考的其它博客
Dagger 2 完全解析(一),Dagger 2 的基本使用與原理
Dagger 2 完全解析(二),進(jìn)階使用 Lazy、Qualifier、Scope 等
Dagger 2 完全解析(三),Component 的組織關(guān)系與 SubComponent
Dagger 2 完全解析(四),Android 中使用 Dagger 2
Dagger 2 完全解析(五),Kotlin 中使用 Dagger 2
Dagger 2 完全解析(六),dagger.android 擴(kuò)展庫(kù)的使用