原文链接:https://medium.com/azimolabs/dagger-2-on-production-reducing-methods-count-5a13ff671e30#.2gs179pgj
Dagger 2——完全静态,编译时依赖注入框架是Azimo Android应用的代码架构骨干。我们已经知道,随着开发团队的增长,干净的代码结构是每个项目中最重要的事情之一。初始化/使用分离,更以测试(单元或功能),更好扩展——这些只是引入如Dagger 2依赖注入框架的一小部分好处。
现在我们可以找到许多教程和代码例子展示如何在Android中开始使用Dagger2和依赖注入。如果这是你寻找的,这是我的系列博客可以帮助你:
- 介绍依赖注入
- Dagger 2 API
- Dagger 2——定制范围
- Dagger 2——图形创建性能
- 使用Dagger2进行依赖注入——生产者
- Dagger2使用RxJava进行异步注入
- 注入万物——ViewHolder和Dagger2(使用Multibinding和AutoFactor的例子)
但这些只展示如何开始使用DI。这也是在我们app中使用两年后,为什么今天打算开始分析使用Dagger2的经验。
@Component vs @Subcomponent
自定义范围在简单教程中通常解释不清楚,但它是Dagger2最强有力功能之一。在更复杂的app中只使用@Singleton不会让你的工作更简单。
让我们考虑一个简单的例子——我们有严格连接当前登录用户的依赖(例如,UserProfilePresenter类负责用户配置屏幕的逻辑)。当我们需要用户实体时,不需要每次都调用数据存储,我们可以创建@User范围,并将所有依赖保持在单一实例UserComponent中,该实例生命周期与登陆的用户一致。
在生成app中使用自定义范围不是这篇文字的主题,但我们会在接下来讲解它。
在Dagger2,我没有两种方式构建自定义范围和组件来继承并扩展对象图:
- 我们可以构建另一个@Component来明确展示扩展Component(本例的AppComponent)
@UserScope
@Component(
modules = UserModule.class,
dependencies = AppComponent.class
)
public interface UserComponent {
UserProfileActivity inject(UserProfileActivity activity);
}
- 或者我们可以定义@Subcomponent,它是从基础组件的抽象工厂方法(阅读更多)创建的:
@UserScope
@Subcomponent(
modules = UserModule.class
)
public interface UserComponent {
UserProfileActivity inject(UserProfileActivity activity);
}
//===== AppComponent.java =====
@Singleton
@Component(modules = {...})
public interface AppComponent {
// Factory method to create subcomponent
UserComponent plus(UserModule module);
}
这两种方法的区别?
文档讨论了依赖可视性的区别。@Subcomponent从它的父类访问所有依赖,而@Component只能访问在基类@Component接口暴露的公共性的依赖。
考虑到这点,我们最初选择带依赖@Component的方法。工程的所有组件都依赖范围是@Singleton的AppComponent。
当出现方法数事情…
但在@Component和@Subcomponent之间有另一个重要的不同。让我们看看生成的代码。子组件的实现只是基础组件的一个内部类。所以对于依赖AppCompoenet生成代码的UserProfiileActivityComponent可能长这样:
public final class DaggerAppComponent implements AppComponent {
//...AppComponent code...
private final class UserProfileActivityComponentImpl implements UserProfileActivityComponent {
private final UserProfileActivityComponent.UserProfileActivityModule userProfileActivityModule;
private Provider<UserProfileActivity> provideActivityProvider;
private Provider<UserProfileActivityPresenter> userProfileActivityPresenterProvider;
private MembersInjector<UserProfileActivity> userProfileActivityMembersInjector;
private UserProfileActivityComponentImpl(
UserProfileActivityComponent.UserProfileActivityModule userProfileActivityModule) {
this.userProfileActivityModule = Preconditions.checkNotNull(userProfileActivityModule);
initialize();
}
private void initialize() {
this.provideActivityProvider = DoubleCheck.provider(BaseActivityModule_ProvideActivityFactory.create(userProfileActivityModule));
this.userProfileActivityPresenterProvider = DoubleCheck.provider(
UserProfileActivityPresenter_Factory.create(
MembersInjectors.<UserProfileActivityPresenter>noOp(),
provideActivityProvider,
DaggerAppComponent.this.logoutManagerProvider,
DaggerAppComponent.this.userManagerProvider)
);
this.userProfileActivityMembersInjector = UserProfileActivity_MembersInjector.create(
DaggerAppComponent.this.logoutManagerProvider,
DaggerAppComponent.this.userManagerProvider
userProfileActivityPresenterProvider)
);
}
@Override
public UserProfileActivity inject(UserProfileActivity activity) {
userProfileActivityMembersInjector.injectMembers(activity);
return activity;
}
}
}
20-31行展示了Dagger如何提供从AppComponent到UserProfileActivityComponent的依赖。子组件可访问基础组件提供的域,所以它们可以直接使用。
不同情况取决于@Component。相同的结构(UserProfileActivityComponent依赖AppComponent)将生成这样的代码:
public final class DaggerUserProfileActivityComponent implements UserProfileActivityComponent {
private Provider<LogoutManager> logoutManagerProvider;
private Provider<UserManager> userManagerProvider;
//...
private DaggerUserProfileActivityComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.logoutManagerProvider = new Factory<LogoutManager>() {
private final AppComponent appComponent = builder.appComponent;
@Override
public LogoutManager get() {
return Preconditions.checkNotNull(
appComponent.getLogoutManager(),
"Cannot return null from a non-@Nullable component method");
}
};
this.userManagerProvider = new Factory<UserManager>() {
private final AppComponent appComponent = builder.appComponent;
@Override
public UserManager get() {
return Preconditions.checkNotNull(
appComponent.getUserManager(),
"Cannot return null from a non-@Nullable component method");
}
};
//... more providers ....
}
@Override
public UserProfileActivity inject(UserProfileActivity activity) {
userProfileActivityMembersInjector.injectMembers(activity);
return activity;
}
//...
}
14-34行展示了每个从AppComponent发出的依赖请求都需要自己的方法来生成Provider<>对象。为什么?因为依赖组件只能访问那些基类组件暴露了公共接口的依赖。
这对我们意味着什么——Android开发者?从基类组件带到依赖组件中的每一个依赖都讲都让我们更接近65k方法数限制:
因为这一点,我们决定所有依赖于@Component都迁移到@SubComponent。经过这样处理,我们减少了大约5000个额外的方法。
快速分析你的APK
最后值得一提的是从Android Studio 2.2开始出现了一个新的功能,让我们分析APK文件。访问方式是Build -> Analyze APK…
这个工具允许我们检查特殊组件的大小(类,资源等),和最能帮助我们的——得到.dex文件的基础信息(类/方法数)。这是依赖注入优化的起始点。
今天就到这里了,但保持联系。不久,我们将分享更多关于Dagger2和依赖注入的经验。感觉阅读!
想了解更多?关注我们的Twitter@AzimoLabs和Github账号。