(這篇博客真是磨難重重啊,寫到一半電腦藍屏了,還好markdown編輯器保持了部分類容)
最近開始重構項目,在重構項目中用到了依賴注入框架dagger2,發現它確實很方便,能大大加快我們編寫代碼的速度,同時也很方便我們對於功能模塊的解耦。在這裏就不過多介紹dagger2了,大家谷歌 百度一下就能得到很多關於dagger2的介紹。學習dagger2是需要一定的學習成本的,我自己開始學習的時候也差不多花了一週的時間才弄明白怎樣使用dagger2,下面就說說自己對dagger2的理解和使用方法。
dagger2中最重要的就是module和component了,module是生產我們需要的對象的地方,component是聯繫module和module生產出的對象的使用場景的橋樑。(說白了,dagger2就像是設計模式中的工廠模式,爲我們生產各種我們需要的對象)
不明白?上代碼:(假如我們有一個類A,然後我們通過依賴注入方法要在MainActivity中使用)
不使用dagger2
類A:
public class A { private int field; ... //其他屬性 public A() { } public void doSomething(){ Log.i("美女","我愛你"); } public void doOtherthing(){ Log.i("美女","我是真的愛你"); } ... //其他方法 }
類MainActivity
public class MainActivity extends AppCompatActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); A a=new A(); a.doSomething(); a.doOtherthing(); } }
使用dagger2
類A:
public class A { private int field; ... //其他屬性 public A() { } public void doSomething(){ Log.i("美女","我愛你"); } public void doOtherthing(){ Log.i("美女","我是真的愛你"); } ... //其他方法 }
我們先要構造一個Module,這個Module中生產A的對象
@Module public class AMedule { @Provides A provideA(){ return new A(); } }
然後需要一個Component,用了連接Module和MainActivity
@Component(modules = AMedule.class) public interface ActivityComponent { A a(); }
在MainActivity類中使用
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ActivityComponent component=DaggerActivityComponent.builder() .aMedule(new AMedule()) .build(); A a=component.a(); a.doSomething(); a.doOtherthing(); } }
一個簡單的依賴注入就完成了。
也許大家會覺得這樣寫很麻煩,其實不然,還有一種更加簡單的方法來做這件事,我們把MainActivity注入到ActivityComponent中,直接使用@Inject註解:
使用@Inject註解
類A :
public class A { private int field; ... //其他屬性 public A() { } public void doSomething(){ Log.i("美女","我愛你"); } public void doOtherthing(){ Log.i("美女","我是真的愛你"); } ... //其他方法 }
我們先要構造一個Module
@Module public class AMedule { @Provides A provideA(){ return new A(); } }
然後需要一個Component
@Component(modules = AMedule.class) public interface ActivityComponent { void inject(MainActivity mainActivity); }
在MainActivity類中使用
public class MainActivity extends AppCompatActivity { @Inject A a; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerActivityComponent.builder() .aMedule(new AMedule()) .build() .inject(this); a.doSomething(); a.doOtherthing(); } }
怎麼樣,是不是簡單,方便一些了。
什麼?還麻煩,那我們更進一步吧(由於這裏的A類是我們自己定義的,可以更進一步):
更進一步的@Inject註解
類A :
public class A { private int field; ... //其他屬性 @Inject public A() { } public void doSomething(){ Log.i("美女","我愛你"); } public void doOtherthing(){ Log.i("美女","我是真的愛你"); } ... //其他方法 }
我們先要構造一個Module(其實這裏Module已經用不到了,我們不需要通過Modu生產A了,不過爲了與上面對比,還是留着吧)
@Module public class AMedule { // @Provides // A provideA(){ // return new A(); // } }
然後需要一個Component
@Component(modules = AMedule.class) public interface ActivityComponent { void inject(MainActivity mainActivity); }
在MainActivity類中使用
public class MainActivity extends AppCompatActivity { @Inject A a; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerActivityComponent.builder() .aMedule(new AMedule()) .build() .inject(this); a.doSomething(); a.doOtherthing(); } }
是不是看迷糊了,A沒有在Module生產,那是怎麼來的呢?不知道大家注意到沒有,A的構造方法上面有個@Inject註解,沒錯,就是他,就是這個註解生產A對象的。
看到這裏,我們就知道dagger2生產對象有兩種方式,一種是在Module中通過@Provides註解加上providexxx 方法,另一種就是直接在要生產的對象的類的構造方法加上@inject註解。
那麼什麼時候用Module,什麼時候用@Inject呢?
在這裏悄悄告訴大家: 一般生成系統或者SDK或者第三方庫提供的類的對象時用Module,而自定義的類的對象用@Inject,當然,你也可以用一個類繼承系統或者SDK或者第三方庫提供的類,然後在其構造方法加上@Inject註解。
2.可能大家對Module和Component的還是似懂非懂,在這裏在打一個形象一點的比喻吧。
例如到吃飯的時間了,我們要去做飯(吃外賣的繞道),我們得去廚房吧(希望不是野外燒烤),我們需要煤氣竈(也可能是電磁爐),鍋,刀,砧板,盆子….(發現需要的真多哈),這些都是一個個類吧,你廚房裏的那些具體的比如美的電飯煲(美的是不是得給點廣告費),蘇泊爾不粘鍋(剛買了一個)…..就是一個個具體的對象,我們有了這些具體的對象才能愉快的做飯,而Module就是讓我們得到這一個個對象(一個大工廠,生產美的電飯煲,蘇泊爾不粘鍋….),Component 就是我們的廚房,DaggerActivityComponent .builder().aMedule(new AMedule()).build() 就是讓我們擁有了一個廚房(想想機器貓的口袋,我們可以從裏面得到任何東西 ),我們可以從廚房裏面拿炊具做飯(往外拿:A a=component.a(),這是把廚房裏的東西拿到廚房外面,在外面做飯),而 DaggerActivityComponent.builder() .aMedule(new AMedule()) .build().inject(this)就是我們自己進入廚房,在廚房裏面 做飯(我們當然很容易獲得廚房裏面的炊具了,用@Inject獲得),好了,開始做飯…
3.一個Component可以依賴多個Module,如
@Component(modules = {AModule.class,BModule.class,...})
同樣,Component也可以依賴另一個Component,如
@Component(dependencies = BComponent.class,modules = {AModule.class,BModule.class,...})
甚至多個Component,如
@Component(dependencies = {BComponent.class,CComponent.class,...} ,modules = {AModule.class,BModule.class,...})
有個需要注意的地方是被依賴的Component需要將對象暴露出來,依賴的Component才能夠獲取到這個對象,如
被依賴Component
@Component(modules = {...}) public interface BComponent { A a(); B b(); }
依賴Component
@Component(dependencies = BComponent.class,modules = {...}) public interface CComponent { }
CComponent可以通過BComponent獲取A,B的對象,其他BComponent的對象對CComponent來說是透明的,不可見。
4.@Scope註解
這個註解是用來劃分作用域的(在哪兒使用),意思就是說我劃出一塊區域來,生產出的東西如果限定到此作用域都放到這裏,作用域的場景類(一般就是消費對象的地方,比如Activity)下次需要某個對象的時候,我就來這兒拿,如果有,就直接拿來用,如果沒有,就生產,並且放到這裏,同時自己用(有沒有一種方便自己,方便他人的感覺),dagger2自已有個@Scope註解,叫@Singleton,就是大家都熟悉的單例模式,你也可以自定義@Scope註解,比如限定生產出的某個對象只能在Activity中使用,只能在Application中使用,只能在Fragment中使用等等。。。
例如:我們寫一個限定作用域爲Activity的Scope註解
@Scope @Documented @Retention(RetentionPolicy.RUNTIME) public @interface PerActivity { }
使用(構造函數方式)
類A :
@PerActivity public class A { private int field; ... //其他屬性 @Inject public A() { } public void doSomething(){ Log.i("美女","我愛你"); } public void doOtherthing(){ Log.i("美女","我是真的愛你"); } ... //其他方法 }
然後需要一個Component
@PerActivity @Component() public interface ActivityComponent { void inject(MainActivity mainActivity); }
在MainActivity類中使用
public class MainActivity extends AppCompatActivity { @Inject A a; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerActivityComponent.builder() .build() .inject(this); a.doSomething(); a.doOtherthing(); } }
使用(Module方式)
類A :
public class A { private int field; ... //其他屬性 public A() { } public void doSomething(){ Log.i("美女","我愛你"); } public void doOtherthing(){ Log.i("美女","我是真的愛你"); } ... //其他方法 }
我們先要構造一個Module
@Module public class AMedule { @PerActivity @Provides A provideA(){ return new A(); } }
然後需要一個Component
@PerActivity @Component(modules = AMedule.class) public interface ActivityComponent { void inject(MainActivity mainActivity); }
在MainActivity類中使用
public class MainActivity extends AppCompatActivity { @Inject A a; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerActivityComponent.builder() .aMedule(new AMedule()) .build() .inject(this); a.doSomething(); a.doOtherthing(); } }
沒看明白?好吧,我們繼續
假如我們還有一個SecondActivity也要使用A的對象
類A :
public class A { private int field; ... //其他屬性 public A() { } public void doSomething(){ Log.i("美女","我愛你"); } public void doOtherthing(){ Log.i("美女","我是真的愛你"); } ... //其他方法 }
我們先要構造一個Module
@Module public class AMedule { @PerActivity @Provides A provideA(){ return new A(); } }
然後需要一個Component
@PerActivity @Component(modules = AMedule.class) public interface ActivityComponent { void inject(MainActivity mainActivity); void inject(SecondActivity secondActivity); }
在MainActivity類中使用
public class MainActivity extends AppCompatActivity { @Inject A a; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerActivityComponent.builder() .aMedule(new AMedule()) .build() .inject(this); a.doSomething(); a.doOtherthing(); } }
在SecondActivity類中使用
public class SecondActivity extends AppCompatActivity { @Inject A a; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerActivityComponent.builder() .aMedule(new AMedule()) .build() .inject(this); a.doSomething(); a.doOtherthing(); } }
那麼,這裏MainActivity和SecondActivity中的A的對象a是同一個
5.子Component
子Component跟前面的一個Component依賴另一個Component有點像,區別是子Component可以獲取到父Component的所有可以生產出的對象,而Component依賴則只能獲取到被依賴的Component所暴露出來的可以生產的對象。 例子:
類A :
public class A { private int field; ... //其他屬性 public A() { } public void doSomething(){ Log.i("美女","我愛你"); } public void doOtherthing(){ Log.i("美女","我是真的愛你"); } ... //其他方法 }
我們先要構造一個Module
@Module public class AMedule { @Provides A provideA(){ return new A(); } }
然後需要一個Component
@Component(modules = AMedule.class) public interface ActivityComponent { void inject(MainActivity mainActivity); CComponent plus(Module1 module1,Module2 module2,...);//參數爲CComponent依賴的Module }
然後一個子Component(使用@Subcomponent註解)
@Subcomponent(modules = {...}) public interface CComponent { void inject(SecondActivity secondActivity); }
在MainActivity類中使用,將MainActivity注入到父Component中
public class MainActivity extends AppCompatActivity { @Inject A a; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerActivityComponent.builder() .aMedule(new AMedule()) .build() .inject(this); a.doSomething(); a.doOtherthing(); } }
在SecondActivity類中使用,將SecondActivity注入到子Component中
public class SecondActivity extends AppCompatActivity { @Inject A a; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerActivityComponent.builder() .aMedule(new AMedule()) .build() .plus(new Module1(),new Module2(),...)//CComponent依賴的Module .inject(this); a.doSomething(); a.doOtherthing(); } }
6.@Named註解
這個註解是用來設置別名的,用來區分同一個類型的不同對象。比如我們都知道Android 中的Context有兩種,一種是全局的,一種是Activity中的,爲了區分他們,我們就可以加上別名;又比如有一個Person類,他有兩個實例,一個是小李,一個是小美,我們也可以用別名區分他們。
Person類 :
public class Person { private String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
我們先要構造一個Module
@Module public class ActivityMedule { @Named("小李") @Provides Person providePerson(){ return new Person("小李",18); } @Named("小美") @Provides Person providePerson(){ return new Person("小美",24); } }
然後需要一個Component
@Component(modules = ActivityMedule.class) public interface ActivityComponent { void inject(MainActivity mainActivity); }
在MainActivity類中使用
public class MainActivity extends AppCompatActivity { @Named("小李") @Inject Person p1; @Named("小美") @Inject Person p2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerActivityComponent.builder() .activityMedule(new ActivityMedule()) .build() .inject(this); Log.i("person",p1.toString()); Log.i("person",p2.toString()); } }
當然,如果你覺得@Named註解滿足不了你的需求,你也可以使用@Qualifier來自定義自己的別名註解
7.構造函數注入和方法注入(補充)
之前寫文章的時候忘了這個內容了,有點不好意思,看到這篇文章這麼受大家的歡迎,心中很高興,決定儘量做到完美。
構造函數注入和方法注入其實差不多,都是爲我們要生產的對象提供他要依賴的其他對象,不明白?看代碼吧
BeautyGirl類:
public class BeautyGirl{ private String name; private int age; //恕罪 恕罪,女人的年齡是禁忌 ... //其他屬性 public void setName(String name){ this.name = name; } public String getName(){ return this.name; } public void setAge(int age){ this.age = age; } public int getAge(){ return this.age; } ... //其他方法 }
類A :
public class A { private int field; private BeautyGirl girl; ... //其他屬性 @Inject public A(BeautyGirl girl) { this.girl=girl; } public void doSomething(){ Log.i("美女",girl.getName+"我愛你"); } public void doOtherthing(){ Log.i("美女",girl.getName+"我是真的愛你"); } ... //其他方法 }
我們先要構造一個Module,生產BeautyGirl
@Module public class AMedule { @PerActivity @Provides BeautyGirl provideBeautyGirl(){ BeautyGirl gril= new BeautyGirl(); girl.setName("天上少有,地上沒有的大美女"); girl.setAge(20); return girl; } }
然後需要一個Component
@PerActivity @Component(modules = AMedule.class) public interface ActivityComponent { void inject(MainActivity mainActivity); }
在MainActivity類中使用
public class MainActivity extends AppCompatActivity { @Inject A a; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); DaggerActivityComponent.builder() .aMedule(new AMedule()) .build() .inject(this); a.doSomething(); a.doOtherthing(); } }
明白了沒,A的依賴對象BeautyGirl是通過構造參數從外部傳入的(也就是一個依賴注入),當構造方法或者其他方法添加了@Inject註解的時候,我們要生產這個類的對象的時候首先就會去查找滿足的依賴,然後纔會生產我們需要的對象。(方法注入就不講了,跟構造函數一樣,只不過構造函數添加@Inject註解,在場景類(上面的MainActivity)就可以直接通過@Inject生產這個對象)
8.在android studio的使用,在build.gradle中添加依賴庫
compile 'com.google.dagger:dagger:2.2' compile 'com.google.dagger:dagger-compiler:2.2' provided 'org.glassfish:javax.annotation:10.0-b28'
好了,大概就講這麼多吧!
如果有更深的理解,本文將會修改;
如果有錯誤的地方,歡迎指正;
如果你有更好的理解,歡迎交流。本文爲原創文章,版權歸博主所有,轉載請註明出處。
更多資料:
dagger官方github地址 : https://github.com/google/dagger
dagger2在android中使用 :http://fernandocejas.com/2015/04/11/tasting-dagger-2-on-android/
我的github地址以及使用demo: https://github.com/naivor/naivorapp