注解是元数据,用于为代码(类、方法、字段等)添加标记信息,本身不直接影响程序行为。要学习如何使用别人定义的 Java 注解,通常要了解注解的作用目标、生命周期和元数据的处理细节。
1. 学习 Java 注解
1.1. 作用目标
@Target 决定了注解的作用目标,可以是下列之一或组合:
值 | 说明 |
---|---|
ElementType.TYPE | 类、接口、枚举、注解 |
ElementType.FIELD | 字段(包括枚举常量 |
ElementType.METHOD | 方法 |
ElementType.PARAMETER | 方法参数 |
ElementType.CONSTRUCTOR | 构造函数 |
ElementType.LOCAL_VARIABLE | 局部变量 |
ElementType.ANNOTATION_TYPE | 其他注解 |
ElementType.PACKAGE | 包声明 |
ElementType.TYPE_PARAMETER | 泛型类型参数 |
ElementType.TYPE_USE | 任何类型 |
[表1 注解作用目标] |
1.2. 生命周期
@Retention 决定了注解在程序的哪个生命周期阶段可以获取和使用。
值 | 说明 |
---|---|
RetentionPolicy.SOURCE | 仅保留在源码中,编译后丢弃 |
RetentionPolicy.CLASS | 保留到编译后的字节码文件(.class),但运行时 JVM 不加载 |
RetentionPolicy.RUNTIME | 保留到运行时,可通过反射获取 |
1.3. 元数据的处理者、处理时机和处理方式,以及如何影响程序行为
谁在什么时候,以什么方式处理这些元数据?结果对程序行为有什么样的改变?
@Nonnull 、 @Override 等注解常用于编译期代码检查。Lombok等注解在类加载期生成字节码。依赖注入注解通常由插件在编译期处理,生成注入代码;或者由框架在运行期框架初始化时,由框架注入对象。序列化/反序列化注解则是在运行期由序列化驱动类读取,来调整序列化/反序列化行为。
2. Hilt 注解
Hilt 注解可以分为4类:
|------------|----------------------------|
| 哪里需要注入对象 | @AndroidEntryPoint @Inject |
| 对象从哪里来 | @Provides @Binds @Module |
| 对象作用域和生命周期 | @Singleton @ActivityScoped |
| 组织和管理对象 | @HiltAndroidApp @InstallIn |
常用 Hilt 注解有
注解 | 目标 | 处理时机 | 说明 |
---|---|---|---|
@HiltAndroidApp | Application类 | 编译期 | 标记应用入口,触发 Hilt 代码生成。 |
@AndroidEntryPoint | Activity类 | 编译期 | 标记 Hilt 容器,容器可以注入对象。 |
@Module | 类 | 编译期 | 标记 Hilt 模块,模块负责创建对象。 |
@InstallIn | 类 | 编译期 | 这个模块可以安装到哪些容器。 |
@Privodes | 方法 | 编译期 | 这个方法可以创建注入对象。 |
@Binds | @Module类的抽象方法 | 编译期 | 这个方法需要绑定到实现类。 |
@Inject | 构造方法、域 | 编译期 | 这里需要注入对象。 |
@Singleton | 类、方法 | 编译期 | 这是单例。 |
@ActivityScoped | 类、方法 | 编译期 | 在Activity生命周期内是单例。 |
@FragmentScoped | 类、方法 | 编译期 | 在Fragment生命周期内是单例。 |
@ViewModelScoped | 类、方法 | 编译期 | 在ViewModel生命周期内是单例。 |
@ServiceScoped | 类、方法 | 编译期 | 在Service生命周期内是单例。 |
@EntryPoint | 接口 | 编译期 | 可以在非 Hilt 托管类中访问 Hilt 对象。 |
@HiltViewModel | ViewModel类 | 编译期 | 这个 ViewModel 类可以使用 Hilt 注入对象。 |
2.1. Hilt 注解示例
@HiltAndroidApp
public class MyApplication extends Application { ... }
@AndroidEntryPoint
public class MainActivity extends AppCompatActivity { ... }
@Module
@InstallIn(SingletonComponent.class) // 指定模块安装在哪个组件中
public class AppModule { ... }
@Module
@InstallIn(SingletonComponent.class)
public class NetworkModule {
@Provides
@Singleton // 作用域注解,表示单例
public OkHttpClient provideOkHttpClient() {
return new OkHttpClient.Builder().build();
}
}
@Module
@InstallIn(ActivityComponent.class)
public abstract class AnalyticsModule {
@Binds
public abstract AnalyticsService bindAnalyticsService(AnalyticsServiceImpl impl);
}
@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {
@Inject // Hilt 将注入一个 UserRepository 实例
UserRepository userRepository;
...
}
@ActivityScoped // 该 Repository 实例在同一个 Activity 内是单例
public class MainActivityRepository {
@Inject
public MainActivityRepository() { ... }
}
@Provides
@ActivityScoped
public MyDependency provideMyDependency() { ... }
@EntryPoint
@InstallIn(SingletonComponent.class)
public interface MyEntryPoint {
MyDependency getMyDependency();
}
// 在非 Hilt 类中使用
MyEntryPoint entryPoint = EntryPointAccessors.fromApplication(context, MyEntryPoint.class);
MyDependency dep = entryPoint.getMyDependency();
@HiltViewModel
public class MyViewModel extends ViewModel {
private final UserRepository userRepository;
@Inject
public MyViewModel(UserRepository userRepository) { // 依赖注入
this.userRepository = userRepository;
}
}
// 在 Activity/Fragment 中获取 (使用 by viewModels())