Android Dagger不同作用域下的注入规则及相关实践疑问
Dagger 作用域相关问题解答
核心规则说明
Dagger 对作用域的判定是显式声明机制,不存在依赖链自动传递作用域的逻辑:
- 一个类的作用域归属,仅由该类自身标注的作用域注解、或是为该类提供实例的Module中声明的作用域决定,和它构造函数依赖的对象带什么限定符、什么作用域没有任何关系。
- 无作用域(Unscoped)类的特性是:每次被请求注入时都会生成新实例,不依附于任何组件的生命周期,所有能满足它全部依赖要求的组件,都可以创建它的实例。
问题1解答
Foo、Bar、Damn 完全不会自动归入 ActivityScope。
这三个类都没有标注任何作用域注解,本质都是无作用域类。哪怕 Damn 的构造函数用了 @ActivityContext 限定的Context参数,也只是要求创建 Damn 实例时必须拿到对应限定符的Context对象,不会给类本身赋予任何作用域属性,更不会顺着依赖链向上传递作用域标记。
问题2解答
这段代码确实会编译失败,但失败原因和你假设的作用域不匹配完全没有关系:
- 如问题1所述,
Foo/Bar/Damn根本没有被声明为ActivityScope,所以不存在“子作用域对象无法被ApplicationScope组件注入”的问题。 - 真正的编译失败原因是:标注
@ApplicationScope的HelloWorld隶属于ApplicationComponent,这个组件的依赖图里永远不会提供@ActivityContext限定的Context实例(这类绑定Activity上下文的依赖通常只在ActivityComponent中提供)。Dagger在为ApplicationComponent构建依赖图时,顺着依赖链找到Damn需要的@ActivityContextContext,发现整个图里没有能提供这个依赖的绑定,就会直接报错。 - 可以做个对照实验验证:如果把
Damn构造函数里的@ActivityContext改成@ApplicationContext,那哪怕依赖关系完全不变,ApplicationComponent也可以正常为HelloWorld注入Foo实例,根本不会触发任何作用域相关的编译报错。
问题3解答
在不修改 Foo/Bar/Damn 代码的前提下,完全可以实现这个需求,核心原则是不打破生命周期边界,不要把短生命周期的Activity依赖硬注入到Application层,推荐实现方案:
- 不要把
Foo作为构造注入的参数传给HelloWorld,改为在HelloWorld中维护一个针对当前前台ActivityComponent的弱引用持有者:- 在每个Activity创建对应的
ActivityComponent时,将这个Component的实例通过弱引用的方式注册到HelloWorld中;在Activity销毁时,清空对应的弱引用。 - 当
HelloWorld需要调用Foo的方法时,先检查持有的ActivityComponent弱引用是否有效(没有被GC回收、对应的Activity还在前台),如果有效就从这个Component中获取Foo实例执行调用;如果引用失效,就走对应的降级逻辑(比如跳过调用、等待Activity回到前台后再补执行)。
- 在每个Activity创建对应的
- 这个方案的优势是:完全不需要修改原有三个类的代码,也不会因为长期持有Activity上下文产生内存泄漏,同时严格遵守了不同层级的依赖生命周期边界。
内容的提问来源于stack exchange,提问作者user2062024
相关产品推荐
相关产品推荐

