Dagger2的使用

一、Dagger2简介

Dagger2是google的一款依赖注入框架,前身是square公司开发的dagger1,适用于Android和Java应用程序的开发。

Dagger2提供给Java和Android使用,主要用于模块间解耦、提高代码的可维护性和代码的可测试性,是一款依赖注入框架,使用了IOC(控制反转)的思想,在编译阶段使用APT或kapt利用注解生成Java代码,然后结合部分手写代码来完整依赖注入工作。Dagger1通过反射实现的,而Dagger2则是编译阶段生成代码实现。

二、 Dagger2注解

1、@Inject有两种用处,一表明需求方,用来标注需要获取依赖对象的属性或函数中参数,下面例子FirstActivity需要依赖Book。二表明提供方,用来标注构造方法,Dagger2通过@Inject注解可以在需要这个类实例的时候来找到这个构造函数并把相关实例构造出来,以此来为被@Inject标记了的变量提供依赖,同时会在build下生成对应的xxx_Factory类,具体看后面详解。

scala 复制代码
public class FirstActivity extends AppCompatActivity {
    @Inject
    Book book;

    ...
}

public class Book {

    @Inject
    public Book() {
    }
}

2、@Module用来标注提供依赖对象的类,类似于媒人。Module类中会有一个方法来提供依赖对象,在这个方法中可以对注入对象的有参构造函数传入参数或者进行其他处理,该方法一般会被@Provides注解修改。

kotlin 复制代码
@Module
//@Singleton
class ServiceModule {

//    @Singleton
    @Provides
    fun provideService() = HttpClient.getInstance().getService(ApiService::class.java, Constants.BASE_URL)

}

3、@Provides用来标注Module类中的提供依赖对象的方法进行标注,该方法在需要提供依赖时被调用,从而把提供好的对象当做依赖给标注了@Inject 的变量赋值。被@Module@Provides标注的类在build下会生成对应的xxxModule_xxxFactory类。

4、@Component用来标注接口,该接口提供了方法用来传入业务层,是业务层和Module之间的连接器。标注后会在build下生成DaggerxxxComponent类。

less 复制代码
@Singleton
@Component(
    modules = [
        ServiceModule::class
    ]
)
interface AppComponent {

    fun injectService(repository : HomeRepository)
}

5、 @Singleton. 默认情况下,@Inject获取到的依赖对象是非单例的,要想实现单例,需要用@Singleton对Module中的provide方法和Conponent接口进行标注。

三、 Dagger2使用

1、添加gradle依赖

arduino 复制代码
implementation 'com.google.dagger:dagger-android:2.51.1'
kapt 'com.google.dagger:dagger-compiler:2.51.1'

2、添加代码,分别创建依赖对象类、module、component、代码调用注入部分

java 复制代码
public class Book {
    @Inject
    public Book() {
    }
}


@Module
public class BookModule {

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


@Component(modules = BookModule.class)
public interface BookComponent {

    void injectFirstActivity(FirstActivity activity);

    void injectSecondActivity(SecondActivity activity);
}


创建两个Activity,通过`@Inject`获取两个依赖注入对象Book,并打印两个Book。
public class FirstActivity extends AppCompatActivity {
    private static final String TAG = "First";

    @Inject
    Book book;

    @Inject
    Book book2;

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

        //DaggerBookComponent是由dagger-compiler通过APT自动生成的
        DaggerBookComponent.builder()
            .bookModule(new BookModule())
            .build().injectFirstActivity(this);

        //另个Book的hashCode不一样,说明Dagger2提供的Book默认是非单例的
        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 = "Second";

    @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());
    }
}

通过log可以看出3个Book对象的是不同的,可见通过Dagger2默认获取到的依赖注入对象是非单例的。 3、局部单例 首先在Module的provide方法上加上@Singleton,需要注意的是之前版本的Dagger2需要在Module类上也添加@Singleton注解,新版本的已经不需要。

java 复制代码
/**
 * 若想依赖对象是单例,需要加上@Singleton注解。最新版本的Dagger2已经不需要在Module上添加@Singleton注解(不然编译会报错),只需要在下面的provider上添加@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);
}

重新编译后运行,可以看到FirstActivity中的两个Book的hashCode是相同的,SecondActivity和FirstActivity中的Book对象hashCode是不一样的,可见实现了局部单例。 4、全局单例,需要借助全局保存DaggerxxxComponent对象。 在上边的基础上继续做修改,创建Application,在Application中实例化Component对象,并提供获取Component的方法。

scala 复制代码
public class MyApplication extends Application {
    private BookComponent mBookComponent;

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

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

    }

    public BookComponent getBookComponent() {
        return mBookComponent;
    }
}

然后在Activity中通过Application去获取Component。

public class FirstActivity extends AppCompatActivity {
    private static final String TAG = "First";

    @Inject
    Book book;

    @Inject
    Book book2;

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

        //在Application中获取Component,实现全局单例
        ((MyApplication) getApplication()).getBookComponent().injectFirstActivity(this);

        //两个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 = "Second";

    @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());
    }
}

查看Log可以发现两个页面的3个Book是同一个,那就实现了全局单例。

四、原理初步

typescript 复制代码
// Generated by Dagger (https://dagger.dev).
package com.cding.dagger.app;

import com.cding.dagger.app.activity.FirstActivity;
import com.cding.dagger.app.activity.FirstActivity_MembersInjector;
import com.cding.dagger.app.activity.SecondActivity;
import com.cding.dagger.app.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 使用构造者模式实例化DaggerBookComponent对象
        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方法,通过BookModule_ProvideBookFactory来获取依赖注入对象
        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 传入一个BookModule对象
        public Builder bookModule(BookModule bookModule) {
            this.bookModule = Preconditions.checkNotNull(bookModule);
            return this;
        }

        //3 实例化DaggerBookComponent
        public BookComponent build() {
            if (bookModule == null) {
                this.bookModule = new BookModule();
            }
            return new DaggerBookComponent(bookModule);
        }
    }
}
java 复制代码
// Generated by Dagger (https://dagger.dev).
package com.cding.dagger.app;

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 通过BookModule拿到Book对象,并用Dagger2提供的Preconditions.checkNotNullFromProvides()方法来检查获取到的对象是否为空
  public static Book provideBook(BookModule instance) {
    return Preconditions.checkNotNullFromProvides(instance.provideBook());
  }
}
java 复制代码
// Generated by Dagger (https://dagger.dev).
package com.cding.dagger.app.activity;

import com.cding.dagger.app.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对象赋值给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原理是通过Dagger生成辅助类,在业务层需要获取依赖对象的地方使用@Inject去标注对象,就可以交给Dagger2的这些辅助类去实例化对象,起到了解耦的作用,提高了代码的可读性和可维护性和可测试性。

相关推荐
coder_pig1 小时前
🤡 公司Android老项目升级踩坑小记
android·flutter·gradle
死就死在补习班2 小时前
Android系统源码分析Input - InputReader读取事件
android
死就死在补习班2 小时前
Android系统源码分析Input - InputChannel通信
android
死就死在补习班2 小时前
Android系统源码分析Input - 设备添加流程
android
死就死在补习班3 小时前
Android系统源码分析Input - 启动流程
android
tom4i3 小时前
Launcher3 to Launchpad 01 布局修改
android
雨白3 小时前
OkHttpClient 核心配置详解
android·okhttp
淡淡的香烟3 小时前
Android auncher3实现简单的负一屏功能
android
RabbitYao4 小时前
Android 项目 通过 AndroidStringsTool 更新多语言词条
android·python
RabbitYao4 小时前
使用 Gemini 及 Python 更新 Android 多语言 Excel 文件
android·python