zoukankan      html  css  js  c++  java
  • Dagger2进阶必备技能

    之前写过一篇文章介绍Dagger2的初步知识, 本篇文章主要介绍Dagger2的进阶知识点.

    主要包含的内有有

    • @Binds与@Provides的使用
    • Provider与Lazy的使用
    • 依赖与包含
    • Dagger.Android

    @Binds与@Provides

    相信大家经常会使用@Provides来在Module里面提供需要注入对象的构造, 但从来没有用过@Binds.

    如果我们需要注入一个接口的实现,我们常常会这么做:

    @Provides
    public XXInterface providesXX(XXImp imp) {
    	return imp;
    }
    

    其实这样的代码可以通过@Binds简化为

    @Binds
    public abstract XXInterface bindXX(XXImp imp);
    

    同时你需要将你的Module改为abstract即可,但是要注意这两个不能共存于一个Module,是不是很简单.

    Provider与Lazy

    大家想必会使用过Lazy,很多语言都有Lazy,如最近大红大紫的Kotlin就可以通过by lazy {}来实现对象的延迟加载.

    没错,Lazy<T>也是如此,只有当你调用get()时,才会真正注入.

    Provider<T>与之的区别在于,Lazy延迟加载之后每次的调用都是同一个对象,而Provider则要看注入对象的实现,如果是通过@Scope约束的对象,则是同一个,否则每次都会创建新的.

    依赖于包含

    字面意思很好理解,翻译成Dagger的术语就是dependencysubcomponent.

    下面分别举例子来说明其中的区别和使用方法.

    Dependency

    AnimalComponent

    @Component(
            dependencies = FoodComponent.class,
            modules = AnimalModule.class
    )
    public interface AnimalComponent {
        Animal getAnimal();
    }
    

    AnimalModule

    @Module
    class AnimalModule {
    	@Provides
    	public Animal providesAnimal(Food food) {
    		//Animal需要另外一个Component提供的Food来创建
    		return new Animal(food);
    	}
    }
    

    FoodComponent

    @Component(modules = FoodModule.class)
    public interface FoodComponent {
        //这个是关键,必须显示指出可以提供Food对象的生成
        Food getFood();
    }
    

    这样我们可以通过传入FoodComponent来完成注入, 如下:

    DaggerAnimalComponent.builder().foodComponent(foodComponent).build().getAnimal()
    

    Subcomponent

    与依赖不同,Subcomponent拥有主Component所有注入对象,也就是说Subcomponent可以注入更多的对象, 通过生成代码也可以看出, 它的实现是主Component的内部类.

    Cat

    @Subcomponent(modules = {CatModule.class})
    public interface CatComponent {
        Cat getCat();
    }
    
    

    CatModule

    @Module
    public class CatModule {
        @Provides
    	public Cat providesCat(Leg leg//Animal Component提供) {
    		return Cat(leg);
    	}
    }
    

    我们还必须在AnimalComponent显示提供CatComponent,因为如上所述,Cat是Animal的内部类了.

    @Component(
            dependencies = FoodComponent.class,
            modules = AnimalModule.class
    )
    public interface AnimalComponent {
        Animal getAnimal();
        CatComponent createCatComponent();
    }
    

    这样我们就可以通过下面的办法来实现Cat的注入:

    DaggerAnimalComponent.build().createCatComponent().getCat();
    

    Subcomponent with explicit builder

    当我们AnimalComponent需要对Cat进行修改再输出的话(如指定猫的名字),可能就需要为CatComponent提供Builder

    @Subcomponent(modules = {CatModule.class})
    public interface CatComponent {
        @Subcomponent.Builder
        interface Builder {
            @BindsInstance Builder name(String name);
            CatComponent build();
        }
    }
    

    然后我们需要在AnimalModule里面使用这个Builder

    @Module(subcomponents = CatComponent.class)//注意这里需要加上这一条声明
    class AnimalModule {
    	@Provides
    	public Animal providesAnimal(Food food) {
    		//Animal需要另外一个Component提供的Food来创建
    		return new Animal(food);
    	}
    	@Provides
    	public CatComponent providesCatComponent(CatComponent.Builder builder) {
    		//这里只是举个例子,可能这里的Cat构造依赖于Animal的另外属性
    		return builder.name("喵喵").build();
    	}
    }
    

    Dagger.Android

    平时我们注入的时候常常都是将Component存在Application里面,然后在Acitivity的onCreate或者Fragment的onAttach, 通过静态对象Applicate.component.inject(xxx)或者((XXApplication)getApplication()).getComponent().inject(xxx)来注入.

    我们常常需要记住在固定的生命周期里面调用固定的语句,如果你的Activity或者Fragment在别的module里面使用公开的接口,对于Fragment你还可以对其对象进行注入(inject(fragmentInstance)),然而对Activity可能就没有很好的办法了...

    Google的Dagger2提供了一套针对Android的东西,帮助你只需要调用AndroidInjection.inject(this)或者AndroidSupportInjection.inject(this)来注入,甚至还可以通过添加一些监听器,达到自动注入的效果哦.

    下来看看如何实现吧

    添加依赖

    //x>=10
    implementation 'com.google.dagger:dagger-android:2.x'
    // if you use the support libraries
    implementation 'com.google.dagger:dagger-android-support:2.x'
    annotationProcessor 'com.google.dagger:dagger-android-processor:2.x'
    

    引入AndroidInjectionModule.class到你的Component

    提供继承AndroidInjector<T>Subcomponent, 及其Builder

    @Subcomponent(modules = ...)
    public interface YourActivitySubcomponent extends AndroidInjector<YourActivity> {
      @Subcomponent.Builder
      public abstract class Builder extends AndroidInjector.Builder<YourActivity> {}
    }
    

    通过Builder提供对应ActivityAndroidInjector.Factory

    @Module(subcomponents = YourActivitySubcomponent.class)
    abstract class YourActivityModule {
      @Binds
      @IntoMap
      @ActivityKey(YourActivity.class)
      abstract AndroidInjector.Factory<? extends Activity>
          bindYourActivityInjectorFactory(YourActivitySubcomponent.Builder builder);
    }
    
    @Component(modules = {..., YourActivityModule.class})
    interface YourApplicationComponent {}
    

    Tips

    类似于之前介绍Subcomponent.Builder, 如果你不需要定制该Builder, 如添加方法之类的, 那么这上面两步可以做简化.

    @Module
    abstract class YourActivityModule {
        @ContributesAndroidInjector(modules = { /* modules to install into, like FragmentModule */ })
        abstract YourActivity contributeYourActivityInjector();
    }
    

    参数module可以把想要注入的Fragment抽象到YourFragmentModule一并注入.

    实现HasActivityInjector

    public class YourApplication extends Application implements HasActivityInjector {
      @Inject DispatchingAndroidInjector<Activity> dispatchingActivityInjector;
    
      @Override
      public void onCreate() {
        super.onCreate();
        DaggerYourApplicationComponent.create()
            .inject(this);
      }
    
      @Override
      public AndroidInjector<Activity> activityInjector() {
        return dispatchingActivityInjector;
      }
    }
    

    实际上所有的注入构造的工场方法AndroidInjector.Factory都被存入了一个Map保存在DispatchingAndroidInjector,我们可以查看其生成的代码,其中核心逻辑在maybeInject(T instance)

    public boolean maybeInject(T instance) {
        Provider<AndroidInjector.Factory<? extends T>> factoryProvider =
            injectorFactories.get(instance.getClass());
        if (factoryProvider == null) {
          return false;
        }
        AndroidInjector.Factory<T> factory = (AndroidInjector.Factory<T>) factoryProvider.get();
        try {
          AndroidInjector<T> injector = factory.create(instance);
          injector.inject(instance);
          return true;
        } catch (ClassCastException e) {
            ...
        }
      }
    

    这就是其之所以能通过AndroidInjection.inject(this)实现注入的核心原理所在,Fragment同理.

    当然你需要通过持有AndroidInjector Module的Component将这个DispatchingAndroidInjector注入了才行,一般可以在Application里面做.

    实现自动注入.

    由于使用Dagger.android扩展使注入入口得到统一,那么就可以通过添加监听的方式在activity与fragment创建的时候实现自动注入.

    当然相信之后此部分代码可能会被融入进Dagger2.

    Application.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
                        @Override
                        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                            handleActivity(activity);
                        }
                        ....
                    }
                    
    
    private static void handleActivity(Activity activity) {
            if (activity instanceof HasSupportFragmentInjector) {
                AndroidInjection.inject(activity);
            }
            if (activity instanceof FragmentActivity) {
                ((FragmentActivity) activity).getSupportFragmentManager()
                        .registerFragmentLifecycleCallbacks(
                                new FragmentManager.FragmentLifecycleCallbacks() {
                                    @Override
                                    public void onFragmentCreated(FragmentManager fm, Fragment f,
                                            Bundle savedInstanceState) {
                                        if (f instanceof Injectable) {
                                            AndroidSupportInjection.inject(f);
                                        }
                                    }
                                }, true);
            }
        }
    

    总结

    Dagger2.Android2.10版本后推出,只有不到三个月,可见Dagger2还在不断自我强大的过程中,它的出现使得Android开发在很多层面变的简单,如果有希望进一步学习的朋友,可以参考官方文档和一些Google的Sample,会在最后的Reference给出连接.

    Demo

    Github

    Reference

  • 相关阅读:
    P2523 [HAOI2011]Problem c
    P2518 [HAOI2010]计数
    P2513 [HAOI2009]逆序对数列
    P2519 [HAOI2011]problem a
    P5020 货币系统
    P2580 于是他错误的点名开始了(Trie)
    P3805 【模板】manacher算法
    基础
    白兔的字符串(hash入门)
    ACM的分类训练题集(转载)
  • 原文地址:https://www.cnblogs.com/mengdd/p/advanced-dagger2-skills.html
Copyright © 2011-2022 走看看