Android IOC框架的实现原理

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的疑问,就是我们使用注解的变量或者方法不能被privateprotected修饰,因为我们的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 + "'}";
    }
}

MainModuleMainComponent 关联起来,这样依赖注入组件就知道从哪个依赖提供方取数据了:

@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 对象,完成 MainActivitycat 的赋值,injectCat()方法声明如下:

public static void injectCat(MainActivity instance, Cat cat) {
    instance.cat = cat;
  }

然后是调用injectFlower()方法,完成 MainActivityflower 的赋值,那么 flower 对象的值从哪里来呢?这里调用了MainModule_ProvideRedRoseFactoryproxyProvideRedRose()方法:

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)方法时,就是开始创建依赖对象,并完成注入工作。

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值