Dagger2使用入门

Dagger2简介

Dagger2Dagger的升级版,是一个依赖注入框架,现在由Google接手维护。

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

依赖注入是面向对象编程的一种设计模式,其目的是为了降低程序耦合,这个耦合就是类之间的依赖引起的。

举个例子:我们在写面向对象程序时,往往会在一个类中引用另一个类,从而可以调用引用的类的方法完成某些功能,就像下面这样:

public class ClassA {
    ...
    ClassB b;
    ...
    public ClassA() {
        b = new ClassB();
    }

    public void do() {
        ...
        b.doSomething();
        ...
    }
}

这时就产生了依赖问题,ClassA依赖于ClassB,必须借助ClassB的方法,才能完成一些功能。这样看好像并没有什么问题,但是我们在ClassA的构造方法里面直接创建了ClassB的实例,问题就出现在这,在ClassA里直接创建ClassB实例,违背了单一职责原则ClassB实例的创建不应由ClassA来完成;其次耦合度增加,扩展性差,如果我们想在实例化ClassB的时候传入参数,那么不得不改动ClassA的构造方法,不符合开闭原则

因此我们需要一种注入方式,将依赖注入到宿主类(或者叫目标类)中,从而解决上面所述的问题。依赖注入有以下几种方式:

通过接口注入

interface ClassBInterface {
    void setB(ClassB b);
}

public class ClassA implements ClassBInterface {
    ClassB classB;

    @override
    void setB(ClassB b) {
        classB = b;
    }
}

通过set方法注入

public class ClassA {
    ClassB classB;

    public void setClassB(ClassB b) {
        classB = b;
    }
}

通过构造方法注入

public class ClassA {
    ClassB classB;

    public void ClassA(ClassB b) {
        classB = b;
    }
}

通过Java注解

public class ClassA {
    //此时并不会完成注入,还需要依赖注入框架的支持,如RoboGuice, Dagger2
    @inject ClassB classB;

    ...
    public ClassA() {}

在Dagger2中用的就是最后一种注入方式,通过注解的方式,将依赖注入到宿主类中。

Dagger2依赖注入

引入Dagger2

api 'com.google.dagger:dagger:2.28.3'
annotationProcessor 'com.google.dagger:dagger-compiler:2.28.3'

使用Dagger2

例子基于mvp模式,在mvp中,最常见的一种依赖关系,就是Activity持有Presenter的引用,并在Activity中实例化这个Presenter,即Activity依赖PresenterPresenter又需要依赖View接口,从而更新UI

public class MainActivity extends AppCompatActivity implements MainView {
    private MainPresenter mainPresenter;
    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //实例化presenter 将view传递给presenter
        mainPresenter = new MainPresenter(this);
        //调用Presenter方法加载数据
        mainPresenter.loadData();

        ...
    }

    @Override
    public void updateUI() {
        Toast.makeText(this, "Dagger2", Toast.LENGTH_SHORT).show();
    }
}

public class MainPresenter {
    //MainView是个接口
    private MainView mView;

    MainPresenter(MainView view) {
        mView = view;
    }

    public void loadData() {
        //调用model层方法,加载数据
        ...
        //回调方法成功时
        mView.updateUI();
    }
}

这样ActivityPresenter紧紧耦合在了一起,当需要改变Presenter的构造方式时,需要修改这里的代码。如果用依赖注入的话,是这样的:

public class MainActivity extends AppCompatActivity implements MainView {
    @Inject
    MainPresenter mainPresenter;
    ...

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

        DaggerMainComponent.builder()
                .mainModule(new MainModule(this))
                .build()
                .inject(this);
        //调用Presenter方法加载数据
        mainPresenter.loadData();

        ...
    }

    @Override
    public void updateUI() {
        Toast.makeText(this, "Dagger2", Toast.LENGTH_SHORT).show();
    }
}
public interface MainView {
    void updateUI();
}
public class MainPresenter {
    //MainView是个接口
    private MainView mView;

    @Inject
    MainPresenter(MainView view) {
        mView = view;
    }

    public void loadData() {
        //调用model层方法,加载数据
        ...
        //回调方法成功时
        mView.updateUI();
    }
}
@Module
public class MainModule {
    private final MainView mView;

    public MainModule(MainView view) {
        mView = view;
    }

    @Provides
    MainView provideMainView() {
        return mView;
    }
}
@Component(modules = MainModule.class)
public interface MainComponent {
    void inject(MainActivity activity);
}

直接组合方式虽然简单,但是具有耦合性,为了解决这种耦合,可能就会多产生一些辅助类,让这种直接的依赖关系,变为间接,降低耦合。跟大多数设计模式一样,为了达到高内聚低耦合,往往会有很多接口与类,Daager2也是如此,虽然看似复杂了些,不过这在软件工程中是值得的。

先看MainActivity里的代码,之前是直接声明MainPresenter,现在在声明的基础上加了一个注解@Inject,表明MainPresenter是需要注入到MainActivity中,即MainActivity依赖于MainPresenter,这里要注意的是,使用@Inject时,不能用private修饰符修饰类的成员属性

然后我们在MainPresenter的构造函数上同样加了@Inject注解。这样MainActivity里的mainPresenter与他的构造函数建立了某种联系。这种联系可以这样理解,当看到某个类被@Inject标记时,就会到它的构造方法中,如果这个构造方法也被@Inject标记的话,就会自动初始化这个类,从而完成依赖注入。

然后,他们之间并不会凭空建立起联系,肯定需要一个桥梁,将他们连接起来,也就是下面要介绍的Component

Component是一个接口或者抽象类,用@Component注解标注(这里先不管括号里的modules),我们在这个接口中定义了一个inject()方法,参数是Mainactivity。然后rebuild一下项目,会生成一个以Dagger为前缀的Component类,这里是DaggerMainComponent,然后在MainActivity里完成注册:

DaggerMainComponent.builder()
        .mainModule(new MainModule(this))
        .build()
        .inject(this);

此时Component就将@Inject注解的mainPresenter与其构造函数联系了起来。

完成Presenter的注入过程,发现还有一个MainModule类,MainModlue是一个注解类,用@Module注解标注,主要用来提供依赖。刚才通过@Inject就可以完成依赖,为什么这里还要用到Module类来提供依赖?之所以有Module类主要是为了提供那些没有构造函数的类的依赖,这些类无法用@Inject标注,比如第三方类库,系统类,以及上面示例的View接口。

我们在MainModule类里声明了MainView成员属性,在构造方法里将外界传进来的view赋值给mView,并通过一个@Provides标注的以provide开头的方法,将这个view返回,这个以provide开头的方法就是提供依赖的,我们可以创建多个方法来提供不同的依赖。那么这个类究竟是怎么作用的?可以想到上面提到的@Component注解括号里的参数:

@Component(modules = MainModule.class)
public interface MainComponent {
    void inject(MainActivity activity);
}

所以Module要发挥作用,还是要依靠于Component类,一个Component类可以包含多个Module类,用来提供依赖。我们接着看下面这段代码:

DaggerMainComponent.builder()
        .mainModule(new MainModule(this))
        .homeModulw(new HomeModule(this))
        .build()
        .inject(this);

这里通过new MainModule(this)view传递到MainModule里,然后MainModule里的provideMainView()方法返回这个View,当去实例化MainPresenter时,发现构造函数有个参数,此时会在Module里查找提供这个依赖的方法,将该View传递进去,这样就完成了presenterView的注入。

Dagger2注入总结

我们来重新理一遍上面的注入过程,首先弄清楚以下几个概念:

  • @Inject 带有此注解的属性或构造方法将参与到依赖注入中,Dagger2会实例化有此注解的类
  • @Module 带有此注解的类,用来提供依赖,里面定义一些用@Provides注解的以provide开头的方法,这些方法就是提供所需的依赖,Dagger2会在该类中寻找实例化某个类所需要的依赖
  • @Component 用来将@Inject@Module联系起来的桥梁,从@Module中获取依赖并将依赖注入给@Inject

接着我们重新回顾一下上面的注入过程:首先MainActivity需要依赖MainPresenter,因此,我们在MainActivity中用@InjectMainPresenter进行标注,表明这是要注入的类。然后,我们对MainPresenter的构造函数也添加注解@Inject,此时构造函数里有一个参数MainView,因为MainPresenter需要依赖MainView,所以我们定义了一个类,叫做MainModule,提供一个方法provideMainView,用来提供这个依赖,这个MainView是通过MainModule的构造函数注入进来的,接着我们需要定义Component接口类,并将Module包含进来,即

@Component(modules = MainModule.class)
public interface MainComponent {
    void inject(MainActivity activity);
}

Dagger2注入原理

Dagger2与其他依赖注入框架不同,它是通过APT即注解处理器(Annotation Processing Tool)插件在编译阶段生成相应的注入代码,执行Build ProjectDagger2会在app/build/generated/ap_generated_sources/debug/out目录下生成对应的工厂类:

  • MainPresenter_Factory.java
  • MainModule_ProvideMainViewFactory.java
  • DaggerMainComponent.java
  • MainActivity_MembersInjector.java

构造方法 @Inject

MainPresenter_Factory是类MainPresenter的构造方法使用@Inject标注后构建生成的工厂类:

public class MainPresenter {
    //MainView是个接口
    private MainView mView;

    @Inject
    MainPresenter(MainView view) {
        mView = view;
    }
}
public final class MainPresenter_Factory implements Factory<MainPresenter> {
    private final Provider<MainView> viewProvider;

    public MainPresenter_Factory(Provider<MainView> viewProvider) {
        this.viewProvider = viewProvider;
    }

    @Override
    public MainPresenter get() {
        return newInstance(viewProvider.get());
    }

    public static MainPresenter_Factory create(Provider<MainView> viewProvider) {
        return new MainPresenter_Factory(viewProvider);
    }

    public static MainPresenter newInstance(MainView view) {
        return new MainPresenter(view);
    }
}

@Provides

MainModule_ProvideMainViewFactory是类MainModuleprovideMainView()方法使用@Provides标注后构建生成的工厂类:

@Module
public class MainModule {
    private final MainView mView;

    public MainModule(MainView view) {
        mView = view;
    }

    @Provides
    MainView provideMainView() {
        return mView;
    }
}
public final class MainModule_ProvideMainViewFactory implements Factory<MainView> {
    private final MainModule module;

    public MainModule_ProvideMainViewFactory(MainModule module) {
        this.module = module;
    }

    @Override
    public MainView get() {
        return provideMainView(module);
    }

    public static MainModule_ProvideMainViewFactory create(MainModule module) {
        return new MainModule_ProvideMainViewFactory(module);
    }

    public static MainView provideMainView(MainModule instance) {
        return Preconditions.checkNotNull(instance.provideMainView(), "Cannot return null from a non-@Nullable @Provides method");
    }
}

这里不难发现一种对应关系,在MainModule中定义的@Provides修饰的方法都会对应的生成一个对应工厂类,这里是MainModule_ProvideMainViewFactory

@Component

DaggerMainComponent是类MainComponent使用@Component标注后构建生成的辅助类,@Component是连接@Module@Inject的桥梁,主要作用是执行inject方法,为MainPresenter构造方法赋值参数MainView

@Component(modules = MainModule.class)
public interface MainComponent {
    void inject(MainActivity activity);
}
public final class DaggerMainComponent implements MainComponent {
    private final MainModule mainModule;

    private DaggerMainComponent(MainModule mainModuleParam) {
        this.mainModule = mainModuleParam;
    }

    public static Builder builder() {
        return new Builder();
    }

    private MainPresenter getMainPresenter() {
        return new MainPresenter(MainModule_ProvideMainViewFactory.provideMainView(mainModule));
    }

    @Override
    public void inject(MainActivity activity) {
        injectMainActivity(activity);
    }

    private MainActivity injectMainActivity(MainActivity instance) {
        MainActivity_MembersInjector.injectMainPresenter(instance, getMainPresenter());
        return instance;
    }

    public static final class Builder {
        private MainModule mainModule;

        private Builder() {
        }

        public Builder mainModule(MainModule mainModule) {
            this.mainModule = Preconditions.checkNotNull(mainModule);
            return this;
        }

        public MainComponent build() {
            Preconditions.checkBuilderRequirement(mainModule, MainModule.class);
             return new DaggerMainComponent(mainModule);
        }
    }
}

引用注入 @Inject

MainActivity_MembersInjector是类MainActivitymainPresenter成员变量使用@Inject标注后构建生成的辅助类,主要作用是初始化mainPresenter

public class MainActivity extends AppCompatActivity implements MainView {

    @Inject
    MainPresenter mainPresenter;

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

        DaggerMainComponent.builder()
                .mainModule(new MainModule(this))
                .build()
                .inject(this);
        //调用Presenter方法加载数据
        mainPresenter.loadData();
    }

    @Override
    public void updateUI() {
        Toast.makeText(this, "AppBlog.CN", Toast.LENGTH_SHORT).show();
    }
}
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
    private final Provider<MainPresenter> mainPresenterProvider;

    public MainActivity_MembersInjector(Provider<MainPresenter> mainPresenterProvider) {
        this.mainPresenterProvider = mainPresenterProvider;
    }

    public static MembersInjector<MainActivity> create(
              Provider<MainPresenter> mainPresenterProvider) {
        return new MainActivity_MembersInjector(mainPresenterProvider);
    }

    @Override
    public void injectMembers(MainActivity instance) {
        injectMainPresenter(instance, mainPresenterProvider.get());
    }

    @InjectedFieldSignature("me.yezhou.dagger2.MainActivity.mainPresenter")
    public static void injectMainPresenter(MainActivity instance, MainPresenter mainPresenter) {
        instance.mainPresenter = mainPresenter;
    }
}

调用流程

(1)inject调用

MainActivity

DaggerMainComponent.builder()
        .mainModule(new MainModule(this))
        .build()
        .inject(this);

DaggerMainComponent

@Override
public void inject(MainActivity activity) {
    injectMainActivity(activity);
}
private MainActivity injectMainActivity(MainActivity instance) {
    MainActivity_MembersInjector.injectMainPresenter(instance, getMainPresenter());
    return instance;
}

(2)构建MainPresenter,传入MainView

DaggerMainComponent

private MainPresenter getMainPresenter() {
    return new MainPresenter(MainModule_ProvideMainViewFactory.provideMainView(mainModule));
}

(3)通过MainModule构建MainView

MainModule_ProvideMainViewFactory

public static MainView provideMainView(MainModule instance) {
    return Preconditions.checkNotNull(instance.provideMainView(), "Cannot return null from a non-@Nullable @Provides method");
}

MainModule

@Provides
MainView provideMainView() {
    return mView;
}

(4)初始化MainActivity中的mainPresenter

MainActivity_MembersInjector

@InjectedFieldSignature("me.yezhou.dagger2.MainActivity.mainPresenter")
public static void injectMainPresenter(MainActivity instance, MainPresenter mainPresenter) {
    instance.mainPresenter = mainPresenter;
}

版权声明:
作者:Joe.Ye
链接:https://www.appblog.cn/index.php/2023/03/29/getting-started-with-dagger2/
来源:APP全栈技术分享
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
打赏
海报
Dagger2使用入门
Dagger2简介 Dagger2是Dagger的升级版,是一个依赖注入框架,现在由Google接手维护。 Github:https://github.com/google/dagger 依赖注入是面向对象编程的一……
<<上一篇
下一篇>>
文章目录
关闭
目 录