背景
阅读 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
的方式,毕竟更简单;这里只是纠结了一下为什么按照其注解里的方式无法正常编译成功的原因,最终,问题得以解决。