主要讲解@HiltViewModel,用于修饰自定义ViewModel。
@DefaultComponent修饰的节点。
@InstallIn和@TestInstallIn、@Module、@EntryPoint和@EarlyEntryPoint和@GeneratedEntryPoint和@ComponentEntryPoint注解的处理。
@HiltViewModel注解 修饰的ViewModel类给MVVM项目提供ViewModel实例。
使用@HiltViewModel修饰的节点必须继承androidx.lifecycle.ViewModel;
使用@HiltViewModel修饰的节点的构造函数不允许使用@AssistedInject,如果使用了@Inject修饰,那么有且仅有一个@Inject修饰的构造函数,并且该@Inject修饰的构造函数不允许使用private修饰;
@HiltViewModel修饰的节点如果是内部类,那么需要使用static修饰;
@HiltViewModel修饰的节点不允许使用@Scope修饰的注解修饰。
生成$CLASS_HiltModules类。
demo:
@HiltViewModel
class UserListViewModel @Inject constructor(
private val userListRepository: UserListRepository
) : ViewModel() {...}
UserListViewModel生成UserListViewModel_HiltModules类:
@OriginatingElement(
topLevelClass = UserListViewModel.class
)
public final class UserListViewModel_HiltModules {
private UserListViewModel_HiltModules() {
}
@Module
@InstallIn(ViewModelComponent.class)
public abstract static class BindsModule {
private BindsModule() {
}
@Binds
@IntoMap
@StringKey("com.aregyan.github.views.userList.UserListViewModel")
@HiltViewModelMap
public abstract ViewModel binds(UserListViewModel vm);
}
@Module
@InstallIn(ActivityRetainedComponent.class)
public static final class KeyModule {
private KeyModule() {
}
@Provides
@IntoSet
@HiltViewModelMap.KeySet
public static String provide() {
return "com.aregyan.github.views.userList.UserListViewModel";
}
}
}
我们再去看ActivityComponent容器:
@subComponent
@ActivityScoped
public static abstract class ActivityComponent
implements
MainActivity_GeneratedInjector
{
//MainActivity_GeneratedInjector中的方法
//void injectMainActivity(MainActivity mainActivity);
InternalFactoryFactory getHiltInternalFactoryFactory();
ViewModelComponent.Builder getViewModelComponentBuilder();
FragmentComponent.Builder fragmentComponentBuilder();
@SubComponent.Builder
static interface Builder{
Builder activity(
@BindsInstance
Activity activity);
ActivityComponent build();
}
}
getHiltInternalFactoryFactory通过Injec修饰InternalFactoryFactory构造函数获取依赖:
@Inject
InternalFactoryFactory(
Application application,
@HiltViewModelMap.KeySet Set keySet,
ViewModelComponent.Builder viewModelComponentBuilder) {
this.application = application;
this.keySet = keySet;
this.viewModelComponentBuilder = viewModelComponentBuilder;
}
参数@HiltViewModelMap.KeySet Set keySet依赖来源于KeyModule的provide方法;
viewModelComponentBuilder来源于ViewModelComponent容器。
@InstallIn和@TestInstallIn、@Module、@EntryPoint和@EarlyEntryPoint和@GeneratedEntryPoint和@ComponentEntryPoint注解使用规则。
@InstallIn和@TestInstallIn:当前节点只允许使用其中的一个注解,只允许使用一次;
@EntryPoint、@EarlyEntryPoint、@GeneratedEntryPoint和@ComponentEntryPoint:当前节点只允许使用其中的一个注解,只允许使用一次;
(1)@EntryPoint和@GeneratedEntryPoint的区别在于(当然并不是强制的要求),@EntryPoint是用户使用的注解,@GeneratedEntryPoint是通过hilt源码生成的类使用的该注解;
(2)@EarlyEntryPoint修饰的节点应用于测试环境;
@Module:当前节点只允许使用一次Module注解;
@InstallIn或@TestInstallIn注解修饰的节点。该节点要么使用@Module修饰,要么使用@EntryPoint或@EarlyEntryPoint或@GeneratedEntryPoint或@ComponentEntryPoint修饰;
@Module注解和@EntryPoint(或@EarlyEntryPoint或@GeneratedEntryPoint或@ComponentEntryPoint)注解不可以同时使用;
@InstallIn或@TestInstallIn注解修饰的节点同时使用@Module修饰,我们称之为module节点:
(1)module节点必须是类或接口;
(2)module节点是顶级类(再上一级就是包) || module节点使用static修饰 || module节点使用abstract修饰 || module节点的父节点使用@HiltAndroidTest修饰;
(3)module节点是ApplicationContextModule类 || module节点不需要实例化(module节点何时需要实例化:module节点中的所有bindingMethod方法既不是static就是也不是abstract修饰,并且module不是 Kotlin compainionObject类型) || module不存在构造函数,或者构造函数无参并且没有使用private修饰;
(4) module节点中abstract修饰的bindingMethod方法必须使用@Binds或@Multibinds或@Provides或@BindsOptionalOf修饰;
(5)@TestInstallIn修饰的module节点不能嵌套在(或源自)@HiltAndroidTest 注释的类中;
(6)@TestInstallIn注解的replaces方法必须存在至少一个值;
(7) @TestInstallIn注解的replaces方法中的值类型必须使用@InstallIn修饰;
(8)@TestInstallIn#replaces()中的值类名称不允许使用"HiltWrapper_"作为开头;
(9) 如果当前module节点所在的包路径不是dagger.hilt开头,那么@TestInstallIn#replaces()的值类也不能在dagger.hilt开头开头的包下;
(10)@TestInstallIn#replaces()的值类型不能嵌套在(或源自)@HiltAndroidTest 注释的类中;
(1)@TestInstallIn注解只能和@Module一起使用,不能和@EntryPoint或@EarlyEntryPoint或@GeneratedEntryPoint或@ComponentEntryPoint一起使用;
(2)entryPoint节点只能是接口;
(3)@EarlyEntryPoint修饰的节点使用的注解@InstallIn的value值类型只能是SingletonComponent接口;
(4)@EarlyEntryPoint 修饰的节点 不能嵌套在(或源自)@HiltAndroidTest 修饰的类 中。 此要求是为了避免与其他特定于测试的入口点混淆。
@DefaultComponent修饰的节点不需要我们处理。
@DefineComponent#parent()的层次遍历,不能出现循环节点;
@DefineComponent只能修饰接口;
@DefineComponent修饰的接口不能继承别的接口;
@DefineComponent修饰的接口不能使用泛型;
@DefineComponent修饰的接口中如果存在方法,则必须是static修饰;
@DefineComponent修饰的接口不可以使用@AliasOf注解修饰;
@DefineComponent#parent中的节点类型不能是error,应该是正常的类或接口;
@DefineComponent#parent中的节点类型默认是DefineComponentNoParent类;如果不是,则必须是使用@DefineComponent修饰的接口;
@DefineComponent#parent中的节点使用了@DefineComponent修饰 || 当前component节点就是SingletonComponent接口;
component节点可以是dagger.hilt.components包下的SingletonComponent接口,但是绝对不可以是其他包下的SingletonComponent类或接口;
hilt源码中有哪些@DefaultComponent和@DefaultComponent.Builder修饰的节点:
SingletonComponent作用范围是Application;
@Singleton
@DefineComponent
public interface SingletonComponent {}
ActivityRetainedComponent作用范围是Application以下,Activity和Service以上;
@ActivityRetainedScoped
@DefineComponent(parent = SingletonComponent.class)
public interface ActivityRetainedComponent {}
@DefineComponent.Builder
public interface ActivityRetainedComponentBuilder {
ActivityRetainedComponent build();
}
ViewModelComponent作用范围是Application以下:
@ViewModelScoped
@DefineComponent(parent = ActivityRetainedComponent.class)
public interface ViewModelComponent {}
@DefineComponent.Builder
public interface ViewModelComponentBuilder {
ViewModelComponentBuilder savedStateHandle(@BindsInstance SavedStateHandle handle);
ViewModelComponent build();
}
ActivityComponent的作用范围是Activity:
@ActivityScoped
@DefineComponent(parent = ActivityRetainedComponent.class)
public interface ActivityComponent {}
@DefineComponent.Builder
public interface ActivityComponentBuilder {
ActivityComponentBuilder activity(
@BindsInstance
Activity activity);
ActivityComponent build();
}
ServiceComponent作用范围是Service:
@ServiceScoped
@DefineComponent(parent = SingletonComponent.class)
public interface ServiceComponent {}
@DefineComponent.Builder
public interface ServiceComponentBuilder {
ServiceComponentBuilder service(@BindsInstance Service service);
ServiceComponent build();
}
ViewComponent作用范围是view - Activity中的View:
@ViewScoped
@DefineComponent(parent = ActivityComponent.class)
public interface ViewComponent {}
@DefineComponent.Builder
public interface ViewComponentBuilder {
ViewComponentBuilder view(@BindsInstance View view);
ViewComponent build();
}
FragmentComponent作用范围是Fragment:
@FragmentScoped
@DefineComponent(parent = ActivityComponent.class)
public interface FragmentComponent {
}
@DefineComponent.Builder
public interface FragmentComponentBuilder {
FragmentComponentBuilder fragment(@BindsInstance Fragment fragment);
FragmentComponent build();
}
ViewWithFragmentComponent作用范围是Fragment中使用View:
@ViewScoped
@DefineComponent(parent = FragmentComponent.class)
public interface ViewWithFragmentComponent {}
@DefineComponent.Builder
public interface ViewWithFragmentComponentBuilder {
ViewWithFragmentComponentBuilder view(@BindsInstance View view);
ViewWithFragmentComponent build();
}
(1)@Component @Singleton SingletonComponent;
(2)@Subcomponent @ActivityRetainedScoped ActivityRetainedComponent;
(3)@Subcomponent @ServiceScoped ServiceComponent;
(4)@Subcomponent @ViewModelScoped ViewModelComponent;
(5)@Subcomponent @ActivityScoped ActivityComponent;
(6)@Subcomponent @ViewScoped ViewComponent;
(7)@Subcomponent @FragmentScoped FragmentComponent;
(8)@Subcomponent @ViewScoped ViewWithFragmentComponent;
(1)SingletonComponent容器,这里其实hilt源码中没有对应的@DefineComponent.Builder,但是为了理解,我拼上去的。
@Component
@Singleton
public abstract class SingletonComponent
{
ActivityRetainedComponent.Builder retainedComponentBuilder();
ServiceComponent.Builder serviceComponentBuilder();
@Component.Builder
public static interface Builder{
Builder applicationContextModule(ApplicationContextModule context);
SingletonComponent build();
}
}
(2)ActivityRetainedComponent容器
@subComponent
@ActivityRetainedScoped
public static abstract class ActivityRetainedComponent{
ActivityComponentBuilder activityComponentBuilder();
@SubComponent.Builder
static interface Builder{
ActivityRetainedComponent build();
}
}
(3)ServiceComponent容器
@subComponent
@ServiceScoped
public static abstract class ServiceComponent{
@SubComponent.Builder
static interface Builder{
Builder service(@BindsInstance Service service);
ServiceComponent build();
}
}
(4)ViewModelComponent容器
@subComponent
@ViewModelScoped
public static abstract class ViewModelComponent
{
@SubComponent.Builder
static interface Builder{
Builder savedStateHandle(@BindsInstance SavedStateHandle handle);
ViewModelComponent build();
}
}
(5)ActivityComponent容器
@subComponent
@ActivityScoped
public static abstract class ActivityComponent
{
ViewModelComponentBuilder getViewModelComponentBuilder();
FragmentComponentBuilder fragmentComponentBuilder();
@SubComponent.Builder
static interface Builder{
Builder activity(
@BindsInstance
Activity activity);
ActivityComponent build();
}
}
(6)ViewComponent容器
@subComponent
@ViewScoped
public static abstract class ViewComponent
{
@SubComponent.Builder
static interface Builder{
Builder view(@BindsInstance View view);
ViewComponent build();
}
}
(7)FragmentComponent容器
@subComponent
@FragmentScoped
public static abstract class FragmentComponent
{
@SubComponent.Builder
static interface Builder{
Builder fragment(@BindsInstance Fragment fragment);
FragmentComponent build();
}
}
(8)ViewWithFragmentComponent容器
@subComponent
@ViewScoped
public static abstract class ViewWithFragmentComponent
{
@SubComponent.Builder
static interface Builder{
Builder view(@BindsInstance View view);
ViewWithFragmentComponent build();
}
}
e.g.@InstallIn(SingletonComponent::class),那么关联的一定是SingletonComponent容器;
(1)@InstallIn(component容器.class) 和 @Module修饰的节点汇集到表示@(Sub)Component#modules里面;案例:
@InstallIn(SingletonComponent::class)
@Module
object DatabaseModule {
@Provides
@Singleton
fun provideAppDatabase(@ApplicationContext appContext: Context): UsersDatabase {
return Room.databaseBuilder(
appContext,
UsersDatabase::class.java,
"Users"
).fallbackToDestructiveMigration().build()
}
@Provides
fun provideChannelDao(usersDatabase: UsersDatabase): UsersDao {
return usersDatabase.usersDao
}
}
//下面是将当前module节点加入到SingletonComponent#modules中
@Component(modules = [
DatabaseModule.class,
NetworkModule.class,
//hilt中源码,非常简单,自行去看下:为DatabaseModule提供Context参数依赖
ApplicationContextModule.class
])
@Singleton
public abstract class SingletonComponent
{
ActivityRetainedComponent.Builder retainedComponentBuilder();
ServiceComponent.Builder serviceComponentBuilder();
@Component.Builder
public static interface Builder{
Builder applicationContextModule(ApplicationContextModule context);
SingletonComponent build();
}
}
还有上面@HiltViewModel修饰的节点生成的UserListViewModel_HiltModules类存在@Module和@InstallIn修饰的节点,这里可以自行尝试下,不表;
(2)@InstallIn(component容器.class) 和 @EntryPoint(或@GeneratedEntryPoint)修饰的节点 表示component容器 继承当前节点;案例@HiltAndroidApp修饰的MyApplication生成MyApplication_GeneratedInjector:
@OriginatingElement(
topLevelClass = MyApplication.class
)
@GeneratedEntryPoint
@InstallIn(SingltonComponent.class)
public interface MyApplication_GeneratedInjector {
void injectMyApplication(MyApplication myApplication);
}
//当前接口会被SingltonComponent容器继承,如下
@Component(modules = [
DatabaseModule.class,
NetworkModule.class,
//hilt中源码,非常简单,自行去看下:为DatabaseModule提供Context参数依赖
ApplicationContextModule.class
])
@Singleton
public abstract class SingletonComponent implements MyApplication_GeneratedInjector
{
ActivityRetainedComponent.Builder retainedComponentBuilder();
ServiceComponent.Builder serviceComponentBuilder();
@Component.Builder
public static interface Builder{
Builder applicationContextModule(ApplicationContextModule context);
SingletonComponent build();
}
}
会实现当前injectMyApplication方法,完成对MyApplication的注入。