Daggger2 使用姿势及源码分析

栏目: 后端 · 发布时间: 7年前

内容简介:Daggger2 使用姿势及源码分析

最近计划摸索一下Dagger2在模块化开发中的应用,在讲述其模块化应用之前,先总结一下Dagger2,主要从两方面来说明:Dagger2的使用姿势和Dagger2自动生成的源码分析。

[TOC]

Dagger2使用姿势

主要结合Google官方todo+MVP+Dagger2 和自己的项目经验来讲述dagger2的使用姿势。

定义Module

Module 是类实例提供的工厂模式,Module里面的方法基本都是创建类实例的方法。这样,Dagger2中就有2个维度可以创建类实例: • 通过用Inject注解标注的构造函数来创建(以下简称Inject维度) • 通过工厂模式的Module来创建(以下简称Module维度)

关于Module维度,以Google例子为例:

1.ApplicationModuleApplicationModule提供了Context的实例注入方式

@Module public final class ApplicationModule {      private final Context mContext;      ApplicationModule(Context context) {         mContext = context;     }      @Provides     Context provideContext() {         return mContext;     } }

2.TasksRepositoryModuleTasksRepositoryModule提供了两种TasksDataSource的实例注入方式

@Module public class TasksRepositoryModule {      @Singleton     @Provides     @Local     TasksDataSource provideTasksLocalDataSource(Context context) {         return new TasksLocalDataSource(context);     }      @Singleton     @Provides     @Remote     TasksDataSource provideTasksRemoteDataSource() {         return new FakeTasksRemoteDataSource();     } }

3.TasksRepositoryTasksRepository采用的是Inject维度来定义提供实例,其以Singleton(单例声明),并采用inject的方式来构造,在创建实例的时候,会自动从依赖的Module,即TasksRepositoryModule获取所需的实例。

@Singleton public class TasksRepository implements TasksDataSource {      private final TasksDataSource mTasksRemoteDataSource;      private final TasksDataSource mTasksLocalDataSource;      ...      @Inject     TasksRepository(@Remote TasksDataSource tasksRemoteDataSource,             @Local TasksDataSource tasksLocalDataSource) {         mTasksRemoteDataSource = tasksRemoteDataSource;         mTasksLocalDataSource = tasksLocalDataSource;     }      ...   }

4.TasksPresenterModuleTasksPresenterModule应用于TasksActivity, 提供了TasksContract.View的实例注入方式

@Module public class TasksPresenterModule {      private final TasksContract.View mView;      public TasksPresenterModule(TasksContract.View view) {         mView = view;     }      @Provides     TasksContract.View provideTasksContractView() {         return mView;     } }

定义Component

Component 要解决的问题就是Inject的实例从哪里来,所以它承担的就是一个连接器的作用。Component需要引用到目标类的实例,Component会查找目标类中用Inject注解标注的属性,查找到相应的属性后会接着查找该属性对应的用Inject标注的构造函数(这时候就发生联系了),剩下的工作就是初始化该属性的实例并把实例进行赋值。

1.TasksRepositoryComponentTasksRepositoryComponent属于ApplicationComponent的范畴,全局唯一,包含了TasksRepositoryModule 和 ApplicationModule,意味着仅能通过TasksRepositoryComponent注入以上两个Module中的实例

@Singleton @Component(modules = {TasksRepositoryModule.class, ApplicationModule.class}) public interface TasksRepositoryComponent {      TasksRepository getTasksRepository(); }

2.TasksComponentTasksComponent依赖了TasksRepositoryComponent,这就使得TasksComponent继承了TasksRepositoryComponent的注入能力,此外还指向了TasksPresenterModule

@FragmentScoped @Component(dependencies = TasksRepositoryComponent.class, modules = TasksPresenterModule.class) public interface TasksComponent {      void inject(TasksActivity activity); }

Inject注入

Inject,即注入,该注解标示地方表示需要通过DI框架来注入实例。Inject有三种方式,分别是Constructor injection、Fields injection、Methods injection。申明了Inject之后,会从注入框架中去查找需要注入的类实例,然后注入进来,也就是通过Component去查找。

1.创建Component实例

以TasksRepositoryComponent为例,在Application的 OnCreate方法中创建:

public class ToDoApplication extends Application {      private TasksRepositoryComponent mRepositoryComponent;      @Override     public void onCreate() {         super.onCreate();          mRepositoryComponent = DaggerTasksRepositoryComponent.builder()                 .applicationModule(new ApplicationModule((getApplicationContext())))                 .build();      }      public TasksRepositoryComponent getTasksRepositoryComponent() {         return mRepositoryComponent;     }  }

以TasksComponent为例:

DaggerTasksComponent.builder()                 .tasksRepositoryComponent(((ToDoApplication) getApplication()).getTasksRepositoryComponent())                 .tasksPresenterModule(new TasksPresenterModule(tasksFragment)).build()                 .inject(this);

2.通过Inject方式在目标类中注入实例,并使用

以TasksRepositoryComponent为例,我们可以通过((ToDoApplication) getApplication()).getTasksRepositoryComponent().getTasksRepository(); 方式来创建TasksRepository。

但是,更多的时候,我们还是通过Inject来实现Fields injection、Methods injection。

在Google的例子中,TasksActivity的注入方式如下:

public class TasksActivity extends AppCompatActivity {      @Inject      TasksPresenter mTasksPresenter;      @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);            // Create the presenter         DaggerTasksComponent.builder()                 .tasksRepositoryComponent(((ToDoApplication) getApplication()).getTasksRepositoryComponent())                 .tasksPresenterModule(new TasksPresenterModule(tasksFragment)).build()                 .inject(this);     } }

有读者就会疑问,它是如何注入TasksPresenter的,我们并没有在任何Module中有看到Provide了TasksPresenter的创建方法。确实,这个地方采用的是我前面提到的“Inject维度”来提供注入实例的方式,代码如下:

final class TasksPresenter implements TasksContract.Presenter {      /**      * Dagger strictly enforces that arguments not marked with {@code @Nullable} are not injected      * with {@code @Nullable} values.      */     @Inject     TasksPresenter(TasksRepository tasksRepository, TasksContract.View tasksView) {         mTasksRepository = tasksRepository;         mTasksView = tasksView;     }      /**      * Method injection is used here to safely reference {@code this} after the object is created.      * For more information, see Java Concurrency in Practice.      */     @Inject     void setupListeners() {         mTasksView.setPresenter(this);     }  }

所以,当Inject TasksPresenter的时候,会找到TasksPresenter的构造方式,而它的构造方法采用的是Inject方式注入参数,于是会注入TasksRepository、TasksContract.View这两个实例,从而创建成功。

总结下来,Dagger2的使用姿势大抵来说就是上面三个步骤,而在使用中,又可以通过Singleton, Provides,Scope,Qualifier来丰富上述三个步骤。这几个标注在google project中都有应用,细节我就不再赘述。至于Dagger2具体如何注入,我会在下一节做重点分析。

Dagger2源码分析

这一节主要解决两个问题:

  1. Dagger2自动生成了什么东西?
  2. Dagger2如何实现依赖注入

Dagger2自动生成了什么东西

依赖注入,我们知道,它本质上就是采用某种工厂模式去创建实例,并未目标类提供实例。以这个思路去看Dagger2自动生成的源码,就会豁然开朗。

分析源码,你会发现主要有以下几个概念:

连接器Component以TasksRepositoryComponent为例,Dagger2自动生成了其实现类DaggerTasksRepositoryComponent,其主要完成以下几个事情:

  1. 通过DaggerTasksRepositoryComponent.Builder创建component, Builder传入component依赖的module;
  2. Component初始化,为Module中的每一个实例创建Provider<T>;
  3. 为Provider<T>生成对应的工厂方法;
  4. 重载component接口中方法,通过调用Provider<T>
Daggger2 使用姿势及源码分析

component.jpeg

提供者 ProviderModule中的每个@provide都会自动生成一个Provider相对应,Provider顾名思义就是实例的提供者,其结构如下,核心就是get()方法,在它的实现类中,重载get()并返回实例:

public interface Provider<T> {     T get(); }

以ApplicationModule中的provideContext()为例,DaggerTasksRepositoryComponent自动生成了provideContextProvider,在component构建的时候,会初始化provideContextProvider,如下:

private Provider<Context> provideContextProvider;  this.provideContextProvider =ApplicationModule_ProvideContextFactory.create(builder.applicationModule);

这里,你会发现,Provider是通过ApplicationModule_ProvideContextFactory来get()实例,这个Factory我把它叫做工厂生产者。

工厂生产者Factory在Module中,提供了实例的创建方法,在Dagger2生成的源码中,所有的实例都会生成对应的Factory,注入的时候都是通过Factory来创建实例,虽然,本质上还是调用了Module中的provider方法。

以ApplicationModule_ProvideContextFactory为例:

Daggger2 使用姿势及源码分析

factory.png

成员注入器 MembersInjector如上所述,Inject有三种方式注入,这里主要讲述一下Fields injection,并以上文提到的在TasksActivity注入@Inject TasksPresenter mTasksPresenter;为例。

在DaggerTasksComponent,已经生成有Provider<TasksPresenter> tasksPresenterProvider; 为了实现注入,还生成了一个注入器 MembersInjector<TasksActivity> tasksActivityMembersInjector;这个注入器的作用就是往TasksActivity注入@Inject声明的东西,这里只有mTasksPresenter需要注入。

注入器结构如下:

public interface MembersInjector<T> {   void injectMembers(T instance); }

在DaggerTasksComponent中的源码如下:

public final class DaggerTasksComponent implements TasksComponent {    private Provider<TasksPresenter> tasksPresenterProvider;    private MembersInjector<TasksActivity> tasksActivityMembersInjector;     private void initialize(final Builder builder) {     。。。     this.tasksPresenterProvider =         TasksPresenter_Factory.create(             tasksPresenterMembersInjector,             getTasksRepositoryProvider,             provideTasksContractViewProvider);       this.tasksActivityMembersInjector =          TasksActivity_MembersInjector.create(tasksPresenterProvider);    }     @Override    public void inject(TasksActivity activity) {      tasksActivityMembersInjector.injectMembers(activity);    }  }

从以上的源码,可以发现亮点:

  1. 注入都是通过注入器来实现的;
  2. 注入是发生在调用inject的时候

查看TasksActivity_MembersInjector你会发现,它在构造的时候需要传入待注入的实例的Provider,上例就需要传入tasksPresenterProvider。

Daggger2 使用姿势及源码分析

injector.png

Dagger2如何实现依赖注入

鉴于本文篇幅有限,另外两种注入Constructor injection、Methods injection我不在此处讲述,会在其他文章中进行。有兴趣的读者可以保持关注

结语写完该文章,细细阅读,发现还有很多写的不尽人意的地方,很多细节之处也讲的不够详细,在此,也建议用户按照我的思路,重点还得结合源码再去细细品味。祝好


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Inside Larry's and Sergey's Brain

Inside Larry's and Sergey's Brain

Richard Brandt / Portfolio / 17 Sep 2009 / USD 24.95

You’ve used their products. You’ve heard about their skyrocketing wealth and “don’t be evil” business motto. But how much do you really know about Google’s founders, Larry Page and Sergey Brin? Inside......一起来看看 《Inside Larry's and Sergey's Brain》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具