4.基于Dagger2.38.1的hilt源码-@HiltViewModel注解处理

314 阅读2分钟

前言

ViewModelValidationPlugin用于校验@HiltViewModel注解和外部的关联。ViewModelProcessor类处理@HiltViewModel。当前使用该注解的节点校验并且生成ViewModelMetadata对象,针对该ViewModelMetadata对象再生成类。

ViewModelValidationPlugin

代码本身存在缺陷,所以做了简单处理,重点看visitGraph(bindingGraph: BindingGraph, diagnosticReporter: DiagnosticReporter)方法即可。其他的不用管。

@HiltViewModel修饰的目的在于使用当前ViewModelProcessor创建ViewModel 实例;

现已一个案例来说明:

@Module
public class XXModule{
	@Binds
    @IntoMap
    @StringKey(...)
    @HiltViewModelMap
    abstract ViewModel bindViewModel(FooViewModel vm)

}

以上bindViewModel方法的依赖匹配的是FooViewModel类使用@Inject修饰的构造函数,如下:

@HiltViewModel
class FooViewModel extends ViewModel{
	@Inject
	FooViewModel(){}
}

为了方便讲解,这里bindViewModel方法我们叫源头节点,该方法参数生成的依赖我们叫边,再把FooViewModel类使用@Inject修饰的构造函数我们称之为目标节点。

用于判断在某些情况下不允许使用@HiltViewModel的情况(即不能创建通过@HiltViewModel来创建ViewModule实例)。

如果目标节点是@Inject修饰的构造函数,并且该构造函数所在类使用了@HiltViewModel修饰,那么源头节点必须满足 :源头节点 必须是Binding对象(注释1) && 当前Binding对象的key使用@HiltViewModelMap注解修饰,并且当前Binding对象的key使用@IntoMap或@IntoSet或@ElementsIntoSet注解修饰;

注释1:可以自行查看Dagger关系图<component代码实现核心>哪些情况下可以生成Binding对象;

校验并且生成ViewModelMetadata对象

@HiltViewModel修饰的注解规则

  1. 使用@HiltViewModel修饰的节点必须继承androidx.lifecycle.ViewModel;

  2. 使用@HiltViewModel修饰的节点的构造函数不允许使用@AssistedInject,如果使用了@Inject修饰,那么有且仅有一个@Inject修饰的构造函数,并且该@Inject修饰的构造函数不允许使用private修饰;

  3. @HiltViewModel修饰的节点如果是内部类,那么需要使用static修饰;

  4. @HiltViewModel修饰的节点不允许使用@Scope修饰的注解修饰。

ViewModelMetadata对象对象属性

val typeElement: TypeElement - 就一个属性,当前@HiltViewModel修饰的节点。

ViewModelModuleGenerator生成类

demo如下

  1. UserListViewModel

     @HiltViewModel
     class UserListViewModel @Inject constructor(
         private val userListRepository: UserListRepository
     ) : ViewModel() {...}
    
  2. UserDetailsViewModel

     @HiltViewModel
     class UserDetailsViewModel @Inject constructor(
         private val userDetailsRepository: UserDetailsRepository
     ) : ViewModel() {...}
    

@HiltViewModel修饰的节点生成的类

  1. UserDetailsViewModel_HiltModules

     @OriginatingElement(
         topLevelClass = UserDetailsViewModel.class
     )
     public final class UserDetailsViewModel_HiltModules {
       private UserDetailsViewModel_HiltModules() {
       }
     
       @Module
       @InstallIn(ViewModelComponent.class)
       public abstract static class BindsModule {
         private BindsModule() {
         }
     
         @Binds
         @IntoMap
         @StringKey("com.aregyan.github.views.userDetails.UserDetailsViewModel")
         @HiltViewModelMap
         public abstract ViewModel binds(UserDetailsViewModel 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.userDetails.UserDetailsViewModel";
         }
       }
     }
    
  2. 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";
     }
    

    } }

总结

@HiltViewModel修饰的节点生成的类包含static修饰的@Module类,该类使用@Binds、@IntoMap、@StringKey的目的是收集实例化的viewModule类。

至此hilt关于android模块已完成,下面是针对android或其他注解的进一步生成工作。