1. IOC思想
先看一个简单的例子:
如果在 Class A 中,有 Class B 的实例,则称 Class A 对 Class B 有一个依赖。例如下面类 Human 中用到一个 Father 对象,我们就说类 Human 对类 Father 有一个依赖。
public class Human {
...
Father father;
...
public Human() {
father = new Father();
}
}
仔细看这段代码我们会发现存在一些问题:
如果现在要改变 father 生成方式,如需要用new Father(String name)初始化 father,需要修改 Human 代码,而如果Father类在程序多个类中被使用,那么需要所有地方都修改。
我们还有另外一种方式,如下:
public class Human {
...
Father father;
...
public Human(Father father) {
this.father = father;
}
}
上面代码中,我们将 father 对象作为构造函数的一个参数传入。在调用 Human 的构造方法之前外部就已经初始化好了 Father 对象。像这种非自己主动初始化依赖,而通过外部来传入依赖的方式,我们就称为依赖注入。
但这种方式并不是足够好的,如果有几百个类需要引用Father类的对象,意味着我们需要写几百个以Father类为参数的构造函数、或者set方法,所以随着使用规模的增加会产生大量的模板代码,这对后期的维护和修改带来困难。而且在 Activity、Fragment 这样的类中,用上边这些方式似乎很难实现依赖注入。因此像Dagger2这样的IOC框架就应运而生,很好的解决了上面的问题,将依赖之间解耦。
1.1 什么是IOC
IOC是Inversion of Control
的缩写,翻译为控制反转,是面向对象编程中的一种设计原则,可以用来降低代码之间的耦合度。
既然IOC是控制反转,那么是哪些方面的控制被反转了呢?其实是获得依赖对象的过程被反转了,控制被反转之后,获得依赖对象的过程由自身管理变为了由IOC容器主动注入。
因此IOC中最常见的方式叫做依赖注入(Dependency Injection,简称DI),所谓依赖注入,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中。
2. Android 中应用
Android中应用最常见的有ButterKnife、Dagger2等,用来实现对象和事件的注入。普遍的实现方式是使用自定义注解来做标记,然后可以使用反射或者java动态代理来生成依赖注入的代码,但考虑到运行时反射的效率低问题转而使用注解处理器APT在编译时生成注入代码。
2.1 ButterKnife
我们在Activity中简单的使用:
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tvTitle)
TextView textView;
@BindView(R.id.button)
Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
textView.setText("文本");
button.setText("按钮");
}
@OnClick(R.id.button)
public void testClick(){
Toast.makeText(getApplicationContext(),"测试点击事件",Toast.LENGTH_SHORT).show();
}
}
build后我们可以看到MainActivity_ViewBinding
这个java类就是Butterknife通过APT来解析注解生成的代码:
我们回到ButterKnife在Activity中绑定的代码ButterKnife.bind(this)
,内部实现:
@NonNull @UiThread
public static Unbinder bind(@NonNull Object target, @NonNull View source) {
//1
Class<?> targetClass = target.getClass();
if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName());
//2
Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);//2
if (constructor == null) {
return Unbinder.EMPTY;
}
//noinspection TryWithIdenticalCatches Resolves to API 19+ only type.
try {
//3
return constructor.newInstance(target, source);
} catch (IllegalAccessException e) {
throw new RuntimeException("Unable to invoke " + constructor, e);
} catch (InstantiationException e) {
throw new RuntimeException("Unable to invoke " + constructor, e);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException("Unable to create binding instance.", cause);
}
}
在1处拿到传入的Activity的Class类对象,在2处拿到Constructor
对象,并在3处返回了创建的实例。再看2处方法具体代码:
@Nullable @CheckResult @UiThread
private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
if (bindingCtor != null || BINDINGS.containsKey(cls)) {
if (debug) Log.d(TAG, "HIT: Cached in binding map.");
return bindingCtor;
}
String clsName = cls.getName();
if (clsName.startsWith("android.") || clsName.startsWith("java.")
|| clsName.startsWith("androidx.")) {
if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
return null;
}
try {
Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");//重点
//noinspection unchecked
bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class);
if (debug) Log.d(TAG, "HIT: Loaded binding class and constructor.");
} catch (ClassNotFoundException e) {
if (debug) Log.d(TAG, "Not found. Trying superclass " + cls.getSuperclass().getName());
bindingCtor = findBindingConstructorForClass(cls.getSuperclass());
} catch (NoSuchMethodException e) {
throw new RuntimeException("Unable to find binding constructor for " + clsName, e);
}
BINDINGS.put(cls, bindingCtor);
return bindingCtor;
}
重点看cls.getClassLoader().loadClass(clsName + "_ViewBinding")
拿到了生成的java类,并调用了构造方法获取对象。我们在看下生成的类代码:
public class MainActivity_ViewBinding implements Unbinder {
private MainActivity target;
private View view7f070022;
@UiThread
public MainActivity_ViewBinding(MainActivity target) {
this(target, target.getWindow().getDecorView());
}
@UiThread
public MainActivity_ViewBinding(final MainActivity target, View source) {
this.target = target;
View view;
target.textView = Utils.findRequiredViewAsType(source, R.id.tvTitle, "field 'textView'", TextView.class);
view = Utils.findRequiredView(source, R.id.button, "field 'button' and method 'testClick'");
target.button = Utils.castView(view, R.id.button, "field 'button'", Button.class);
view7f070022 = view;
view.setOnClickListener(new DebouncingOnClickListener() {
@Override
public void doClick(View p0) {
target.testClick();
}
});
}
@Override
@CallSuper
public void unbind() {
MainActivity target = this.target;
if (target == null) throw new IllegalStateException("Bindings already cleared.");
this.target = null;
target.textView = null;
target.button = null;
view7f070022.setOnClickListener(null);
view7f070022 = null;
}
}
看到这里我们就会豁然开朗,最初的绑定方法ButterKnife.bind(this)
最终就是调用MainActivity_ViewBinding
构造方法,在构造方法中findViewById
,并赋值给Activity的变量
这里也解决了一个我们在使用Butterknife的疑问,就是我们使用注解的变量或者方法不能被private和protected修饰,因为我们的target的对象要直接调用对应的变量和方法,比如 target.textView、target.testClick()
2.2 Dagger2
基本实现与ButterKnife一致,先实现一个基础的功能:
public class Cat {
@Inject
public Cat() {
}
@Override
public String toString() {
return "喵星人来了!";
}
}
我们定义了一个 Cat
类,并在它的无参构造函数上使用了 @Inject
注解,告诉 Dagger 2 这个无参构造函数可以被用来创建 Cat
对象,即依赖的提供方。然后我们在自定义一个依赖提供方:
@Module
public class MainModule {
@Provides
public Flower provideRedRose() {
return new Flower("玫瑰", "红色");
}
}
public class Flower {
private String name;
private String color;
public Flower(String name, String color) {
this.name = name;
this.color = color;
}
@Override
public String toString() {
return "Flower{" + "name='" + name + "', color='" + color + "'}";
}
}
将 MainModule
和 MainComponent
关联起来,这样依赖注入组件就知道从哪个依赖提供方取数据了:
@Component(modules = {MainModule.class})
public interface MainComponent {
void inject(MainActivity activity);
}
编译项目后修改依赖注入配置代码:
public class MainActivity extends AppCompatActivity {
@Inject
Cat cat;
@Inject
Flower flower;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainComponent.builder()
// 设置 MainModule 对象
.mainModule(new MainModule())
.build()
.inject(this);
Log.e("cat", cat.toString());
Log.e("flower", flower.toString());
}
}
运行后可以看到如下 Log 信息:
cat:'喵星人来了!'
flower:Flower{name='玫瑰',color='红色'}
实现原理:
以前边的demo为例,项目编译后会在app\build\generated\source\apt\debug\包名目录
下生成依赖注入的相关类,如下:
按照之前说法,DaggerMainComponent
是完成依赖注入的核心,所以从这个类开始分析,它的源码如下:
public final class DaggerMainComponent implements MainComponent {
private MainModule mainModule;
private DaggerMainComponent(Builder builder) {
initialize(builder);
}
public static Builder builder() {
return new Builder();
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.mainModule = builder.mainModule;
}
// 重写inject方法,完成依赖注入
@Override
public void inject(MainActivity activity) {
injectMainActivity(activity);
}
// 完成依赖注入的重要方法
private MainActivity injectMainActivity(MainActivity instance) {
// 给 MainActivity 中的 cat 成员变量赋值
MainActivity_MembersInjector.injectCat(instance, new Cat());
// 给 MainActivity 中的 flower 成员变量赋值
MainActivity_MembersInjector.injectFlower1(
instance, MainModule_ProvideRedRoseFactory.proxyProvideRedRose(mainModule));
return instance;
}
public static final class Builder {
private MainModule mainModule;
private Builder() {}
// 完成DaggerMainComponent对象的创建
public MainComponent build() {
if (mainModule == null) {
this.mainModule = new MainModule();
}
return new DaggerMainComponent(this);
}
// 设置 mainModule 对象
public Builder mainModule(MainModule mainModule) {
this.mainModule = Preconditions.checkNotNull(mainModule);
return this;
}
}
}
先通过builder()
方法创建一个 Builder
对象,再通过其mainModule()
方法设置mainModule
对象,接下来用 build()方法就是创建DaggerMainComponent
对象,这样它里边也有了一个mainModule
对象。
在 DaggerMainComponent
中还重写了 MainComponent
接口的inject()
方法,里边调用的injectMainActivity()
方法是完成依赖注入的关键:
private MainActivity injectMainActivity(MainActivity instance) {
MainActivity_MembersInjector.injectCat(instance, new Cat());
MainActivity_MembersInjector.injectFlower1(
instance, MainModule_ProvideRedRoseFactory.proxyProvideRedRose(mainModule));
return instance;
}
首先是调用MainActivity_MembersInjector
类的injectCat()
方法直接创建一个 Cat
对象,完成 MainActivity
中 cat
的赋值,injectCat()
方法声明如下:
public static void injectCat(MainActivity instance, Cat cat) {
instance.cat = cat;
}
然后是调用injectFlower()
方法,完成 MainActivity
中 flower
的赋值,那么 flower
对象的值从哪里来呢?这里调用了MainModule_ProvideRedRoseFactory
的proxyProvideRedRose()
方法:
public static Flower proxyProvideRedRose(MainModule instance) {
return Preconditions.checkNotNull(
instance.provideRedRose(), "Cannot return null from a non-@Nullable @Provides method");
}
里边最终是调用了我们在 MainModule
中声明的 provideRedRose()
方法,所以在 DaggerMainComponent
内部是通过 MainActivity_MembersInjector
完成了最终的依赖注入。所以当在 Activity
中执行inject(this)
方法时,就是开始创建依赖对象,并完成注入工作。