Android-Rxjava&retrofit&daggerAndroid-Dagger2andriod

Dagger2在Android平台上的新魔法

2017-11-09  本文已影响268人  珞泽珈群

0. 前言

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

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

1. 一个问题

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

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

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,里面有UserFragmentSearchFragment。定义两个ModuleMainActivityModuleFragmentBuildersModule

@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<MainActivity> {
        @Subcomponent.Builder
        abstract class Builder extends AndroidInjector.Builder<MainActivity> {}
    }
}

这是魔法得以发生的核心,里面包含了许多概念,让我们一一来看。
先来看几个dagger2中的功能。@Binds可以用于注解Moduleabstract方法,一般是用来把一个接口的实现绑定到接口上。@IntoMap@ActivityKey注解,向Map中加入数据。@Module(subcomponents = ...)其中的参数subcomponents可以指定一些subcomponent,这些subcomponent必须是该Module被使用的component的子component,这样这些Subcomponent.Builder就可以像普通对象一样被注入。

再来看看AndroidInjector

public interface AndroidInjector<T> {
    void inject(T instance);

    /**
    * Factory模式创建具体类型T的 AndroidInjector
    *
    * @param <T>  四大组件或Fragment
    */
    interface Factory<T> {
        AndroidInjector<T> create(T instance);
    }

    /**
    * Builder模式实现Factory
    */
    abstract class Builder<T> implements AndroidInjector.Factory<T> {
        @Override
        public final AndroidInjector<T> create(T instance) {
            seedInstance(instance);
            return build();
        }

        /**
         * 把四大组件或Fragment的实例instance绑定到AndroidInjector上
         */
        @BindsInstance
        public abstract void seedInstance(T instance);

        public abstract AndroidInjector<T> build();
    }
}

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

@Subcomponent(modules = FragmentBuildersModule.class)
public interface MainActivitySubcomponent extends AndroidInjector<MainActivity> {
    @Subcomponent.Builder
    abstract class Builder extends AndroidInjector.Builder<MainActivity> {}
}

MainActivitySubcomponent的定义完全借助AndroidInjector接口和AndroidInjector.Builder抽象类。其中,AndroidInjector.Builder通过seedInstance(T instance)方法,把MainActivity实例绑定到了MainActivitySubcomponent中,这样就可以在MainActivitySubcomponent关联的Module或其子Component中使用MainActivity实例了。
让我们来回顾一下到目前为止的流程:

ActivitySubcomponent流程

同理对于Fragment有:

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创建的地方实现HasActivityInjectorHasServiceInjectorHasBroadcastReceiverInjectorHasContentProviderInjectorHasFragmentInjectorHasSupportFragmentInjector),具体实现哪个接口,取决于子Component有哪些。

/**
* 对于Application,其子Component包括 *ActivitySubcomponent,所以实现 HasActivityInjector接口
*/
public class MyApp extends Application implements HasActivityInjector {
  @Inject 
  DispatchingAndroidInjector<Activity> dispatchingActivityInjector;

  @Override
  public void onCreate() {
    super.onCreate();
    DaggerAppComponent.builder()
        .application(this)
        .build()
        .inject(this);
  }

  @Override
  public AndroidInjector<Activity> activityInjector() {
    return dispatchingActivityInjector;
  }
}

/**
* 对于Activity,其子Component包括 *FragmentSubcomponent,所以实现 HasSupportFragmentInjector接口
*/
public class MainActivity extends AppCompatActivity implements HasSupportFragmentInjector {
    @Inject
    DispatchingAndroidInjector<Fragment> dispatchingAndroidInjector;

    @Override
    public DispatchingAndroidInjector<Fragment> supportFragmentInjector() {
        return dispatchingAndroidInjector;
    }
}

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

/**
 * @param <T> the core Android type to be injected
 */
public final class DispatchingAndroidInjector<T> implements AndroidInjector<T> {
    private final Map<Class<? extends T>, Provider<AndroidInjector.Factory<? extends T>>>
      injectorFactories;

    @Inject
    DispatchingAndroidInjector(
        Map<Class<? extends T>, Provider<AndroidInjector.Factory<? extends T>>> injectorFactories) {
        this.injectorFactories = injectorFactories;
    }

    /**
    * 对于instance尝试 members-injection
    */
    public boolean maybeInject(T instance) {        
        //通过instance.getClass()去查找对应的AndroidInjector.Factory,其实就是我们的AndroidInjector.Builder
        Provider<AndroidInjector.Factory<? extends T>> factoryProvider =
            injectorFactories.get(instance.getClass());
        if (factoryProvider == null) {
          return false;
        }

        @SuppressWarnings("unchecked")
        AndroidInjector.Factory<T> factory = (AndroidInjector.Factory<T>) factoryProvider.get();
        try {
            AndroidInjector<T> 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<Activity>
        AndroidInjector<Activity> 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() {}
}

对于ServiceBroadcastReceiverContentProvider,逻辑跟inject(Activity activity)方法是一样的:从Application中获得DispatchingAndroidInjector<Activity/Service/BroadcastReceiver/ContentProvider>,然后调用其inject方法完成成员注入。
对于android.support.v4.app.Fragment,逻辑稍微复杂一些:

/** Injects core Android types from support libraries. */
public final class AndroidSupportInjection {
    /**
    * 按照如下逻辑找到合适的 AndroidInjector<Fragment>:
    * 1. 查找所有的父fragment,看它是否实现了HasSupportFragmentInjector接口;如果没有则
    * 2. 看getActivity()的Activity是否实现了HasSupportFragmentInjector接口;如果没有则
    * 3. 看Application是否实现了HasSupportFragmentInjector接口
    */
    public static void inject(Fragment fragment) {
        checkNotNull(fragment, "fragment");
        HasSupportFragmentInjector hasSupportFragmentInjector = findHasFragmentInjector(fragment);
        
        AndroidInjector<Fragment> 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为例,回顾一下整体的流程。

总流程

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

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

4. 尾巴

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

上一篇下一篇

猜你喜欢

热点阅读