【问题标题】:Dagger cannot create object graph although it can produce dot fileDagger 虽然可以生成点文件,但无法创建对象图
【发布时间】:2013-08-07 11:22:23
【问题描述】:

我正在努力在现有应用程序中设置 Dagger (1.0.1)。它被配置为使用 ProGuard,但我使用 -dontobfuscate 禁用了它。

当我启用 dagger-compiler 时,它能够成功地生成一个带有依赖关系图的点文件,但是当我删除编译器并在发布模式下构建应用程序时,它在启动时崩溃,抱怨它无法创建对象图。

java.lang.RuntimeException: Unable to start activity 
  ComponentInfo{com.corp.myapp/com.corp.myapp.ui.activity.MainActivity}: 
  java.lang.IllegalStateException: Errors creating object graph:

No injectable members on com.corp.myapp.core.services.ConnectionMonitor. Do 
  you want to add an injectable constructor? required by 
  com.corp.myapp.core.services.ConnectionMonitor 
  com.corp.myapp.ui.activity.MyAppBaseActivity.connectionManager

No injectable members on com.corp.myapp.ui.crouton.CroutonManager. Do you want 
  to add an injectable constructor? required by 
  com.corp.myapp.ui.crouton.CroutonManager 
  com.corp.myapp.ui.activity.MyAppBaseActivity.croutonManager

No injectable members on com.corp.core.assembler.ResourceAssembler. Do you want 
  to add an injectable constructor? required by 
  com.corp.core.assembler.ResourceAssembler 
  com.corp.myapp.ui.activity.MyAppBaseActivity.resourceAssembler

我看到MyAppBaseActivity,它与CroutonManagerConnectionMonitor 的依赖关系显示在生成的点文件中,所以根据this comment,我希望这可以工作。 AFAIK 如果有问题,我用来生成点文件的启用编译器的构建应该会检测到它。


更新:

我之前说过

在调试模式下它永远不会失败

但经过进一步测试后并非如此:在 Debug 模式下它不会失败,因为 ProGuard 被禁用,而在 Release 模式下它默认启用。如果我在发布模式下构建应用程序但跳过 ProGuard,我也不会收到错误并且应用程序成功启动。所以这个问题肯定和我的 ProGuard 配置有关。

【问题讨论】:

  • 为什么要删除发布模式的编译器?编译器旨在用于加快速度,因为在运行时不必进行注释处理。对我来说,通过删除 dagger-compiler 让应用程序在发布时运行速度变慢是没有意义的。
  • 是的,我知道编译器可以提供这样的性能改进(除了编译时的图形验证)。我禁用它是因为我也在使用 ProGuard,根据 Square 的人的说法,避免问题的方法是禁用编译器并让 Dagger 在运行时生成所有必需的代码。

标签: android dependency-injection proguard dagger


【解决方案1】:

Dagger 在很大程度上依赖于反射和类名,这些名称是硬编码和作为字符串操作的。这使得代码难以收缩/优化/混淆。

以下配置适用于示例dagger/examples/simple in Dagger 1.1.0

-keepattributes *Annotation*

-keepclassmembers,allowobfuscation class * {
    @javax.inject.* *;
    @dagger.* *;
    <init>();
}

-keep class **$$ModuleAdapter
-keep class **$$InjectAdapter
-keep class **$$StaticInjection

-keepnames !abstract class coffee.*

-keepnames class dagger.Lazy

配置保留所有带有javax.injectdagger 注释的字段和方法,以及所有无参数构造函数。如果它们看起来未使用,ProGuard 可能会以其他方式删除它们,但 Dagger 实际上是通过反射注入/访问它们。这类似于 RoboGuice。

它还必须保留 Dagger 生成的所有适配器类。

它还必须保留与这些适配器类相关的所有类名,因此名称仍然匹配。在这个示例中,这些几乎是包coffee 中的所有类,因此最简单的方法是使用通配符。对于其他应用程序,此行会有所不同。

最后,它还必须保留类的名称dagger.Lazy,因为它的名称在生成的代码中被硬编码为字符串。

【讨论】:

  • 感谢 Eric 的彻底回复!
  • 关于您自己的应用程序类的 keepnames 条目,到目前为止,我已将它们简化为两种可能的情况:1) 每当您通过相应的 provideXXX 方法将任何绑定添加到 Dagger 模块以获取新依赖项时,返回的类型必须添加到配置中。 2) 如果您有一个带有 @Inject 注释构造函数的类扩展了另一个尚未在 ProGuard 配置中过滤的类,则必须将该基类添加到配置中。
  • 大卫,如果我使用 dagger-compiler,我的印象是它不应该使用反射。在这种情况下,我应该在我的 proguard 文件中实现哪些配置?
  • 如果你像我一样不太了解 ProGuard 并试图复制上面的内容,在 coffee.* 中添加第二个 * 将导致它递归工作(coffee.** 将匹配 coffee.gui。任何)。我花了一点时间才弄清楚。
【解决方案2】:

在将-dontshrink 添加到 ProGuard 配置文件后,我启动了应用程序。开头有-dontobfuscate 是不够的。

事实上,如果我删除 -dontobfuscate 它也可以。

我确实需要对此进行更好的控制,但这是一个起点。我目前对 Dagger 的 ProGuard 设置是:

#############
#   Dagger  #
#############

-keep class dagger.** { *; }
-dontwarn dagger.internal.codegen.**

【讨论】:

  • 仅供参考:这适用于 dagger 1.0.1 但不适用于 1.1.0,我在访问模块适配器时收到与混淆代码相关的应用程序初始化错误。
【解决方案3】:

Dagger 不需要 @Inject 在要传递到 graph.inject(myActivity) 的类上,因为某些活动可能不需要进行任何注入。但是,这些看起来像是上游依赖项,这意味着它们需要提供给ComponentInfo,因此需要由 Dagger 提供。如果它不能创建这些类,它就不能这样做,如果它们没有注释,它就不能这样做,除非它通过@Provides 方法提供它们。

因此,您要么需要创建一个 @Module-annotated 类,该类从 @Provides-annotated 方法返回这些类型,要么您需要将 @Inject 添加到它们的构造函数中。

-keep class * extends dagger.internal.Binding

也就是说,在这种情况下,您是否在“发布”模式下使用 proguard?而不是在调试模式下进行保护?如果是这样,我怀疑 Proguard 正在剥离注释。你需要做一些变体:

-keep class javax.inject.** { *; }

... 以确保 Proguard 不会删除注释。

【讨论】:

  • Christian 我正在测试更多配置替代方案以尝试隔离问题,但到目前为止,避免应用程序崩溃的唯一方法是完全跳过 ProGuard。我会考虑您对与注入相关的 ProGuard 配置的建议,并使用更多详细信息更新问题。感谢您的反馈!
  • 另外,我在这里担心的是,如果禁用 dagger-compiler,Dagger 应该可以与 ProGuard 一起正常工作,因此它必须在运行时执行这些操作,从而失去预编译的性能增益.这对我来说似乎不是这样:/
  • 是的 - 如果编译器被禁用,dagger 在任何情况下都不应该正常工作。从 1.0(我认为或 1.1)开始,模块必须生成代码(尽管 @Inject 类不是)。我们试图通过不显式存储字符串来解决这个问题,并且在我们如何管理它时更加聪明。随时了解主要项目,您应该很快就会看到相关内容。
  • 嗯,我没想到根据 Jesse 在 plus.google.com/117210567825404150882/posts/HGjnJJHbfMj 的最后评论 -> “如果你从构建中删除 dagger-compiler,它应该是 proguard 安全的”。
【解决方案4】:

我最终花了一周的时间+试图让它发挥作用。最后我失败了,决定给 DexGuard 一个机会。它开箱即用,效果很好。是的,它是一种商业产品,但 DexGuard 有很大的支持,因此我们最终能够发货。如果你绝对需要解决这个问题,我绝对推荐 DexGuard。

【讨论】:

  • DexGuard 是由同一个人编写的。你知道有什么区别吗?我认为它们需要相同的配置,但 DexGuard 提供了额外的优化和安全性。
猜你喜欢
  • 2021-02-24
  • 2020-02-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-21
  • 2011-10-06
  • 1970-01-01
相关资源
最近更新 更多