本次使用说明是以 GitHub 上 Dagger 仓库 2.15版本来进行的分析,同时以官方网站文档作为参考。如后续版本有较大改变,恕不另行修改
Dagger 在项目中的使用
前两篇中我们了解 Dagger 的基本用法和高级用法,本篇中,我们通过一些开源项目和我司自己的项目为例来说下具体的使用。 通常我们在开发一个App时,有全局单例的变量,例如 AccountMgr 用来管理账户信息相关,DataMgr 负责管理本地数据和网络请求数据。这些变量往往是不会随着不同 Presenter 请求信息而改变,通常是在 App 初始化的时候,他们就构造完成。还有局部单例变量,最常见的就是每个 View 层配套的 Presenter 变量。使用上篇说的 @Singleton 和 @Scope 可以很好的实现这样的功能。 因此我们可以把全局变量定义到 AppComponent ,并在相应的依赖的 module 来提供单例。
@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
ApiClient apiClient();
AccountMgr accountMgr();
@Component.Builder
interface Builder{
@BindsInstance
AppComponent.Builder app(AppParent app);
AppComponent build();
}
}
@Module
public class AppModule {
@Provides
@Singleton ApiClient getApiClient(AccountMgr accountMgr){
...
return new ApiClient();
}
@Provides
@Singleton AccountMgr getAccountMgr(SharedPreferences sharedPreferences){
return new AccountMgr();
}
}
这里面 @BindsInstance 是用来代替有参数的 AppModule 的构造函数。
当前开源项目中使用Dagger的两种方式
上面说的是针对App的全局的单例变量,而对于局部的单例变量,会有两种常见的实现方式。上一篇最后的部分我们分析了 Subcomponent 与 Component.dependencies 的区别。而大多依赖注入也是通过这两种方式来进行的。
以 Subcomponent 方式
Subcomponent 我们说过,是不会产生 DaggerXXXComponent,而是定义的 Subcomponent.Builder 由 Component 调用相应的 build() 来获得相应 SubComponent Impl实例。
@ActivityScope
@Subcomponent(
modules = SplashActivityModule.class
)
public interface SplashActivityComponent {
void inject(SplashActivity splashActivity);
@Subcomponent.Builder
interface Builder{
SplashActivityComponent build();
}
}
@Module
public class SplashActivityModule {
private SplashActivity splashActivity;
public SplashActivityModule(SplashActivity splashActivity) {
this.splashActivity = splashActivity;
}
@Provides
@ActivityScope
SplashActivity provideSplashActivity() {
return splashActivity;
}
@Provides
@ActivityScope
SplashActivityPresenter
provideSplashActivityPresenter() {
return new SplashActivityPresenter();
}
}
@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
SplashActivityComponent.Builder splashActivityComponent();
...
}
这样每个 SubComponent 都是“继承” AppComponent。不过我感觉这样并不好,因为从逻辑意义上,SubComponent 和 AppComponent 并不是一样的东西,只是 SubComponent 使用了一些全局单例变量,所以我个人感觉使用 Component.dependencies 来定义的各个子Component更为恰当一些。
以 Component.dependencies 方式
此种方法,可能使用起来更为广泛。
@ActivityScope
@Component(dependencies = AppComponent.class, modules = SplashActivityModule.class)
public interface SplashActivityComponent {
void inject(SplashActivity activity);
}
@Module
public class SplashActivityModule {
private SplashActivity splashActivity;
public SplashActivityModule(SplashActivity splashActivity) {
this.splashActivity = splashActivity;
}
@Provides
@ActivityScope
SplashActivity provideSplashActivity() {
return splashActivity;
}
@Provides
@ActivityScope
SplashActivityPresenter
provideSplashActivityPresenter() {
return new SplashActivityPresenter();
}
}
此种方式是使用 AppComponent 提供的 ApiManager 等进行相应的操作。同样,我们可进行相应的抽象操作:
@ActivityScope
@Component(dependencies = AppComponent.class, modules = { ActivityModule.class, SplashActivityModule.class})
public interface ActivityComponent {
Activity getActivity();
}
这样每个Activity的Component都可以继承 ActivityComponent 来进行相应的注入。
关于框架的几点思考
总的来说,关于 Dagger 在 App 中的使用就是上面几点。但是如果使用Dagger 肯定是希望在框架层面上做一些事情的,那么就此延申出来,我们可以思考一些事情:
是否有使用 Dagger 的必要
首先第一点,我觉得还是有必要的,首先不用 hard init 有利于解耦。其实,这里的解耦描述并不准确,更多的是将耦合转移。例如你在View 层面 new 一个 Presenter,相当于 hard init Presenter,而如果你是用了 Module的 Provider 样式,则相当于将 View 和 Presenter 的耦合转业到了 Module 和 Presenter,不过仅这样也是有提升的,毕竟 View 中不需要作相应的修改,那里还是以业务逻辑为主,无需关心Presenter 具体怎么变化,只要其接口不会修改就行。还有,我觉得认为没有必要的开发者,可能并没有学会使用Dagger 方法,才导致他们认为没有必要。
Component 的抽象要怎么做才更合适
第二点,说到 Component ,我们默认使用 Component.dependencies 的形式来进行,并且进行抽象设计:
@ActivityScope
@Component(dependencies = AppComponent.class, modules = ActivityModule.class)
public interface ActivityComponent {
Activity getActivity();
void inject(WelcomeActivity welcomeActivity);
}
@Module
public class ActivityModule {
private Activity mActivity;
public ActivityModule(Activity activity) {
this.mActivity = activity;
}
@Provides
@ActivityScope
public Activity provideActivity() {
return mActivity;
}
}
这里我们以 GeekNews 来进行举例,作者抽象出 ActivityComponent ,并定义了一个 BaseActivity 在 onCreate 方法中进行注入。这样我认为是一个好方法,所有继承了 BaseActivity 的Activity 可在初始化时调用 ActivityComponent.inject(this) 方法实现相应的注入,同时不会因为使用 SubComponent 造成每个 Activity 都有唯一的 SubComponent 实例来进行注入,保持了每个 Activity 注入的统一性。同样我们看到这个ActivityComponent 只有唯一的一个 ActivityModule ,而其内部只是提供了同样的 poviderActivity 方法。
其各个 Presenter 都是由构造函数加 @Inject 注解,来生成的依赖,同样在 BaseActivity 直接持有一个相应 Presenter 的引用。我们以其WelcomActivity 为例来看一下,作者思路。
public abstract class BaseActivity<T extends BasePresenter> extends SimpleActivity implements BaseView {
@Inject
protected T mPresenter;
...
//在注入成功后将View 手动绑定到 Presenter上
@Override
protected void onViewCreated() {
super.onViewCreated();
initInject();
if (mPresenter != null)
mPresenter.attachView(this);
}
//View Destroy时,同时将 Presenter 与 View 解绑
@Override
protected void onDestroy() {
if (mPresenter != null)
mPresenter.detachView();
super.onDestroy();
}
}
//module 不提供给相应的 Provider 函数,由构造函数进行注入
public class WelcomePresenter extends RxPresenter<WelcomeContract.View> implements WelcomeContract.Presenter{
...
@Inject
public WelcomePresenter(DataManager mDataManager) {
this.mDataManager = mDataManager;
}
//持有Presenter 类型通过 泛型传入
public class WelcomeActivity extends BaseActivity<WelcomePresenter> implements WelcomeContract.View {
}
作者这样写其实做了个假设:
每个 View 是和唯一的 Presenter 进行绑定。
针对这个项目没有问题,但是不一定具有通用性。例如一个场景是你可以控制多个对象,同时又可以对每个对象进行单独操作。那么这里一个Presenter 明显是不合适的,一个 Presenter 要对不同对象进行控制,添加,删除,置顶等操作,另一个 Presenter 针对每一个对象进行相应的操作,修改,命令。因此,Presenter 不封装到 BaseActivity 中更为合适。
然后我们看其 ActivityComponent 的 ActivityModule 是一个通用的,不是针对每个 Activty 提供不同的 Presenter,这里其实做了另一个假设:
每个 View 中只有唯一 Presenter 会需要注入依赖,没有别的依赖需要注入。
针对第二个假设,则更不合适,一个 Activity 中可能有 Fragment Adapter Presenter 以及一些别的依赖需要注入,并非所有的依赖都可以使用 @Inject 修饰构造函数来生成依赖实例,所以我认为上述做法并不友好。
还有一点就是 Presenter 与 View 的绑定时机,我个人认为也是有待商榷的。作者是在注入 Presenter 后,手动将 View attach 到 Presenter 上,这样做确实也没什么问题,但是不符合控制反转思路,因为 Presenter 可以理解是由于 View 主动调用创建的,应该让 Presenter 给一个回调通知 View 说明已经创建完成,可以绑定。因此我觉得将 View 当作参数传入 Presenter,同时进行绑定更为合理一些。
// 所有 Presenter 的实现基类
public abstract class BasePresenter<T extends BaseContract.View>
implements BaseContract.Presenter {
final protected ApiClient mApiClient;
final protected AccountMgr mAccountMgr;
...
public BasePresenter(ApiClient apiClient, AccountMgr accountMgr, T view) {
mApiClient = apiClient;
mAccountMgr = accountMgr;
mView = view; //创建的同时直接绑定
}
@Override
public void onDestroy() {
this.mView = null; //View 在 Destroy 时进行解绑
}
}
//SplashModule 提供 SplashPresenter
@Module
public class SplashModule {
@Provides
@ActivityScope
public SplashContract.Presenter providePresenter(ApiClient apiClient, BaseContract.View view, AccountMgr accountMgr) {
if (view instanceof SplashContract.View)
return new SplashPresenter(apiClient, (SplashContract.View)view, accountMgr);
return null;
}
}
public class SplashActivity extends BaseInjectActivity implements SplashContract.View {
@Inject SplashContract.Presenter mPresenter;
// 通用注入,注入完成后 开始操作 Presenter
@Override protected void onViewCreated(Bundle savedInstanceState) {
super.onViewCreated(savedInstanceState);
mActivityComponent.inject(this);
mPresenter.start();
}
// onDestroy 时进行解绑
@Override protected void onDestroy() {
super.onDestroy();
mPresenter.onDestroy();
}
}
//Presenter 的实现类
public class SplashPresenter extends BasePresenter<SplashContract.View>
implements SplashContract.Presenter {
public SplashPresenter(ApiClient apiClient, SplashContract.View view, AccountMgr accountMgr) {
super(apiClient,accountMgr, view);
}
}
主要就是改了两个部分:1. Presenter 的注入从基类提出;2. 将 View 作为参数传入 Presenter 的构造函数中,实现创建即绑定。
Module 中放入哪些实现
针对 ActivityModule (这里是泛指每个 Activity 的 Module,非抽象出的 ActivityModlue)应该提供哪些实现,这个应该是根据不同的情况进行区别,这里给出几个例子。
@Module public class ActivityModule {
private Activity mActivity;
public ActivityModule(Activity activity) {
this.mActivity = activity;
}
//提供 RxPermissions
@Provides
@ActivityScope
public RxPermissions provideRxPermission() {
return new RxPermissions(mActivity);
}
//提供 Presenter
@Provides
@ActivityScope
public Presenter providePresenter(BaseContract.View view) {
return new ImplPresenter(view)
}
//提供 Adapter
@Provides
@ActivityScope
public Adapter povideChildAdapter(Activity activity) {
Adapter adapter = new Adapter();
return adapter;
}
//提供 传入参数
@Provides
@ActivityScoped
public String provideTaskId() {
return mActivity.getIntent().getStringExtra(EXTRA_STR);
}
}
当然上面的写到比较简略,不过就是这个意思。
后记
总体来说 Dagger 的学习成本较高,但是使用熟练的话,解耦的效果还是不错的,同时使 App 的业务逻辑更为清晰,不需要一堆构造函数。我个人比较推荐将依赖的生成都提供在Module中,哪怕是最简单 new 一个变量,因为更清楚同时也不会修改依赖类。