3.基于Dagger2.38.1版本源码理解hilt注解-@HiltViewModel和@InstallIn处理

3.基于Dagger2.38.1版本源码理解hilt注解-@HiltViewModel和@InstallIn处理,第1张

前言

主要讲解@HiltViewModel,用于修饰自定义ViewModel。

@DefaultComponent修饰的节点。

@InstallIn和@TestInstallIn、@Module、@EntryPoint和@EarlyEntryPoint和@GeneratedEntryPoint和@ComponentEntryPoint注解的处理。

@HiltViewModel注解

@HiltViewModel注解 修饰的ViewModel类给MVVM项目提供ViewModel实例。

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

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

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

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

@HiltViewModel注解修饰节点生成代码

生成$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;
}
  1. 参数@HiltViewModelMap.KeySet Set keySet依赖来源于KeyModule的provide方法;

  2. viewModelComponentBuilder来源于ViewModelComponent容器。

@InstallIn和@TestInstallIn、@Module、@EntryPoint和@EarlyEntryPoint和@GeneratedEntryPoint和@ComponentEntryPoint注解 使用规则如下

@InstallIn和@TestInstallIn、@Module、@EntryPoint和@EarlyEntryPoint和@GeneratedEntryPoint和@ComponentEntryPoint注解使用规则。

  1. @InstallIn和@TestInstallIn:当前节点只允许使用其中的一个注解,只允许使用一次;

  2. @EntryPoint、@EarlyEntryPoint、@GeneratedEntryPoint和@ComponentEntryPoint:当前节点只允许使用其中的一个注解,只允许使用一次;

  • (1)@EntryPoint和@GeneratedEntryPoint的区别在于(当然并不是强制的要求),@EntryPoint是用户使用的注解,@GeneratedEntryPoint是通过hilt源码生成的类使用的该注解;

  • (2)@EarlyEntryPoint修饰的节点应用于测试环境;

  1. @Module:当前节点只允许使用一次Module注解;

  2. @InstallIn或@TestInstallIn注解修饰的节点。该节点要么使用@Module修饰,要么使用@EntryPoint或@EarlyEntryPoint或@GeneratedEntryPoint或@ComponentEntryPoint修饰;

  3. @Module注解和@EntryPoint(或@EarlyEntryPoint或@GeneratedEntryPoint或@ComponentEntryPoint)注解不可以同时使用;

  4. @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. @InstallIn注解修饰的节点同时使用@EntryPoint或@EarlyEntryPoint或@GeneratedEntryPoint或@ComponentEntryPoint修饰,当前节点称之为entryPoint节点:
  • (1)@TestInstallIn注解只能和@Module一起使用,不能和@EntryPoint或@EarlyEntryPoint或@GeneratedEntryPoint或@ComponentEntryPoint一起使用;

  • (2)entryPoint节点只能是接口;

  • (3)@EarlyEntryPoint修饰的节点使用的注解@InstallIn的value值类型只能是SingletonComponent接口;

  • (4)@EarlyEntryPoint 修饰的节点 不能嵌套在(或源自)@HiltAndroidTest 修饰的类 中。 此要求是为了避免与其他特定于测试的入口点混淆。

  1. @InstallIn的value值或者@TestInstallIn的components值类型 - 必须使用@DefineComponent修饰;
@DefaultComponent修饰的节点

@DefaultComponent修饰的节点不需要我们处理。

校验规则
  1. @DefineComponent#parent()的层次遍历,不能出现循环节点;

  2. @DefineComponent只能修饰接口;

  3. @DefineComponent修饰的接口不能继承别的接口;

  4. @DefineComponent修饰的接口不能使用泛型;

  5. @DefineComponent修饰的接口中如果存在方法,则必须是static修饰;

  6. @DefineComponent修饰的接口不可以使用@AliasOf注解修饰;

  7. @DefineComponent#parent中的节点类型不能是error,应该是正常的类或接口;

  8. @DefineComponent#parent中的节点类型默认是DefineComponentNoParent类;如果不是,则必须是使用@DefineComponent修饰的接口;

  9. @DefineComponent#parent中的节点使用了@DefineComponent修饰 || 当前component节点就是SingletonComponent接口;

  10. component节点可以是dagger.hilt.components包下的SingletonComponent接口,但是绝对不可以是其他包下的SingletonComponent类或接口;

@DefaultComponent修饰的节点工作范围

hilt源码中有哪些@DefaultComponent和@DefaultComponent.Builder修饰的节点:

  1. SingletonComponent作用范围是Application;

     @Singleton
     @DefineComponent
     public interface SingletonComponent {}
    
  2. ActivityRetainedComponent作用范围是Application以下,Activity和Service以上;

     @ActivityRetainedScoped
     @DefineComponent(parent = SingletonComponent.class)
     public interface ActivityRetainedComponent {}
    
     @DefineComponent.Builder
     public interface ActivityRetainedComponentBuilder {
         ActivityRetainedComponent build();
     }
    
  3. ViewModelComponent作用范围是Application以下:

     @ViewModelScoped
     @DefineComponent(parent = ActivityRetainedComponent.class)
     public interface ViewModelComponent {}
    
     @DefineComponent.Builder
     public interface ViewModelComponentBuilder {
         ViewModelComponentBuilder savedStateHandle(@BindsInstance SavedStateHandle handle);
     
         ViewModelComponent build();
     }
    
  4. ActivityComponent的作用范围是Activity:

     @ActivityScoped
     @DefineComponent(parent = ActivityRetainedComponent.class)
     public interface ActivityComponent {}
    
     @DefineComponent.Builder
     public interface ActivityComponentBuilder {
         ActivityComponentBuilder activity(
                 @BindsInstance
                         Activity activity);
     
         ActivityComponent build();
     }
    
  5. ServiceComponent作用范围是Service:

     @ServiceScoped
     @DefineComponent(parent = SingletonComponent.class)
     public interface ServiceComponent {}
    
     @DefineComponent.Builder
     public interface ServiceComponentBuilder {
         ServiceComponentBuilder service(@BindsInstance Service service);
         ServiceComponent build();
     }
    
  6. ViewComponent作用范围是view - Activity中的View:

     @ViewScoped
     @DefineComponent(parent = ActivityComponent.class)
     public interface ViewComponent {}
    
     @DefineComponent.Builder
     public interface ViewComponentBuilder {
         ViewComponentBuilder view(@BindsInstance View view);
     
         ViewComponent build();
     }
    
  7. FragmentComponent作用范围是Fragment:

     @FragmentScoped
     @DefineComponent(parent = ActivityComponent.class)
     public interface FragmentComponent {
     }
    
     @DefineComponent.Builder
     public interface FragmentComponentBuilder {
         FragmentComponentBuilder fragment(@BindsInstance Fragment fragment);
         FragmentComponent build();
     }
    
  8. ViewWithFragmentComponent作用范围是Fragment中使用View:

     @ViewScoped
     @DefineComponent(parent = FragmentComponent.class)
     public interface ViewWithFragmentComponent {}
    
     @DefineComponent.Builder
     public interface ViewWithFragmentComponentBuilder {
         ViewWithFragmentComponentBuilder view(@BindsInstance View view);
     
         ViewWithFragmentComponent build();
     }
    
@DefaultComponent修饰的节点修饰的节点会生成Component容器
  1. @DefineComponent修饰的节点生成component容器,所以共有8个容器,并且@DefineComponent#parent使用的是@DefineComponent修饰的节点(默认是DefineComponentNoParent,非@DefineComponent修饰)表示subcomponent节点,否则表示component节点,如下所示:
  • (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. @DefineComponent.Builder修饰的接口根据build方法返回类型匹配component容器,生成代码如下:
  • (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();
    
     	 	}
     	 }
    
  1. @InstallIn#value的值一定是@DefaultComponent修饰的节点,表示@InstallIn修饰的节点会被归纳到哪一类容器中
  • 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的注入。

总结

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/733802.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-27
下一篇 2022-04-27

发表评论

登录后才能评论

评论列表(0条)

保存