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的术语就是dependency
与subcomponent
.
下面分别举例子来说明其中的区别和使用方法.
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
提供对应Activity
的AndroidInjector.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.Android
自2.10
版本后推出,只有不到三个月,可见Dagger2
还在不断自我强大的过程中,它的出现使得Android
开发在很多层面变的简单,如果有希望进一步学习的朋友,可以参考官方文档和一些Google的Sample,会在最后的Reference
给出连接.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
2016-10-09 Android Weekly Notes Issue #225