最近一直在关注Android项目开发中的整体架构设计问题, 如何在复杂的业务逻辑和日益膨胀的功能需求面前保持项目架构的稳定 是一个很值得深入学习的问题。前段时间看到开发社区里有不少人在推崇 Dagger2 + MVP 模式来构建整体项目框架,MVP模式之前在项目中有使用过,MVP虽然从Activity和Fragment中抽离出了业务逻辑层,但是也不能完全解耦代码。
而Dagger2的出现正好解决了这一问题,这里不谈Dagger2的使用方法,主要是分享下Dagger2的工作原理和使用中遇到的一些疑惑。
用过Dagger2的同学都知道,只要通过几个注解、Component、Module就可以实现依赖注入,再也不用写 new
了,简直太神奇了。下面就让我们揭开Dagger2神秘的面纱,看看它到底是怎么工作的
@Singleton
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
@ApplicationContext
Context context();
ApiService apiService();
DataManager dataManager();
}
@Module
public class ApplicationModule {
private final Application mApplication;
public ApplicationModule(Application application) {
mApplication = application;
}
@Provides
@ApplicationContext
Context provideContext() {
return mApplication;
}
@Provides
@Singleton
ApiService provideApiService() {
return new ApiService();
}
}
@Singleton
public class DataManager {
private ApiService mApiService;
@Inject
public DataManager(ApiService apiService) {
mApiService = apiService;
}
}
上面代码主要有三部分组成, ApplicationComponent、ApplicationModule、DataManager(ApiService是其中一个成员变量)
在编译时,Dagger2会自动生成 ApplicationComponent
实例类 DaggerApplicationComponent
,如下
@Generated("dagger.internal.codegen.ComponentProcessor")
public final class DaggerApplicationComponent implements ApplicationComponent {
// 在ApplicationModule中使用@Providers注解标识
private Provider<Context> provideContextProvider;
// 在ApplicationModule中使用@Providers注解标识
private Provider<ApiService> provideApiServiceProvider;
// DataManager是通过@Inject注解标识在构造函数上的隐式Provider
private Provider<DataManager> dataManagerProvider;
private DaggerApplicationComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
public static Builder builder() {
return new Builder();
}
private void initialize(final Builder builder) {
// 这里contextProvider实现类是ApplicationModule_ProvideContextFactory,
// 而这个工厂中实际使用的是ApplicationModule
this.provideContextProvider = ApplicationModule_ProvideContextFactory
.create(builder.applicationModule);
// 由于ApplicationModule中provideApiService()方法用了@Singleton作用域标识,
// 所以区别于contextProvider,它用了ScopeProvider来包装了一下,
// 通过查看ScopeProvider源码可以看出,它其实是把
// ApplicationModule_ProvideApiServiceFactory封装了一下,
// 当其未实例化时从factory中get一个实例出来保存其中并返回给调用者,
// 而当它已经实例过下次再调用时,就不会再从factory中get而是直接返
// 回之前保存的实例对象
this.provideApiServiceProvider = ScopedProvider
.create(ApplicationModule_ProvideApiServiceFactory
.create(builder.applicationModule));
// dataManagerProvider有别于apiServiceProvider,
// 前者是通过构造函数提供的隐式provider,
// 而后者是通过ApplicationMoudule用@Provider注解显示提供的provider,
// 两者没有本质上的区别,只是写法上的不一样
this.dataManagerProvider = ScopedProvider
.create(DataManager_Factory.create(provideApiServiceProvider));
}
@Override
public Context context() {
// 这里实际调用的是applicationModule.provideContext()方法
return provideContextProvider.get();
}
@Override
public ApiService apiService() {
// 这里实际调用的是applicationModule.provideApiService()方法
return provideApiServiceProvider.get();
}
@Override
public DataManager dataManager() {
// 这里实际调用的是new DataManager(provideApiServiceProvider.get())
return dataManagerProvider.get();
}
// 这里的Builder类就是暴露给外部代码使用的,
// 通过该Builder传入Component的依赖和Module,并通过build()方法实例Component
public static final class Builder {
private ApplicationModule applicationModule;
private Builder() {
}
public ApplicationComponent build() {
if (applicationModule == null) {
throw new IllegalStateException("applicationModule must be set");
}
return new DaggerApplicationComponent(this);
}
public Builder applicationModule(ApplicationModule applicationModule) {
if (applicationModule == null) {
throw new NullPointerException("applicationModule");
}
this.applicationModule = applicationModule;
return this;
}
}
}
从上面的代码可以看出 ApplicationComponent
构建了一系列 Provider
,而这些 Provider
最后在对象注入时使用的,来看下一下代码
@PerActivity
@Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class)
public interface ActivityComponent {
void inject(MainActivity activity);
}
@Module
public class ActivityModule {
private final Activity mActivity;
public ActivityModule(Activity activity) {
mActivity = activity;
}
@Provides
@ActivityContext
Context providesContext() {
return mActivity;
}
}
public class MainActivity extends BaseActivity {
@Inject
DataManager mDataManager;
......
}
ActivityComponet
是依赖于 ApplicationComponent
,所以它可以获得 ApplicationComponent
构建的一系列Provider, MainActivity
是需要注入的对象,最终 MainActivity.mDataManager
变量会被注入赋值
@Generated("dagger.internal.codegen.ComponentProcessor")
public final class DaggerActivityComponent implements ActivityComponent {
private Provider<DataManager> dataManagerProvider;
private MembersInjector<MainActivity> mainActivityMembersInjector;
private void initialize(final Builder builder) {
// 从这里可以看出dataManagerProvider实际调用的是
// applicationComponent.dataManager()
this.dataManagerProvider = new Factory<DataManager>() {
@Override public DataManager get() {
DataManager provided = builder.applicationComponent.dataManager();
if (provided == null) {
throw new NullPointerException("Cannot return null from a non-@Nullable component method");
}
return provided;
}
};
// 这里是创建了MainActivity注入器,
// 目的是为MainActivity中用@Inject注解标识的成员变量注入实例
this.mainActivityMembersInjector = MainActivity_MembersInjector
.create((MembersInjector) MembersInjectors.noOp(),
dataManagerProvider);
}
@Override
public void inject(MainActivity activity) {
// 当在外部调用该方法时,实际是为mDataManager赋值了,等同于
// activity.mDataManager = dataManagerProvider.get();
mainActivityMembersInjector.injectMembers(activity);
}
}
分析完Dagger2的工作原理后,让我们来看看在实际使用过程中会遇到哪些坑
component 的 inject 函数不要声明基类参数
如 MainActivity
和 SettingActivity
都是继承于 BaseActivity
,那么在注入时只能指定 MainActivity
和 SettingActivity
,而不能想当然的使用基类 BaseActivity
,这样子类是不会被注入的。这样就会带来一个问题,如果说我们很多的Activity都需要注入,那么在 ActivityComponet
中我就需要这么写(是不是很蛋疼 ~_~!!!)
void inject(MainActivity activity);
void inject(LoginActivity activity);
void inject(RegisterActivity activity);
void inject(AccountAuthActivity activity);
void inject(UserInfoEditActivity activity);
void inject(UserStatusActivity activity);
void inject(UserBindRescuerActivity activity);
......
Scope 注解必须用在 module 的 provide 方法上,否则并不能达到局部单例的效果
如果 module 的 provide 方法使用了 scope 注解,那么 component 就必须使用同一个注解,否则编译会失败
如果 module 的 provide 方法没有使用 scope 注解,那么 component 和 module 是否加注解都无关紧要,可以通过编译,但是没有局部单例效果;
对于直接使用 @Inject 构造函数的依赖,如果把 scope 注解放到它的类上,而不是构造函数上,就能达到局部单例的效果了;
至此,Dagger2的整个注入流程我们就分析完了,其实也不算复杂,只是使用了大量的设计模式,分析代码时跳来跳去的容易把人搞晕。Dagger2的出现是解耦了代码,但是同时也带来了其它的一些问题,比如:引入大量Component后期 管理、维护就很非常困难 , Component划分粒度问题 , 当业务逻辑存在重复或者相似性时如何重用Component 等等,这些都是需要进一步学习和思考。
原创不易,欢迎转载,但还请注明出处:blog.waynell.com