前面介绍了Dagger2
的基本知识,并且通过示例代码演示了如何在Android
开发中去使用Dagger2
。
Dagger2
可以减少很多模板化的代码,更易于测试、降低耦合度,创建可复用可交换的模板。
Dagger2
优点:
提供了对全局对象实例的简单访问方式
声明了单例的实例都可以通过@Inject
进行访问。比如下面的MyTwitterApiClient
和SharedPreferences
的实例:
public class MainActivity extends Activity {
@Inject MyTwitterApiClient mTwitterApiClient;
@Inject SharedPreferences sharedPreferences;
public void onCreate(Bundle savedInstance) {
// assign singleton instances to fields
InjectorClass.inject(this);
}
}
复杂的依赖关系只需要简单的配置
Dagger2
会通过依赖关系并且生成易懂易分析的代码。以前通过手写的大量模板代码中的对象引用将会由它给你创建并传递到相应对象中。
因此你可以更多的关注模块中构建的内容而不是模块中的对象实例的创建顺序。
作用域实例
我们不仅可以轻松的管理全局实例对象,也可以使用Dagger2
中的scope
定义不同的作用域。(比如根据user session
,activity
的生命周期)
在之前写的一篇文章Android开发中的MVP模式详解中,讲到了Google
官方的android-architecture,下面就从前面这篇文章的基础往下说。android-architecture
里面有一个分支是todo-mvp-dagger
。MVP
和Dagger2
搭配,开发不累。
我们在没使用dagger
之前,进行mvp
开发的时候,是不是需要创建view
和presenter
啊,然后让他俩分别持有对方的对象。
那使用dagger
后该怎么实现呢?
我们可以看到和之前的区别就是:
di
包,这里面都是动态注入相关的类:有全局的ApplicationModule
、AppComponent
、ActivityBindingModule
和自定义的Scope
每个页面都多了一个对应的XXXModule
然后ToDoApplication
实现了DaggerApplication
就从ToDoApplication
开始分析吧:
public class ToDoApplication extends DaggerApplication {
@Inject
TasksRepository tasksRepository;
@Override
protected AndroidInjector extends DaggerApplication> applicationInjector() {
return DaggerAppComponent.builder().application(this).build();
}
/**
* Our Espresso tests need to be able to get an instance of the {@link TasksRepository}
* so that we can delete all tasks before running each test
*/
@VisibleForTesting
public TasksRepository getTasksRepository() {
return tasksRepository;
}
}
继承DaggerApplication
并且实现applicationInjector
方法,这是为了让Activity
不用每个都去调用component
的inject
方法。
这里用到了TasksRepository
和AppComponent
,我们去看一下AppComponent
:
@Singleton
@Component(modules = {TasksRepositoryModule.class,
ApplicationModule.class,
ActivityBindingModule.class,
AndroidSupportInjectionModule.class})
public interface AppComponent extends AndroidInjector<ToDoApplication> {
// 提供TaskRepository的注入对象
TasksRepository getTasksRepository();
// Gives us syntactic sugar. we can then do DaggerAppComponent.builder().application(this).build().inject(this);
// never having to instantiate any modules or say which module we are passing the application to.
// Application will just be provided into our app graph now.
@Component.Builder
interface Builder {
@BindsInstance
AppComponent.Builder application(Application application);
AppComponent build();
}
}
这里面会将TasksRepositoryModule
、ApplicationModule
、ActivityBindingModule
都进行绑定。
@Module
public abstract class ApplicationModule {
//expose Application as an injectable context
@Binds
abstract Context bindContext(Application application);
}
@Module
public abstract class ActivityBindingModule {
// 注意下面的这四个`Module`
@ActivityScoped
// TasksModule是给`TasksActivity`提供注入对象的
@ContributesAndroidInjector(modules = TasksModule.class)
abstract TasksActivity tasksActivity();
@ActivityScoped
@ContributesAndroidInjector(modules = AddEditTaskModule.class)
abstract AddEditTaskActivity addEditTaskActivity();
@ActivityScoped
@ContributesAndroidInjector(modules = StatisticsModule.class)
abstract StatisticsActivity statisticsActivity();
@ActivityScoped
@ContributesAndroidInjector(modules = TaskDetailPresenterModule.class)
abstract TaskDetailActivity taskDetailActivity();
}
而TasksRepositoryModule
是在Mock
中:
/**
* This is used by Dagger to inject the required arguments into the {@link TasksRepository}.
*/
@Module
abstract public class TasksRepositoryModule {
private static final int THREAD_COUNT = 3;
@Singleton
@Binds
@Local
abstract TasksDataSource provideTasksLocalDataSource(TasksLocalDataSource dataSource);
@Singleton
@Binds
@Remote
abstract TasksDataSource provideTasksRemoteDataSource(FakeTasksRemoteDataSource dataSource);
@Singleton
@Provides
static ToDoDatabase provideDb(Application context) {
return Room.databaseBuilder(context.getApplicationContext(), ToDoDatabase.class, "Tasks.db")
.build();
}
@Singleton
@Provides
static TasksDao provideTasksDao(ToDoDatabase db) {
return db.taskDao();
}
@Singleton
@Provides
static AppExecutors provideAppExecutors() {
return new AppExecutors(new DiskIOThreadExecutor(),
Executors.newFixedThreadPool(THREAD_COUNT),
new AppExecutors.MainThreadExecutor());
}
}
这是用于mock
测试的一个类,里面的两个方法分别表示本地数据和远程数据,最终返回的都是TasksDataSource
。mock
测试就是在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法。这里对于数据对象直接在这里进行初始化,而不是在所有的用到该数据的地方new
一遍。这也就体现了Dagger2
的引入对测试是一个极大的便利。
上面看到了ActivityBindingModule
中对每个Activity
都声明的Module
,接下来就看TasksModule
:
/**
* This is a Dagger module. We use this to pass in the View dependency to the
* {@link TasksPresenter}.
*/
@Module
public abstract class TasksModule {
@FragmentScoped
@ContributesAndroidInjector
abstract TasksFragment tasksFragment();
@ActivityScoped
@Binds abstract TasksContract.Presenter taskPresenter(TasksPresenter presenter);
}
这里面分别提供了对应View
的Fragment
以及对应Presenter
。
更多文章请查看AndroidNote
你的star
是我的动力!!!