android dagger2官网,Dagger2在Android平台上的新魔法

0. 前言

上一篇文章 Dagger2在Android平台上的新姿势,主要介绍了Dagger2在Android平台上的更加简洁,更加符合依赖注入思想的新用法。按照里面介绍的步骤,我们可以一步步的实现,并没有什么难度。但是,关于这一切的魔法究竟是怎么发生的,上一篇文章(官方文档)中只给出了大概的描述。这一篇文章将通过一个实际的例子来解释这一切背后的魔法。

我已经放弃使用dagger.android了,具体原因可以查看当定义Dagger2 Scope时,你在定义什么?

1. 一个问题

你可能会说,在Android上,dagger2我用得6的飞起,你现在告诉我说又有新姿势了。。。现在骗子这么多,我凭啥相信你!你倒是说说为什么要换新姿势,就因为我的发际线越来越高了吗?!

c01fdda42434

大兄弟,莫急,你原来那个姿势的问题说大不大,说小也不小。你原来是不是经常这么写:

public class YourActivity extends Activity {

@Inject

Frombulator frombulator;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

DaggerActivityComponent.builder()

.appComponent(YourApp.getAppComponent())

.activityModule(new ActivityModule(this))

.build()

.inject(this);

// ...其它代码

}

}

这里最主要的问题在于,被注入类(YourActivity)需要知道注入器(DaggerActivityComponent)是什么,并且由于四大组件和Fragment的创建不受我们的控制,因此我们只能在其生命周期内完成相应的依赖注入。这破坏了依赖注入的崇高理想:一个类不应该对它是如何被注入的有任何的了解。

对于Activity,一般需要在onCreate中构造DaggerActivityComponent调用它的inject方法。虽然你可以通过封装,将这些步骤封装到YourActivity的父类中,避免每个Activity都写这些仪式感的代码。但是,我们崇高的理想并没有实现。

新姿势就是为了实现我们更懒的崇高理想。为此dagger2给我们指了一条明路:把Android四大组件和Fragment对应的注入器的构造器(Subcomponent.Builder)放在Map中,当系统构建四大组件和Fragment时,从Map中查找出对应的Builder,完成注入。最后,我们可以像下面这样完成注入。

public class YourActivity extends Activity {

@Inject

Frombulator frombulator;//OK!就是这么简单

}

2. 背后的原理

2.1 创建SubComponent并加入到Map中

假设我们有一个MainActivity,里面有UserFragment和SearchFragment。定义两个Module:MainActivityModule和FragmentBuildersModule:

@Module

public abstract class MainActivityModule {

@ContributesAndroidInjector(modules = FragmentBuildersModule.class)

abstract MainActivity contributeMainActivity();

}

@Module

public abstract class FragmentBuildersModule {

@ContributesAndroidInjector

abstract UserFragment contributeUserFragment();

@ContributesAndroidInjector

abstract SearchFragment contributeSearchFragment();

}

这就是dagger2在Android平台上新的用法,详见Dagger2在Android平台上的新姿势。注解@ContributesAndroidInjector会生成如下的代码:

@Module(subcomponents = MainActivityModule_ContributeMainActivity.MainActivitySubcomponent.class)

public abstract class MainActivityModule_ContributeMainActivity {

private MainActivityModule_ContributeMainActivity() {}

@Binds

@IntoMap

@ActivityKey(MainActivity.class)

abstract AndroidInjector.Factory extends Activity> bindAndroidInjectorFactory(

MainActivitySubcomponent.Builder builder);

@Subcomponent(modules = FragmentBuildersModule.class)

public interface MainActivitySubcomponent extends AndroidInjector {

@Subcomponent.Builder

abstract class Builder extends AndroidInjector.Builder {}

}

}

这是魔法得以发生的核心,里面包含了许多概念,让我们一一来看。

先来看几个dagger2中的功能。@Binds可以用于注解Module中abstract方法,一般是用来把一个接口的实现绑定到接口上。@IntoMap和@ActivityKey注解,向Map中加入数据。@Module(subcomponents = ...)其中的参数subcomponents可以指定一些subcomponent,这些subcomponent必须是该Module被使用的component的子component,这样这些Subcomponent.Builder就可以像普通对象一样被注入。

再来看看AndroidInjector:

public interface AndroidInjector {

void inject(T instance);

/**

* Factory模式创建具体类型T的 AndroidInjector

*

* @param 四大组件或Fragment

*/

interface Factory {

AndroidInjector create(T instance);

}

/**

* Builder模式实现Factory

*/

abstract class Builder implements AndroidInjector.Factory {

@Override

public final AndroidInjector create(T instance) {

seedInstance(instance);

return build();

}

/**

* 把四大组件或Fragment的实例instance绑定到AndroidInjector上

*/

@BindsInstance

public abstract void seedInstance(T instance);

public abstract AndroidInjector build();

}

}

看清楚,AndroidInjector接口中只有一个方法inject(是不是让你想到了Component)。AndroidInjector的命名非常的自解释,意思是Android的注入器,即只能在Android四大组件和Fragment上使用的注入器,所谓的注入器就是我们平时定义的Component。AndroidInjector接口中还定义了一个Factory接口,和一个实现了该接口的Builder抽象类。所以,AndroidInjector接口的意义就十分明显了,它是一个帮助我们定义Component的接口。再看看之前MainActivitySubcomponent的定义:

@Subcomponent(modules = FragmentBuildersModule.class)

public interface MainActivitySubcomponent extends AndroidInjector {

@Subcomponent.Builder

abstract class Builder extends AndroidInjector.Builder {}

}

MainActivitySubcomponent的定义完全借助AndroidInjector接口和AndroidInjector.Builder抽象类。其中,AndroidInjector.Builder通过seedInstance(T instance)方法,把MainActivity实例绑定到了MainActivitySubcomponent中,这样就可以在MainActivitySubcomponent关联的Module或其子Component中使用MainActivity实例了。

让我们来回顾一下到目前为止的流程:

c01fdda42434

ActivitySubcomponent流程

同理对于Fragment有:

c01fdda42434

FragmentSubcomponent流程

显然FragmentSubcomponent是ActivitySubcomponent的子Component;而ActivitySubcomponent是AppComponent的子Component。如下是AppComponent的定义:

@Singleton

@Component(modules = {

AndroidInjectionModule.class,//用于提供四大组件和Fragment的Map

AppModule.class,//定义一些全局的对象

MainActivityModule.class//这样MainActivitySubcomponent就成了AppComponent的子Component

})

public interface AppComponent {

@Component.Builder

interface Builder {

//跟之前同样的方式,把Application实例绑定到AppComponent,比把Application传递给AppModule再提供出来要更方便也更快捷

@BindsInstance Builder application(Application application);

AppComponent build();

}

void inject(MyApp myApp);

}

2.2 从Map中把相应的SubComponent取出来

现在 *ActivitySubcomponent.Builder 和 *FragmentSubcomponent.Builder,都被存储在各自的Map中了,最后一步就是从Map中把它们取出来,然后完成注入。这需下两个步骤。

2.2.1 父Component创建分发者

首先,在父Component创建的地方实现HasActivityInjector(HasServiceInjector,HasBroadcastReceiverInjector,HasContentProviderInjector,HasFragmentInjector,HasSupportFragmentInjector),具体实现哪个接口,取决于子Component有哪些。

/**

* 对于Application,其子Component包括 *ActivitySubcomponent,所以实现 HasActivityInjector接口

*/

public class MyApp extends Application implements HasActivityInjector {

@Inject

DispatchingAndroidInjector dispatchingActivityInjector;

@Override

public void onCreate() {

super.onCreate();

DaggerAppComponent.builder()

.application(this)

.build()

.inject(this);

}

@Override

public AndroidInjector activityInjector() {

return dispatchingActivityInjector;

}

}

/**

* 对于Activity,其子Component包括 *FragmentSubcomponent,所以实现 HasSupportFragmentInjector接口

*/

public class MainActivity extends AppCompatActivity implements HasSupportFragmentInjector {

@Inject

DispatchingAndroidInjector dispatchingAndroidInjector;

@Override

public DispatchingAndroidInjector supportFragmentInjector() {

return dispatchingAndroidInjector;

}

}

实现接口的主要目的是创建分发者DispatchingAndroidInjector,也就是解决从哪个Map中查找的问题。下面是DispatchingAndroidInjector的定义:

/**

* @param the core Android type to be injected

*/

public final class DispatchingAndroidInjector implements AndroidInjector {

private final Map, Provider>>

injectorFactories;

@Inject

DispatchingAndroidInjector(

Map, Provider>> injectorFactories) {

this.injectorFactories = injectorFactories;

}

/**

* 对于instance尝试 members-injection

*/

public boolean maybeInject(T instance) {

//通过instance.getClass()去查找对应的AndroidInjector.Factory,其实就是我们的AndroidInjector.Builder

Provider> factoryProvider =

injectorFactories.get(instance.getClass());

if (factoryProvider == null) {

return false;

}

@SuppressWarnings("unchecked")

AndroidInjector.Factory factory = (AndroidInjector.Factory) factoryProvider.get();

try {

AndroidInjector injector = factory.create(instance);

//注意!这里完成了注入。

injector.inject(instance);

return true;

} catch (ClassCastException e) {

throw new InvalidInjectorBindingException();

}

}

@Override

public void inject(T instance) {

boolean wasInjected = maybeInject(instance);

if (!wasInjected) {

throw new IllegalArgumentException(errorMessageSuggestions(instance));

}

}

}

当我们实现了Has*Injector接口时,其实就是确定了DispatchingAndroidInjector需要查找的Map,根据instance.getClass()查找到对于的AndroidInjector.Factory即可完成成员注入。

2.2.2 真正的成员注入

万事俱备了,只差最后的成员注入了。有了DispatchingAndroidInjector对象,只需要指定被注入的对象是什么,然后调用DispatchingAndroidInjector上的inject(T instance)方法即可完成成员注入。

public class MainActivity extends AppCompatActivity implements HasSupportFragmentInjector {

@Inject

SomeDependency someDep;

public void onCreate(Bundle savedInstanceState) {

//这一步往往封装到父类中

AndroidInjection.inject(this);

super.onCreate(savedInstanceState);

}

}

public class UserFragment extends Fragment {

@Inject

SomeDependency someDep;

@Override

public void onAttach(Activity activity) {

//这一步往往封装到父类中

AndroidSupportInjection.inject(this);

super.onAttach(activity);

// ...

}

}

最后把一切连接在一起的就是这个AndroidInjection.inject(this),对于android.support.v4.app.Fragment而言是AndroidSupportInjection.inject(this)。其实这里面的逻辑极其简单:

/** Injects core Android types. */

public final class AndroidInjection {

public static void inject(Activity activity) {

checkNotNull(activity, "activity");

Application application = activity.getApplication();

if (!(application instanceof HasActivityInjector)) {

throw new RuntimeException();

}

//这里的activityInjector显然就是DispatchingAndroidInjector

AndroidInjector activityInjector =

((HasActivityInjector) application).activityInjector();

checkNotNull(

activityInjector,

"%s.activityInjector() returned null",

application.getClass().getCanonicalName());

//在Activity的Map中查找,并完成成员注入

activityInjector.inject(activity);

}

public static void inject(Fragment fragment) {

//...

}

public static void inject(Service service) {

//...

}

public static void inject(BroadcastReceiver broadcastReceiver, Context context) {

//...

}

public static void inject(ContentProvider contentProvider) {

//...

}

private AndroidInjection() {}

}

对于Service,BroadcastReceiver,ContentProvider,逻辑跟inject(Activity activity)方法是一样的:从Application中获得DispatchingAndroidInjector,然后调用其inject方法完成成员注入。

对于android.support.v4.app.Fragment,逻辑稍微复杂一些:

/** Injects core Android types from support libraries. */

public final class AndroidSupportInjection {

/**

* 按照如下逻辑找到合适的 AndroidInjector:

* 1. 查找所有的父fragment,看它是否实现了HasSupportFragmentInjector接口;如果没有则

* 2. 看getActivity()的Activity是否实现了HasSupportFragmentInjector接口;如果没有则

* 3. 看Application是否实现了HasSupportFragmentInjector接口

*/

public static void inject(Fragment fragment) {

checkNotNull(fragment, "fragment");

HasSupportFragmentInjector hasSupportFragmentInjector = findHasFragmentInjector(fragment);

AndroidInjector fragmentInjector =

hasSupportFragmentInjector.supportFragmentInjector();

checkNotNull(

fragmentInjector,

"%s.supportFragmentInjector() returned null",

hasSupportFragmentInjector.getClass().getCanonicalName());

//完成成员注入

fragmentInjector.inject(fragment);

}

private static HasSupportFragmentInjector findHasFragmentInjector(Fragment fragment) {

Fragment parentFragment = fragment;

while ((parentFragment = parentFragment.getParentFragment()) != null) {

if (parentFragment instanceof HasSupportFragmentInjector) {

return (HasSupportFragmentInjector) parentFragment;

}

}

Activity activity = fragment.getActivity();

if (activity instanceof HasSupportFragmentInjector) {

return (HasSupportFragmentInjector) activity;

}

if (activity.getApplication() instanceof HasSupportFragmentInjector) {

return (HasSupportFragmentInjector) activity.getApplication();

}

throw new IllegalArgumentException(

String.format("No injector was found for %s", fragment.getClass().getCanonicalName()));

}

private AndroidSupportInjection() {}

}

之所以Fragment的注入逻辑要复杂些,是因为*FragmentSubcomponent可以是其父Fragment的子Component,也可以是Activity的子Component,还可以是Application的子Component。而Activity(Service,BroadcastReceiver,ContentProvider)的Subcomponent只可能是Application的子Component。

3. 总结

下面以Activity为例,回顾一下整体的流程。

c01fdda42434

总流程

以上就是为什么我们没有显式地构造ActivitySubcomponent,却能完成成员依赖注入的魔法。这样每个Activity都不需要知道其注入器是什么,就可以完成依赖注入,实现了我们最初的崇高理想。

AndroidInjection.inject(activity)传入的activity最后被绑定到了ActivitySubcomponent上,这样在ActivitySubcomponent关联的Module和其子Component中就可以使用该activity实例了。

4. 尾巴

dagger2在Android平台上新的用法,在其底层技术实现上,只是使用了dagger2原来就有的subcomponent和multibindings概念,并没有任何新增的东西。我在想,为什么我们总是技术的使用者,为什么面对很多问题我们总是熟视无睹,为什么总是要等到新的技术产生时,才会发现原来使用的技术有这样那样的问题?我瞎猜了几个原因:

技术能力不够,有的问题看不出来是问题。

只是使用,缺乏思考。

技术本来就是分层的工作,我们大多在应用层。

没拿那份钱,不考虑那些事。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值