網上都說Dagger2是比較難上手的,我在看了大量資料和使用時也遇到了不少不懂或者模糊的知識點,並且大部分博客資料都比較古老。忽然有那麼一瞬間,忽然明白了因此然,故總結了4篇文章。話說在java中使用仍是很繁瑣的,不要怕帶你真正上手,並運用到咱們的Android項目中去。java
本次Dagger2講解總共分4篇:
一、Dagger2基礎知識及在Java中使用(1)
二、Dagger2基礎知識及在Java中使用(2)
三、Dagger2進階知識及在Android中使用
四、Dagger2華麗使用在MVP框架中android
首先簡單申明下,Dagger2的好處不是本文的重點。你們能夠自行百度。Dagger2是依賴註解框架,像咱們之間的butterknife也是這樣的框架,想這樣的框架依賴通常都有2行。第二行是以annotationProcessor開頭的。這實際上是apt的工具,並且這樣的依賴註解框架不會影響性能(不是反射機制),在編譯的時候,apt把要用的代碼生成。因此大可放心使用
再舉我理解的例子(你們不要全信,哈哈稍微不恰當):@Component至關於一個注射器(記住是接口);@Module至關於注射液,就是數據源(記住這裏是類或者抽象類),此時要把注射液放入指定哪一個注射器如:@Component( modules = ... );@Inject 至關於標註被注射體。git
以後的講解都是走完簡單的流程,實現功能,而後講大概理解。貼在博客上的代碼,可能會省略部分代碼,便於理解。github上的Demo及註釋,很是詳細,接近完美0-0#
若是是徹底沒了解過,關於Dagger一些標註的具體介紹和理解,推薦這裏有3篇介紹標註的意思和怎麼工做的github
implementation 'com.google.dagger:dagger:2.24'
annotationProcessor "com.google.dagger:dagger-compiler:2.24"
複製代碼
首先隨便定義個類:Person,無參構造方法用@Inject標註:bash
public class Person {
@Inject
public Person() {
}
}
複製代碼
@Component
public interface AstudyActivityComponent {
void injectTo(AstudyActivity astudyActivity);
}
複製代碼
作好上面步驟後,點開studio裏build標籤下的Make Project。讓apt幫咱們生成代碼,通常生成代碼爲Dagger+你定義Component的類名。
以後這個步驟再也不重複,就是你寫完準備代碼的時候必定要讓apt生成代碼,Make Project下
而後在咱們的Activity裏:app
public class AstudyActivity extends BaseActivity {
@Inject
Person person;
@Override
//這裏是我封裝的onCreate,省略部分代碼,只爲理解,以後都請忽略!
protected void processLogic() {
//第一種
DaggerAstudyActivityComponent.create().injectTo(this);
//第二種
//DaggerAstudyActivityComponent.builder().build().injectTo(this);
}
}
複製代碼
在咱們的Activity裏build下咱們的Component,而後註冊在咱們的Activity裏,就可使用經過咱們的@Inject使用咱們的person了。
這裏初始化有2種:
一、DaggerAstudyActivityComponent.create().injectTo(this);
二、DaggerAstudyActivityComponent.builder().build().injectTo(this); 這個使用module傳值必定要使用框架
那麼一個簡單的使用就實現了,這裏忽略了new的過程,從這個過程就知道他解耦的實現了。ide
爲何會有module的概念,好比上面的Person的構造方法能夠用@Inject標註,可是引入的第三方庫但是沒有辦法加的,因此這裏使用@Module能夠解決這個問題。
這裏咱們定義個Human,僞裝他是第三方類庫,裏面沒有使用@Inject函數
public class Human {
public Human() {
}
}
複製代碼
接下的步驟先定義咱們的數據源Module,也就是先定義初始化的地方,以前Person的構造方法是用@Inject。首先命名規則最好加上Module,用@Module標註。而後裏面定義個方法,用 @Provides標註。返回值爲咱們須要初始化的類,方法名最好是以Provides結尾。其實這裏能夠定義多個方法,後面說工具
@Module
public class BstudyActivityModule {
@Provides
Human humanProvides(){
return new Human();
}
}
複製代碼
而後是咱們的Component。這裏與以前不一樣的是(modules = BstudyActivityModule.class),這就至關於把注射液放進注射器。這裏能夠有多個Module,後面說
@Component(modules = BstudyActivityModule.class)
public interface BstudyActivityComponent {
void injectTo(BstudyActivity bstudyActivity);
}
複製代碼
Make Project後,在Activity裏操做與以前如出一轍。
這個其實不重要,重要的引出4,5的概念。明白這步,後面纔好理解。
首先咱們假設2個類,女人類,和靈魂類:且靈魂類有個錢的屬性。靈魂類又是女人的屬性。靈魂類以下:
public class Soul {
private int money;
public Soul() {
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
}
複製代碼
女人以下:
public class Woman {
private Soul soul;
public Soul getSoul() {
return soul;
}
public Woman(Soul soul) {
this.soul = soul;
}
}
複製代碼
首先仍是定義咱們的Module先。既然能夠傳參,固然是有個money的屬性。最終咱們依賴註解是要使用Woman類。咱們的providesWoman方法用@Provides標註,這個時候他回去找Soul的初始化,先經過@Provides去找Soul。這個時候找到了providesSoul。這樣就造成了女人類。假如這個時候沒有providesSoul。他會去找Soul類裏有沒有用@Inject標註的構造函數。若是尚未,那麼很差意思。出錯
@Module
public class CstudyModule {
private int money;
public CstudyModule(int money) {
this.money = money;
}
@Provides
Soul providesSoul() {
Soul soul = new Soul();
soul.setMoney(this.money);
return soul;
}
@Provides
Woman providesWoman(Soul soul) {
return new Woman(soul);
}
}
複製代碼
接下來是Component,沒有變化
@Component(modules = CstudyModule.class)
public interface CstudyActivityComponent {
void injectTo(CstudyActivity cstudyActivity);
}
複製代碼
Activity有些許變化,固然是傳參了。咱們給女人的靈魂傳了100塊,對,女人只值100塊!
public class CstudyActivity extends BaseActivity {
@Inject
Woman woman;
@Override
protected void processLogic() {
DaggerCstudyActivityComponent.builder()
.cstudyModule(new CstudyModule(100))
.build().injectTo(this);
}
}
複製代碼
咱們把三、經過Module傳參apt生成的代碼點開DaggerCstudyActivityComponent;看下圖是否是發現了一個Builder類,這是apt幫咱們自動生成的,咱們固然也能本身實現
仍是以 三、經過Module傳參的例子,咱們不用系統幫咱們生成的Builder,本身定義。前面的步驟都同樣,直接來看咱們的Component。@Component(modules = CstudyModule.class)
public interface DstudyActivityComponent {
void injectTo(DstudyActivity dstudyActivity);
@Component.Builder
interface Builder {
Builder cstudyModule(CstudyModule cstudyModule);
DstudyActivityComponent build();
}
}
複製代碼
Activity裏使用是同樣的。只不過咱們把系統自動幫咱們生成的,本身去寫了而已。仍是貼下Activity代碼吧
public class DstudyActivity extends BaseActivity {
@Inject
Woman dWoman;
@Override
protected void processLogic() {
DaggerDstudyActivityComponent.builder()
.cstudyModule(new CstudyModule(100))
.build().injectTo(this);
}
}
複製代碼
因此@Component.Builder的用法,用module傳參的例子。其餘都不用變,要變的是Component,定義個Builder並用@Component.Builder標註。這裏有2個方法:
這個時候你又說了,傳參,咱們老是要new CstudyModule(100)。原本說Dagger2在使用的時候省略new的過程,解耦。但這裏還要new,很low是否是。不急不急,強大的google把一切都想好了。這個時候遇到一個新的標註@BindsInstance。
@BindsInstance 大體這裏能夠理解爲幫咱們省去寫類的構造方法,而直接去賦值
省掉構造方法,那麼固然是首先改咱們的Module。咱們去掉Module的構造方法及money成員變量屬性,把money加到providesSoul裏成型參。看到這裏,這裏又可理解爲@BindsInstance 其實去找@Provides標記的方法的參數,假如類型一致就去初始化
@Module
public class EstudyModule {
@Provides
Soul providesSoul(int money) {
Soul soul = new Soul();
soul.setMoney(money);
return soul;
}
@Provides
Woman providesWoman(Soul soul) {
return new Woman(soul);
}
}
複製代碼
而後是修改的Component,改完Module,固然是modules = EstudyModule.class。這些我就忽略了,看了上面的步驟你也明白,我就直接說關鍵地方了。用@BindsInstance標註咱們返回值爲Builder的方法。裏面的參數改爲咱們的int Money。固然改爲咱們用@Provides標註的類型其實均可以,只不過這裏你若是改爲Soul soul,固然你初始化仍是要傳new Soul。過程就是這個過程
@Component(modules = EstudyModule.class)
public interface EstudyActivityComponent {
void injectTo(EstudyActivity estudyActivity);
@Component.Builder
interface Builder {
@BindsInstance
Builder initMoney(int money);
EstudyActivityComponent build();
}
}
複製代碼
最後是咱們的Activity
public class EstudyActivity extends BaseActivity {
@Inject
Woman woman;
@Override
protected void processLogic() {
DaggerEstudyActivityComponent.builder()
.initMoney(100)
.build().injectTo(this);
}
}
複製代碼
看到這裏,是否是以爲Dagger2還比較有意思。更有意思的在後面。固然也愈來愈繞了,可是你得興奮起來,精髓啊。
這裏咱們以Activity和Fragment爲例。假設咱們再Activity依賴注入Human類,此時在Fragment裏使用
先看定義Module,和以前同樣,沒什麼區別
@Module
public class FstudyActivityModule {
@Provides
Human providesHuman() {
return new Human();
}
}
複製代碼
再建ActivityComponent固然這裏,你也能夠加上void inject(FstudyActivity fstudyActivity)。重要一點是咱們要把依賴注入的類返回出去,定義方法provideHuman,由於是Component依賴Component。因此也能理解
@Component(modules = FstudyActivityModule.class)
public interface FstudyActivityComponent {
Human provideHuman();
}
複製代碼
再使用dependencies建FragmentComponent暫且能夠理解爲子Component,由於後面真的有子Component。dependencies = FstudyActivityComponent.class寫上咱們的父Component。後面是注入到Fragment裏
@Component(dependencies = FstudyActivityComponent.class)
public interface TestFragmentComponent {
void inject(TestFragment testFragment);
}
複製代碼
在Activity裏,要先生成ActivityComponent,而後提供個方法,把父Component提供給Fragment
public class FstudyActivity extends BaseActivity {
private FstudyActivityComponent fstudyActivityComponent;
@Override
protected void processLogic() {
fstudyActivityComponent = DaggerFstudyActivityComponent.create();
}
public FstudyActivityComponent getFstudyActivityComponent() {
return fstudyActivityComponent;
}
}
複製代碼
在Fragment裏
public class TestFragment extends BaseFragment {
@Inject
Human human;
@Override
protected void processLogic(Bundle savedInstanceState) {
FstudyActivityComponent fstudyActivityComponent = ((FstudyActivity) getActivity()).getFstudyActivityComponent();
DaggerTestFragmentComponent.builder()
.fstudyActivityComponent(fstudyActivityComponent)
.build().inject(this);
}
}
複製代碼
好了,在fragment可使用human了。在java裏使用,確實很繞,代碼多的讓你難以接受。建議先理解,後面出的一篇在Android中使用,你會很爽。
雖然是實現同一個效果,可是方式不一樣,目的是讓你更多瞭解Dagger2。一樣以上面的例子。Module和上面同樣不變(我這裏是爲了Demo區域化,雖然類名不一樣,可是內容是一致的)
首先建子Component,FragmenComponent,用@Subcomponent標註,並注入咱們的Fragment裏。爲何先建子Component呢。由於子Component要在父Component返回,繞不繞!!
@Subcomponent
public interface DemoFragmentComponent {
void inject(DemoFragment demoFragment);
}
複製代碼
而後是父Component,ActivityComponent,父Component一切正常,返回值是子Component
@Component(modules = GstudyActivityModule.class)
public interface GstudyActivityComponent {
DemoFragmentComponent demoFragmentComponent();
}
複製代碼
在Activity裏的操做同樣,初始化咱們的父Component,並提供方法,返回父Component,供Fragment使用。
而後是Fragment裏
public class DemoFragment extends BaseFragment {
@Inject
Human human;
@Override
protected void processLogic(Bundle savedInstanceState) {
GstudyActivityComponent gstudyActivityComponent = ((GstudyActivity) getActivity()).getGstudyActivityComponent();
gstudyActivityComponent
.demoFragmentComponent()
.inject(this);
}
}
複製代碼
這樣就成功了,能夠在Fragment使用human了。看明白了標題6,其實標題7原理是同樣的。
效果同樣,方式不一樣,目的仍是更瞭解Dagger2。能夠看到這裏的標註是@Subcomponent.Builder。因此和使用@Subcomponent相似。
好了,仍是以上面的例子爲例。這裏須要改的是父Component和子Componet。這個時候咱們難免想到@Component.Builder的用法。是否是同樣呢。這個時候我只能說相似,可是又不同。畢竟這裏多了個sub。咱們先看下@Component.Builder的用法,拷貝以前代碼(不知道再去回顧下【標題4】)
@Component(modules = CstudyModule.class。)
public interface DstudyActivityComponent {
void injectTo(DstudyActivity dstudyActivity);
@Component.Builder
interface Builder {
Builder cstudyModule(CstudyModule cstudyModule);
DstudyActivityComponent build();
}
}
複製代碼
咱們按照找個方式去寫@Subcomponent.Builder。,@Subcomponent.Builder要使用確定是在@Subcomponent下,毋庸置疑。首先發現沒有modules = CstudyModule.class。被@Subcomponent取代了。沒有Module咱們就使用無參
@Subcomponent
public interface OtherFragmentComponent {
void inject(OtherFragment otherFragment);
@Subcomponent.Builder
interface Builder {
Builder noModule();
OtherFragmentComponent build();
}
}
複製代碼
我先告訴告訴你運行結果把,運行結果報錯了。
@Subcomponent.Builder types must have exactly one zero-arg method, and that method must return the @Subcomponent type. Already found: hstudyActivityModule()
報錯信息以下:意思是不須要Build返回值方法,經過Already found: hstudyActivityModule()知道,已經發現了咱們的Module。咱們再想一想這個標註的名稱sub,不就是子Component繼承父Componet嗎。並且Dagger2內部已經默認了,因此這裏沒有Builder返回值方法。因此正確的子Component
@Subcomponent
public interface OtherFragmentComponent {
void inject(OtherFragment otherFragment);
@Subcomponent.Builder
interface Builder {
OtherFragmentComponent build();
}
}
複製代碼
接下來是父Component,返回值固然是咱們的Builder。
@Component(modules = HstudyActivityModule.class)
public interface HstudyActivityComponent {
OtherFragmentComponent.Builder sonbuilder();
}
複製代碼
有人就疑惑了不能夠返回子Component嗎。咱們假如此時返回子Component,我先告訴你運行報錯,信息以下:
Components may not have factory methods for subcomponents that define a builder.
大概意思是:用了@Subcomponent.Builder的話,Component沒有工廠模式方法去建立咱們的子Component。好了,就這樣,請原諒個人英語四級!!
Activity仍是和以前同樣,初始化咱們的父Component,並經過方法返回。Fragment裏使用依賴以下
public class OtherFragment extends BaseFragment {
@Inject
Human human;
@Override
protected void processLogic(Bundle savedInstanceState) {
HstudyActivityComponent hstudyActivityComponent = ((HstudyActivity) getActivity()).getHstudyActivityComponent();
hstudyActivityComponent.
sonbuilder().build().inject(this);
}
}
複製代碼
好了,繞來繞去,功能實現了!看到這裏對Dagger2大體瞭解了吧。
花了老大勁,別誤會,不是要錢。能不能留下個足跡star啊