Dagger2流行已经有一段时间了,是一个很强大的依赖注入框架。以前接触过Spring的IOC,了解过它的实现原理是用反射实现的。而移动端是对资源很敏感的,Dagger2作为移动端的一个主流框架,肯定不会用一样的套路去玩。所以抽空研究了一下它的实现方式。
public class ActivityLogin extends Activity implements ILoginView {
private static final String TAG = "ActivityLogin_";
@Inject
LoginPresenter loginPresenter;
Button loginBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
DaggerMyComponent.create().inject(this);
loginPresenter.attachView(this);
loginBtn = findViewById(R.id.btn_login);
loginBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
loginPresenter.login();
}
});
}
@Override
public void onLoginSuccess() {
Log.i("MyModule", TAG + "LoginSuccess_");
Toast.makeText(this, "ActivityLogin onLoginSuccess", Toast.LENGTH_LONG).show();
}
@Override
public void onLoginError(String errorMsg) {
}
}
上面的例子是一个MVP的例子:要在一个Activity中注入一个LoginPresenter(在上面加了@Inject 注解)。来看LoginPresenter:
public class LoginPresenter extends BasePresenter {
private static final String TAG = "LoginPresenter";
@Inject
public LoginPresenter() {
}
@Override
public void onDetach() {
Log.i("MyModule", TAG+" "+hashCode()+" detach_" + getActivityNameBelongTo());
}
@Override
public void onAttach() {
Log.i("MyModule", TAG+" "+hashCode()+" attach_" + getActivityNameBelongTo());
}
public void login() {
Log.i("MyModule",TAG+" http request ...");
getView().onLoginSuccess();
}
}
需要注入的类,LoginPresenter的构造函数上也加了@Inject注解。
然后看Component:
@Component()
public interface MyComponent {
void inject(ActivityLogin activityLogin);
}
可以看到MyComponent是个接口,@Component注解是可以带参数的(Class>[] 数组),里面指定了依赖要从哪里拿,这个例子默认都由Dagger2生成依赖。
然后发现ActivityLogin中,我们想要的实例LoginPresenter loginPresenter 就被生成了。
那它是怎么做到的呢?依赖的对象实例究竟是从哪里来的呢?
工程的目录结构:
build/generate/source/apt/debug/xxx(包名)下的结构:
可以看到,Dagger2帮我们生成了几个文件:
我们使用的时候是DaggerXXXX.creat().inject(this),看下这个生成的DaggerXXXX:
public final class DaggerMyComponent implements MyComponent {
private Provider loginPresenterProvider;
private MembersInjector activityLoginMembersInjector;
private DaggerMyComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
public static Builder builder() {
return new Builder();
}
public static MyComponent create() {
return builder().build();
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.loginPresenterProvider =
LoginPresenter_Factory.create(MembersInjectors.noOp());
this.activityLoginMembersInjector =
ActivityLogin_MembersInjector.create(loginPresenterProvider);
}
@Override
public void inject(ActivityLogin activityLogin) {
activityLoginMembersInjector.injectMembers(activityLogin);
}
public static final class Builder {
private Builder() {}
public MyComponent build() {
return new DaggerMyComponent(this);
}
}
}
这种情况是最简单的(依赖的类LoginPresenter没有其他依赖),所以这时候分析文件结构最清晰。
贴出来LoginPresenter_Factory:
public final class LoginPresenter_Factory implements Factory {
private final MembersInjector loginPresenterMembersInjector;
public LoginPresenter_Factory(MembersInjector loginPresenterMembersInjector) {
assert loginPresenterMembersInjector != null;
this.loginPresenterMembersInjector = loginPresenterMembersInjector;
}
@Override
public LoginPresenter get() {
return MembersInjectors.injectMembers(loginPresenterMembersInjector, new LoginPresenter());
}
public static Factory create(
MembersInjector loginPresenterMembersInjector) {
return new LoginPresenter_Factory(loginPresenterMembersInjector);
}
}
可以看到LoginPresenter_Factory 会有一个MembersInjector 成员,这个成员是构造函数依赖进来的;并且get方法里,有new的动作,也就是说,如果依赖的类是无参构造,最后依赖对象的实例化是在依赖对应的Factory中生成的。
看ActivityLogin_MembersInjector:
public final class ActivityLogin_MembersInjector implements MembersInjector {
private final Provider loginPresenterProvider;
public ActivityLogin_MembersInjector(Provider loginPresenterProvider) {
assert loginPresenterProvider != null;
this.loginPresenterProvider = loginPresenterProvider;
}
public static MembersInjector create(
Provider loginPresenterProvider) {
return new ActivityLogin_MembersInjector(loginPresenterProvider);
}
@Override
public void injectMembers(ActivityLogin instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.loginPresenter = loginPresenterProvider.get();
}
public static void injectLoginPresenter(
ActivityLogin instance, Provider loginPresenterProvider) {
instance.loginPresenter = loginPresenterProvider.get();
}
}
ActivityLogin_MembersInjector 会有一个Provider 的成员,这个成员是构造的参数,并且injectMembers方法持有被注入依赖类(ActivityLogin)的实例引用,**instance.loginPresenter = loginPresenterProvider.get()**真正做了“桥梁”,为ActivityLogin中的loginPresenter赋值。
看个UML图:
##梳理一下几个类的关系
简单的总结就是,DaggerMyComponent通过Factory获取了Provider包装的依赖的对象(Provider的实例)然后用它生成了ActivityLogin_MembersInjector的实例,最后这个实例包含了被依赖和依赖对象的引用,就可以完成搭桥了。
看一下关系图
@Inject
public LoginPresenter(Dependence1 dependence1) {
}
如果这时候什么都不做,编译的时候会报错:
这是因为Dagger2并不知道Dependence1 的实例要从哪里来。对Dependence1做处理:
public class Dependence1 {
@Inject
public Dependence1(){}
}
给构造函数加Inject注解。
描述下变化:
public enum Dependence1_Factory implements Factory {
INSTANCE;
@Override
public Dependence1 get() {
return new Dependence1();
}
public static Factory create() {
return INSTANCE;
}
}
这是个枚举单例,实现了Factory。可以看到,这里对Dependence1做了实例化(new 动作)此时 DaggerMyComponent中的initalize方法:
private void initialize(final Builder builder) {
this.loginPresenterProvider =
LoginPresenter_Factory.create(
MembersInjectors.noOp(), Dependence1_Factory.create());
this.activityLoginMembersInjector =
ActivityLogin_MembersInjector.create(loginPresenterProvider);
}
对比之前,就是LoginPresenter_Factory.create方法多加了一个参数,这个参数由Dependence1_Factory提供。这样可能还是有些逻辑不清楚,稍做改动:
private void initialize(final Builder builder) {
this.activityLoginMembersInjector =
ActivityLogin_MembersInjector.create(LoginPresenter_Factory.create(
MembersInjectors.noOp(), Dependence1_Factory.create()));
}
这样是不是可以看出一些层级关系?
再做一次抽象:
很明显这是个树形图。
之所以Dagger2生成的文件不是我改动后的这种,是因为每个生成的Provider,都有可能被其他MemberInjector复用。我只是为了让关系层级更清晰。
所以基本可以看出:
这只是一个最简单的使用Dagger2的例子,这样可以很清晰的搞清楚Dagger2为我们生成这么多文件它们之间的关系。后面会把其他的注解也分析,整体的分析思路是一样的。这一趟分析个人感觉有几个很nice的设计思路:
@Inject 修饰的成员不能是private的,因为生成的桥梁类MemberInjector和被注入类在一个包目录下面,private是不能访问的。放在含有注解的类所在包目录下面,可以最大程度保护成员的访问权限,不然只能是public;
DaggerXXX用了外观模式,拿到了本来只能在包目录下才能访问的依赖实例;
DaggerXXX实现了标注了@Component 的接口,这个接口指定了哪些类是需要注入依赖的类,所以我们在要注入依赖的类里使用DaggerXXX.create().inject(this)才这么方便。
使用的设计模式: 外观模式、 工厂模式、 单例模式