Android 关于使用 Hilt 之 HiltAndroidApp(Application::class) 编译失败的问题解决

背景

阅读 sunflower 源码 android/sunflower 时发现其使用了 Hilt 组件,其中需要在 MainApplication 上添加 HiltAndroidApp 注解,查看 HiltAndroidApp 源码,发现其提供了两种使用方式:

/**
 * Annotation for marking the {@link android.app.Application} class where the Dagger components
 * should be generated. Since all components will be built in the same compilation as the annotated
 * application, all modules and entry points that should be installed in the component need to be
 * transitive compilation dependencies of the annotated application.
 *
 * <p>Usage of this annotation is similar to {@link dagger.hilt.android.AndroidEntryPoint} with the
 * only difference being that it only works on application classes and additionally triggers Dagger
 * component generation.
 *
 * <p>This annotation will generate a base class that the annotated class should extend, either
 * directly or via the Hilt Gradle Plugin. This base class will take care of injecting members into
 * the Android class as well as handling instantiating the proper Hilt components at the right point
 * in the lifecycle. The name of the base class will be "Hilt_<annotated class name>".
 *
 * <p>Example usage (with the Hilt Gradle Plugin):
 *
 * <pre><code>
 *   {@literal @}HiltAndroidApp
 *   public final class FooApplication extends Application {
 *     {@literal @}Inject Foo foo;
 *
 *     {@literal @}Override
 *     public void onCreate() {
 *       super.onCreate();  // The foo field is injected in super.onCreate()
 *     }
 *   }
 * </code></pre>
 *
 * <p>Example usage (without the Hilt Gradle Plugin):
 *
 * <pre><code>
 *   {@literal @}HiltAndroidApp(Application.class)
 *   public final class FooApplication extends Hilt_FooApplication {
 *     {@literal @}Inject Foo foo;
 *
 *     {@literal @}Override
 *     public void onCreate() {
 *       super.onCreate();  // The foo field is injected in super.onCreate()
 *     }
 *   }
 * </code></pre>
 *
 * @see AndroidEntryPoint
 */
// Set the retention to RUNTIME because we check it via reflection in the HiltAndroidRule.
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@GeneratesRootInput
public @interface HiltAndroidApp {
  /**
   * The base class for the generated Hilt application. When applying the Hilt Gradle Plugin this
   * value is not necessary and will be inferred from the current superclass.
   */
  // TODO(erichang): It would be nice to make this Class<? extends Application> but then the default
  // would have to be Application which would make the default actually valid even without the
  // plugin. Maybe that is a good thing...but might be better to have users be explicit about the
  // base class they want.
  Class<?> value() default Void.class;
}
  • 一种是使用 Hilt Gradle Plugin 的方式,操作上更简单:
@HiltAndroidApp
public final class FooApplication extends Application {
    @Inject Foo foo;

    @Override
    public void onCreate() {
        super.onCreate();  // The foo field is injected in super.onCreate()
    }
}
  • 另一种是不使用 Hilt Gradle Plugin 的方式,需要手动给 @HiltAndroidApp 设置 value 值,并且 XxxApplication 需继承成自动生成的 Hilt_XxxApplication
@HiltAndroidApp(Application.class)
public final class FooApplication extends Hilt_FooApplication {
    @Inject Foo foo;

    @Override
    public void onCreate() {
        super.onCreate();  // The foo field is injected in super.onCreate()
    }
}

问题

照葫芦画瓢,使用第一种方式时,一切 OK;使用第二种方式时,无论怎么操作都报如下错误(明明就继承了 Hilt_MainApplication 的,其实是 Hilt_MainApplication 没有正确生成出来):

public final class MainApplication {
^
@HiltAndroidApp class expected to extend Hilt_MainApplication. Found: Object
[Hilt] Processing did not complete. See error above for details.

解决

费了九牛二虎之力才找到解决问题的办法,在 dagger 官网 dagger.dev 有如下描述:


Using Hilt with Kotlin

If using Kotlin, then apply the kapt plugin and declare the compiler dependency using kapt instead of annotationProcessor.

Additionally configure kapt to correct error types by setting correctErrorTypes to true.

dependencies {
  implementation 'com.google.dagger:hilt-android:2.41'
  kapt 'com.google.dagger:hilt-compiler:2.41'

  // For instrumentation tests
  androidTestImplementation  'com.google.dagger:hilt-android-testing:2.41'
  kaptAndroidTest 'com.google.dagger:hilt-compiler:2.41'

  // For local unit tests
  testImplementation 'com.google.dagger:hilt-android-testing:2.41'
  kaptTest 'com.google.dagger:hilt-compiler:2.41'
}

kapt {
 correctErrorTypes true
}

Hilt Gradle plugin

The Hilt Gradle plugin runs a bytecode transformation to make the APIs easier to use. The plugin was created for a better developer experience in the IDE since the generated class can disrupt code completion for methods on the base class. The examples throughout the docs will assume usage of the plugin. To configure the Hilt Gradle plugin first declare the dependency in your project’s root build.gradle file:

buildscript {
  repositories {
    // other repositories...
    mavenCentral()
  }
  dependencies {
    // other plugins...
    classpath 'com.google.dagger:hilt-android-gradle-plugin:2.41'
  }
}

then in the build.gradle of your Android Gradle modules apply the plugin:

apply plugin: 'com.android.application'
apply plugin: 'dagger.hilt.android.plugin'

android {
  // ...
}

Warning: The Hilt Gradle plugin sets annotation processor arguments. If you are using other libraries that require annotation processor arguments, make sure you are adding arguments instead of overriding them. See below for an example.

Why use the plugin?

One benefit of the Gradle plugin is that it makes using @AndroidEntryPoint and @HiltAndroidApp easier because it avoids the need to reference Hilt’s generated classes.

Without the Gradle plugin, the base class must be specified in the annotation and the annotated class must extend the generated class:

@HiltAndroidApp(MultiDexApplication.class)
public final class MyApplication extends Hilt_MyApplication {}

With the Gradle plugin the annotated class can extend the base class directly:

@HiltAndroidApp
public final class MyApplication extends MultiDexApplication {}

注意:官网不知为什么打不开,可以直接在 google 搜索框中搜索该网址,然后查看快照即可(后来通过修改 C:\Windows\System32\drivers\etc\hosts 文件解决了)。

也就是说,使用了 kapt 的情况下,需要额外添加如下设置:

// 使用不带 Hilt Gradle Plugin 插件的方式需要添加该配置;
// 使用带 Hilt Gradle Plugin 插件的方式则不需要,估计插件里默认添加了该配置。
kapt {
    correctErrorTypes true
}

至此,终于成功编译过了。

另外,如果整个工程是 java 工程,即没有使用 koltin 的情况下,自然也不会用到 kapt,也不需要进行上面的配置了。对应的,使用 annotationProcessor 即可。

结论

总的来说,还是推荐使用带 Hilt Gradle Plugin 的方式,毕竟更简单;这里只是纠结了一下为什么按照其注解里的方式无法正常编译成功的原因,最终,问题得以解决。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容