模塊解耦神器-Dagger2的使用和詳解

1 Dagger2簡(jiǎn)介

Dagger2提供給Java和Android使用,主要用于模塊間解耦、提高代碼的健壯性和可維護(hù)性。它是一款依賴注入框架,使用了IOC(控制反轉(zhuǎn))的思想,在編譯階段使用APT利用Java注解生成Java代碼,然后結(jié)合部分手寫代碼來(lái)完整依賴注入工作。

運(yùn)行前需要先編譯一次項(xiàng)目,目的是用APT生成中間代碼。Dagger2不使用反射,在編譯階段生成代碼,所以不會(huì)程序性能有影響。

IOC(控制反轉(zhuǎn))和依賴注入的區(qū)別:

控制反轉(zhuǎn)是一種在軟件工程中解耦合的思想,調(diào)用類只依賴接口,而不依賴具體的實(shí)現(xiàn)類,減少了耦合。 控制權(quán)交給了容器,在運(yùn)行的時(shí)候才由容器決定將具體的實(shí)現(xiàn)動(dòng)態(tài)的“注入”到調(diào)用類的對(duì)象中。 依賴注入是一種設(shè)計(jì)模式,可以作為控制反轉(zhuǎn)的一種實(shí)現(xiàn)方式。

Dagger2官網(wǎng):https://dagger.dev/

Dagger2 Github:https://github.com/google/dagger

2 Dagger2原理解析

Dagger2原理.png

可以使用用戶在網(wǎng)上買東西,然后通過(guò)快遞配送來(lái)理解Dagger2的使用原理。用戶購(gòu)買一本書(@Inject注解),然后賣家(Module)進(jìn)行打包,接著快遞員(Component)配送,然后用戶就收到了這本書。這樣就達(dá)到了解耦的目的,只需要使用@Inject注解,就能獲取到該對(duì)象。

3 Dagger2中的注解

Dagger2通過(guò)注解使用APT生成代碼,所以首先要知道Dagger2中常用注解的使用。

3.1 @Inject

@Inject有兩種用處,一是用來(lái)標(biāo)注需要依賴獲取的對(duì)象,比如:

public class FirstActivity extends AppCompatActivity {
    @Inject
    Book book;

    ...
}

就表示在當(dāng)前類中,需要依賴Book。

二是用來(lái)標(biāo)注構(gòu)造方法,Dagger2通過(guò)@Inject注解可以在需要這個(gè)類實(shí)例的時(shí)候來(lái)找到這個(gè)構(gòu)造函數(shù)并把相關(guān)實(shí)例構(gòu)造出來(lái),以此來(lái)為被@Inject標(biāo)記了的變量提供依賴,同時(shí)會(huì)在build下生成對(duì)應(yīng)的xxx_Factory類,后面示例中會(huì)詳解。

public class Book {

    @Inject
    public Book() {
    }
}

3.2 @Module

@Module用來(lái)標(biāo)注提供依賴對(duì)象的類,也就是“包裹”Module類,Module類中會(huì)有一個(gè)方法來(lái)提供依賴對(duì)象,在這個(gè)方法中可以對(duì)注入對(duì)象的有參構(gòu)造函數(shù)傳入?yún)?shù)或者進(jìn)行其他處理。

3.3 @Provides

@Provides用來(lái)標(biāo)注Module類中的提供依賴對(duì)象的方法進(jìn)行標(biāo)注,該方法在需要提供依賴時(shí)被調(diào)用,從而把預(yù)先提供好的對(duì)象當(dāng)做依賴給標(biāo)注了@Inject 的變量賦值。

@Module@Provides標(biāo)注的類在build下會(huì)生成對(duì)應(yīng)的xxxModule_xxxFactory類。

3.4 @Component

@Component用來(lái)標(biāo)注接口,該接口提供了方法用來(lái)傳入業(yè)務(wù)層,是業(yè)務(wù)層和Module之間的連接器。標(biāo)注后會(huì)在build下生成DaggerxxxComponent類。

3.5 @Singleton

默認(rèn)情況下,@Inject獲取到的依賴對(duì)象是非單例的,要想實(shí)現(xiàn)單例,需要用@Singleton對(duì)Module中的provide方法和Conponent接口進(jìn)行標(biāo)注。

4 Dagger2使用

引入依賴:

implementation 'com.google.dagger:dagger:2.41'
annotationProcessor 'com.google.dagger:dagger-compiler:2.41'

4.1 獲取到的依賴注入對(duì)象是非單例

創(chuàng)建商品Book。

public class Book {
    @Inject
    public Book() {
    }
}

創(chuàng)建包裹Module。

@Module
public class BookModule {

    @Provides
    public Book provideBook() {
        return new Book();
    }
}

創(chuàng)建快遞員Component。

@Component(modules = BookModule.class)
public interface BookComponent {
    void injectFirstActivity(FirstActivity activity);

    void injectSecondActivity(SecondActivity activity);
}

創(chuàng)建兩個(gè)FirstActivity,通過(guò)@Inject獲取兩個(gè)依賴注入對(duì)象Book,并打印兩個(gè)Book的hasCode。

public class FirstActivity extends AppCompatActivity {

    private static final String TAG = "zhangmushui";

    @Inject
    Book book;

    @Inject
    Book book2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_first);

        //DaggerBookComponent是由dagger-compiler通過(guò)APT自動(dòng)生成的
        DaggerBookComponent.builder()
            .bookModule(new BookModule())
            .build().injectFirstActivity(this);

        //另個(gè)Book的hashCode不一樣,說(shuō)明Dagger2提供的Book默認(rèn)是非單例的
        Log.d(TAG, "book: " + book.hashCode());
        Log.d(TAG, "book2: " + book2.hashCode());

        findViewById(R.id.button).setOnClickListener(v ->
                                                     startActivity(new Intent(this, SecondActivity.class)));
    }
}

創(chuàng)建SecondActivity

public class SecondActivity extends AppCompatActivity {

    private static final String TAG = "zhangmushui";

    @Inject
    Book book;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        DaggerBookComponent.builder()
                .bookModule(new BookModule())
                .build().injectSecondActivity(this);

        Log.d(TAG, "book: " + book.hashCode());
    }
}

控制臺(tái)可以看到,兩個(gè)頁(yè)面中的3個(gè)Book對(duì)象的hasCode都不同,可見通過(guò)Dagger2默認(rèn)獲取到的依賴注入對(duì)象是非單例的。

2022-04-20 17:00:04.388 6649-6649/cn.zhangmushui.dagger D/zhangmushui: book2: 94427313
2022-04-20 17:00:16.220 6649-6649/cn.zhangmushui.dagger D/zhangmushui: book: 63721347
2022-04-20 18:46:50.724 6649-6649/cn.zhangmushui.dagger D/zhangmushui: book: 194950979

4.2 獲取到的依賴注入對(duì)象是局部單例

首先在Module的provide方法上加上@Singleton,需要注意一點(diǎn)的是,之前版本的Dagger2需要在Module類上也添加@Singleton注解,新版本的已經(jīng)不需要。

/**
 * 想要提供的對(duì)象是單例,需要加上@Singleton注解
 * 但是最新版本的Dagger2已經(jīng)不需要在Module上添加@Singleton注解(不然編譯會(huì)報(bào)錯(cuò)),只需要在下面的provider和Component上添加@Singleton即可。
 */
@Module
public class BookModule {

    @Singleton
    @Provides
    public Book provideBook() {
        return new Book();
    }
}

然后在Component類上添加@Singleton注解。

@Singleton
@Component(modules = BookModule.class)
public interface BookComponent {
    void injectFirstActivity(FirstActivity activity);

    void injectSecondActivity(SecondActivity activity);
}

重新編譯后運(yùn)行,可以看到FirstActivity中的兩個(gè)Book的hashCode是相同的,SecondActivity和FirstActivity中的Book對(duì)象hashCode是不一樣的,可見實(shí)現(xiàn)了局部單例。

2022-04-20 19:11:04.306 9367-9367/cn.zhangmushui.dagger D/zhangmushui: book: 29400920
2022-04-20 19:11:04.306 9367-9367/cn.zhangmushui.dagger D/zhangmushui: book2: 29400920
2022-04-20 19:11:06.047 9367-9367/cn.zhangmushui.dagger D/zhangmushui: book: 146490930

4.3 獲取到的依賴注入對(duì)象是全局單例

在上邊的基礎(chǔ)上繼續(xù)做修改,創(chuàng)建Application,在Application中實(shí)例化Component對(duì)象,并提供獲取Component的方法。

public class MainApplication extends Application {

    private BookComponent mBookComponent;

    @Override
    public void onCreate() {
        super.onCreate();

        mBookComponent = DaggerBookComponent.builder()
                .bookModule(new BookModule())
                .build();

    }

    public BookComponent getBookComponent() {
        return mBookComponent;
    }
}

然后在Activity中通過(guò)Application去獲取Component。

public class FirstActivity extends AppCompatActivity {

    private static final String TAG = "zhangmushui";

    @Inject
    Book book;

    @Inject
    Book book2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_first);

        //在Application中獲取Component,實(shí)現(xiàn)全局單例
        ((MainApplication) getApplication()).getBookComponent().injectFirstActivity(this);

        //兩個(gè)Book的。hashCode一致。
        Log.d(TAG, "book: " + book.hashCode());
        Log.d(TAG, "book2: " + book2.hashCode());

        findViewById(R.id.button).setOnClickListener(v ->
                startActivity(new Intent(this, SecondActivity.class)));
    }
}
public class SecondActivity extends AppCompatActivity {

    private static final String TAG = "zhangmushui";

    @Inject
    Book book;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        ((MainApplication) getApplication()).getBookComponent().injectSecondActivity(this);

        //SecondActivity和FirstActivity中的Book的hashCode一致。
        Log.d(TAG, "book: " + book.hashCode());
    }
}

編譯后運(yùn)行查看日志,發(fā)現(xiàn)兩個(gè)頁(yè)面的3個(gè)Book的hashCode都是一樣的,這就實(shí)現(xiàn)了全局單例。

2022-04-20 19:15:13.890 9544-9544/cn.zhangmushui.dagger D/zhangmushui: book: 29400920
2022-04-20 19:15:13.890 9544-9544/cn.zhangmushui.dagger D/zhangmushui: book2: 29400920
2022-04-20 19:15:23.048 9544-9544/cn.zhangmushui.dagger D/zhangmushui: book: 29400920

5 Dagger2原理解析

接下來(lái)以默認(rèn)非單例的版本去分析Dagger2的原理。

所有用注解標(biāo)注的類,Dagger2都會(huì)用APT在build下去生成對(duì)應(yīng)的輔助類,這些輔助類的名字是根據(jù)dagger-compiler注解依賴庫(kù)中的命名規(guī)則生成的。

手動(dòng)創(chuàng)建 APT生成 命名規(guī)則 注解
FirstActivity FirstActivity_MembersInjector 類名_MembersInjector @Inject
SecondActivity SecondActivity_MembersInjector 類名_MembersInjector @Inject
BookComponent DaggerBookComponent Dagger對(duì)象名Component
@Component
BookModule BookModule_ProvideBookFactory 對(duì)象名Module_方法名Factory @Module @Provides
Book Book_Factory 對(duì)象名_Factory @Inject

為了方便觀看,原理直接寫到注釋里邊,注釋前邊用數(shù)字標(biāo)出,表示大概的調(diào)用順序。

// Generated by Dagger (https://dagger.dev).
package cn.zhangmushui.dagger.demo1;

import cn.zhangmushui.dagger.demo1.activity.FirstActivity;
import cn.zhangmushui.dagger.demo1.activity.FirstActivity_MembersInjector;
import cn.zhangmushui.dagger.demo1.activity.SecondActivity;
import cn.zhangmushui.dagger.demo1.activity.SecondActivity_MembersInjector;
import dagger.internal.DaggerGenerated;
import dagger.internal.Preconditions;

@DaggerGenerated
@SuppressWarnings({
        "unchecked",
        "rawtypes"
})
public final class DaggerBookComponent implements BookComponent {
    private final BookModule bookModule;

    private final DaggerBookComponent bookComponent = this;

    private DaggerBookComponent(BookModule bookModuleParam) {
        this.bookModule = bookModuleParam;

    }

    public static Builder builder() {
        //1 使用構(gòu)造者模式實(shí)例化DaggerBookComponent對(duì)象
        return new Builder();
    }

    public static BookComponent create() {
        return new Builder().build();
    }

    @Override
    public void injectFirstActivity(FirstActivity activity) {
        injectFirstActivity2(activity);
    }

    @Override
    public void injectSecondActivity(SecondActivity activity) {
        injectSecondActivity2(activity);
    }

    private FirstActivity injectFirstActivity2(FirstActivity instance) {
        //4 Activity中的@Inject生成injectBook方法,通過(guò)BookModule_ProvideBookFactory來(lái)獲取依賴注入對(duì)象
        FirstActivity_MembersInjector.injectBook(instance, BookModule_ProvideBookFactory.provideBook(bookModule));
        FirstActivity_MembersInjector.injectBook2(instance, BookModule_ProvideBookFactory.provideBook(bookModule));
        return instance;
    }

    private SecondActivity injectSecondActivity2(SecondActivity instance) {
        SecondActivity_MembersInjector.injectBook(instance, BookModule_ProvideBookFactory.provideBook(bookModule));
        return instance;
    }

    public static final class Builder {
        private BookModule bookModule;

        private Builder() {
        }

        //2 傳入一個(gè)BookModule對(duì)象
        public Builder bookModule(BookModule bookModule) {
            this.bookModule = Preconditions.checkNotNull(bookModule);
            return this;
        }

        //3 實(shí)例化DaggerBookComponent
        public BookComponent build() {
            if (bookModule == null) {
                this.bookModule = new BookModule();
            }
            return new DaggerBookComponent(bookModule);
        }
    }
}
// Generated by Dagger (https://dagger.dev).
package cn.zhangmushui.dagger.demo1;

import dagger.internal.DaggerGenerated;
import dagger.internal.Factory;
import dagger.internal.Preconditions;
import dagger.internal.QualifierMetadata;
import dagger.internal.ScopeMetadata;

@ScopeMetadata
@QualifierMetadata
@DaggerGenerated
@SuppressWarnings({
    "unchecked",
    "rawtypes"
})
public final class BookModule_ProvideBookFactory implements Factory<Book> {
  private final BookModule module;

  public BookModule_ProvideBookFactory(BookModule module) {
    this.module = module;
  }

  @Override
  public Book get() {
    return provideBook(module);
  }

  public static BookModule_ProvideBookFactory create(BookModule module) {
    return new BookModule_ProvideBookFactory(module);
  }

  //5 通過(guò)BookModule拿到Book對(duì)象,并用Dagger2提供的Preconditions.checkNotNullFromProvides()方法來(lái)檢查獲取到的對(duì)象是否為空
  public static Book provideBook(BookModule instance) {
    return Preconditions.checkNotNullFromProvides(instance.provideBook());
  }
}
// Generated by Dagger (https://dagger.dev).
package cn.zhangmushui.dagger.demo1.activity;

import cn.zhangmushui.dagger.demo1.Book;
import dagger.MembersInjector;
import dagger.internal.DaggerGenerated;
import dagger.internal.InjectedFieldSignature;
import dagger.internal.QualifierMetadata;
import javax.inject.Provider;

@QualifierMetadata
@DaggerGenerated
@SuppressWarnings({
    "unchecked",
    "rawtypes"
})
public final class FirstActivity_MembersInjector implements MembersInjector<FirstActivity> {
  private final Provider<Book> bookProvider;

  private final Provider<Book> book2Provider;

  public FirstActivity_MembersInjector(Provider<Book> bookProvider, Provider<Book> book2Provider) {
    this.bookProvider = bookProvider;
    this.book2Provider = book2Provider;
  }

  public static MembersInjector<FirstActivity> create(Provider<Book> bookProvider,
      Provider<Book> book2Provider) {
    return new FirstActivity_MembersInjector(bookProvider, book2Provider);
  }

  @Override
  public void injectMembers(FirstActivity instance) {
    injectBook(instance, bookProvider.get());
    injectBook2(instance, book2Provider.get());
  }

  @InjectedFieldSignature("cn.zhangmushui.dagger.demo1.activity.FirstActivity.book")
  public static void injectBook(FirstActivity instance, Book book) {
    //6 將獲取到的Book對(duì)象賦值給Activity中的book
    instance.book = book;
  }

  @InjectedFieldSignature("cn.zhangmushui.dagger.demo1.activity.FirstActivity.book2")
  public static void injectBook2(FirstActivity instance, Book book2) {
    instance.book2 = book2;
  }
}

以上幾處注釋,就是Dagger2的核心原理,也就是通過(guò)Dagger生成輔助類,在業(yè)務(wù)層需要獲取依賴對(duì)象的地方使用@Inject去標(biāo)注對(duì)象,就可以交給Dagger2的這些輔助類去實(shí)例化對(duì)象,起到了解耦的作用,提高了代碼的可讀性和可維護(hù)性。

類圖如下,白色的是手動(dòng)創(chuàng)建的類,灰色的是APT生成的類。

完整源碼下載:https://gitee.com/mushuicode/dagger

關(guān)注木水小站 (zhangmushui.cn)和微信公眾號(hào)【木水Code】,及時(shí)獲取更多最新文章。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容