一屁柏、介紹
Hilt 提供了一種將Dagger依賴注入合并到Android應(yīng)用程序中的標準方法啦膜。
說白了,他就是針對android平臺對Dagger進行了封裝與擴展淌喻,并提供了android平臺特有的一些Component ,Scope等,內(nèi)置了預(yù)定義綁定僧家、預(yù)定義限定符等。
支持Android的類有:
Activity裸删、Application八拱、Fragment、View烁落、Service乘粒、BroadcastReceiver
二、基本使用
1伤塌、核心注解說明
1) @HiltAndroidApp注解
所有使用Hilt的應(yīng)用程序都必須要包含一個使用@HiltAndroidApp注解的appliaction灯萍。
因為在生成代碼時,需要訪問所有的module 每聪,編譯Application類還需要將所有Dagger模塊包含在其傳遞的依賴項中旦棉。其他組件可以訪問它提供的所有的依賴項。
@HiltAndroidApp注解是用來生成Hilt Application的药薯,如果使用了Hilt 的Gradle Plugin绑洛,那么他的參數(shù)value 就可以不指明,默認就是當前類的父類童本;如果沒有使用插件真屯,那么就需要指明value的類型。
2) @AndroidEntryPoint注解
該注解是表明你的依賴注入注入的位置穷娱。該注解目前僅支持在Activity绑蔫、Fragment、View泵额、Service配深、BroadcastReceiver中。
ViewModel的注入使用@HiltViewModel.
如果對Fragment使用依賴注入點嫁盲,那么他所在的Activity也必須添加該注解篓叶。
注意:
hilt僅支持擴展ComponentActivity的Activity;
Hilt只支持AndroidX下的Fragment;不支持保留的Fragment;
3) @InstallIn注解
該注解表明了當前的Module安裝在哪個Component上,在使用Dagger時,在每一個Module上需要加上@Module注解缸托,而在Hilt中是使用@Module和@InstallIn注解左敌;這其實就是Dagger中的一個標準的Module。
使用了@InstallIn注解的modules 嗦董,當Component生成后母谎,就會被安裝到關(guān)聯(lián)的Component或SubComponent上去;
通過給@InstallIn傳遞參數(shù)京革,來指定該模塊安裝到哪一個Component中去;
4) @Singleton注解
Hilt中的@Singleton注解必須要和ApplicationComponent組件一起使用幸斥,不能單獨使用匹摇。
5) @Binds注入接口實例
將接口與實現(xiàn)類綁定。例如:當構(gòu)造方法需要傳遞一個接口類型的實例對象時甲葬,就需要使用Binds注解來注入廊勃。
@Binds注解使用的類必須是abstract類
@Binds注解的方法必須是abstract抽象方法,其參數(shù)必須是接口的實例對象经窖,返回值必須是接口類
6) @Provides注入實例
在module中提供的實例坡垫,不能直接通過構(gòu)造函數(shù)來提供時(例如使用第三方的構(gòu)建模式創(chuàng)建等),這時候就可以@Providers注解來注釋一個方法專門提個這個對象實例來完成注入画侣。
該方法的返回值必須是需要的對象的實例類型冰悠;
7) @Qualifier 限定符
當你想要Inject的兩個對象實例的類型是一樣的,但是他們又是不同的實例配乱,就可以使用Qualifier限定兩個自定義的注解來完成.
自定義Qualifier的注解
在module的提供實例的方式上使用自定義的Qualifier注解
在Inject的地方使用Qualifier注解來進行區(qū)分溉卓;
@Qualifier
@kotlin.annotation.Retention(AnnotationRetention.BINARY)
annotation class Binder
@Qualifier
@kotlin.annotation.Retention(AnnotationRetention.BINARY)
annotation class Customer
個人理解,和Dagger中的Named注解類似搬泥。
2桑寨、Hilt組件的生命周期
Hilt內(nèi)置的android組件的生命周期如下表格所示,可以看出這些Component組件是隨著對應(yīng)的Android類的生命周期的創(chuàng)建和銷毀而變化忿檩,所以我們不需要手動去管理這些組件的生命周期尉尾。
生成的組件 | 創(chuàng)建時機 | 銷毀時機 |
---|---|---|
ApplicationComponent | Application#onCreate() | Application#onDestroy() |
ActivityRetainedComponent | Activity#onCreate() | Activity#onDestroy() |
ActivityComponent | Activity#onCreate() | Activity#onDestroy() |
FragmentComponent | Fragment#onAttach() | Fragment#onDestroy() |
ViewComponent | View#super() | 視圖銷毀時 |
ViewWithFragmentComponent | View#super() | 視圖銷毀時 |
ServiceComponent | Service#onCreate() | Service#onDestroy() |
3、組件的作用域
默認情況下燥透,Hilt中的所有綁定都未限制作用域沙咏。這就是說:每當應(yīng)用請求綁定的時候,都會創(chuàng)建所需要的類型的一個實例兽掰。
Hilt允許將綁定的作用域限定為特定組件芭碍。Hilt為在同一個作用域限定范圍內(nèi)的組件創(chuàng)建一次實例,這樣應(yīng)用的綁定請求共享同一實例孽尽。
下面看看生成組件的作用域:
Android 類 | 生成的組件 | 作用域 |
---|---|---|
Application | ApplicationComponent | @Singleton |
View Model | ActivityRetainedComponent | @ActivityRetainedScope |
Activity | ActivityComponent | @ActivityScoped |
Fragment | FragmentComponent | @FragmentScoped |
View | ViewComponent | @ViewScoped |
帶有 @WithFragmentBindings 注釋的 View
|
ViewWithFragmentComponent | @ViewScoped |
Service | ServiceComponent | @ServiceScoped |
作用域直接作用在對應(yīng)的類上面就可以了窖壕。
4、使用步驟
1)添加依賴
從下面的依賴可以看到使用了annotationProcessor注解處理器,說明Hilt中使用了APT技術(shù)
//hilt 依賴
implementation 'com.google.dagger:hilt-android:2.38.1'
annotationProcessor 'com.google.dagger:hilt-compiler:2.38.1'
2)添加插件
Hilt 插件是為了更好的使用Hilt API ,提供的一個字節(jié)碼轉(zhuǎn)換器瞻讽,可想而知這其中使用了字節(jié)碼插樁技術(shù)鸳吸。
//工程的build.gradle
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.38.1'
}
}
//模塊module的build.gradle 添加應(yīng)用插件
plugins {
id 'dagger.hilt.android.plugin'
id 'kotlin-kapt'
}
3)Hilt入口:自定義application
每一個使用了Hilt的app都必須使用@HiltAndroidApp注解了的自定義application ,這里是生成代碼的入口位置,方便訪問所有使用了Dagger的module.
@HiltAndroidApp
class CustomApplication : Application() {
override fun onCreate() {
super.onCreate()
//這里可以處理一些全局的Component等
}
}
4)創(chuàng)建一個Module
@InstallIn(ActivityComponent::class) 這個安裝注解速勇,我們需要特別注意一下:
1晌砾、他的參數(shù)必須是使用@DefineComponent注解的Component ;
下面是筆者傳遞了MainActivity 參數(shù)后編譯出現(xiàn)的錯誤烦磁,MainActivity里面是需要Inject 該Module的
@InstallIn, can only be used with @DefineComponent-annotated classes, but found: [com.leon.study_jetpack_hilt.MainActivity]
2养匈、必須通過參數(shù)指明是在哪個Component上,不可不傳參數(shù)都伪;
下面是不傳遞參數(shù)呕乎,報錯的異常錯誤:
Execution failed for task ':app:kaptDebugKotlin'. > A failure occurred while executing org.jetbrains.kotlin.gradle.internal.KaptExecution > java.lang.reflect.InvocationTargetException (no error message)
@Module
@InstallIn(ActivityComponent::class)
class HttpResponseModule {
@Provides
fun getHttpModule(): HttpResponseModule {
return HttpResponseModule()
}
}
5)添加注入點、Inject Modules
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var mHttpResponseModule:HttpResponseModule
@Inject
lateinit var mHttpResponseModule2:HttpResponseModule
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
println("HttpResponseModule object that via inject of hilt: hashcode = "+mHttpResponseModule.hashCode())
println("HttpResponseModule object that via inject of hilt: hashcode = "+mHttpResponseModule2.hashCode())
}
}
/*
* 輸出的結(jié)果:
2021-08-06 14:33:39.641 27582-27582/com.leon.study_jetpack_hilt I/System.out: HttpResponseModule object that via inject of hilt: hashcode = 26182251
2021-08-06 14:33:39.641 27582-27582/com.leon.study_jetpack_hilt I/System.out: HttpResponseModule object that via inject of hilt: hashcode = 228151752
*/
5陨晶、接口實例注入
接口沒有辦法直接構(gòu)造對象猬仁,那么就需要一個帶有@Binds注解的抽象方法的Module來告知,該抽象方法的參數(shù)會告訴Hilt創(chuàng)建的實例的類型先誉,而返回值的類型則告知是返回的哪個接口的實例湿刽。
下面的java代碼是成功的:
//定義接口
public interface IBook {
}
//接口的實現(xiàn)類
public class KotlinBook implements IBook{
@Inject
KotlinBook(){
}
}
//定義module
@Module
@InstallIn(ActivityComponent.class)
public abstract class BookModule {//這里注意是抽象類
@Binds
abstract IBook getBooks(KotlinBook book);//這里需要特別注意參數(shù)和返回值,方法是抽象方法
}
@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {
@Inject
IBook book;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.e(TAG, "onCreate: "+book.hashCode());
}
}
kotlin代碼:一直無法inject成功:
interface Book {
}
class JavaBook @Inject constructor() : Book {
}
/**
* 模塊必須是抽象類
*/
@Module
@InstallIn(ActivityComponent::class)
abstract class BookModule{
//抽象函數(shù)
@Binds
abstract fun getBook(book: JavaBook): Book
}
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var book: Book
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
println("HttpResponseModule object that via inject of hilt: listener----hashcode = " + book.hashCode())
}
}
三褐耳、原理及生成源碼分析
Hilt的核心還是Dagger的原理來完成诈闺,他是在Dagger的基礎(chǔ)上去掉了Component組件,而在編譯的時候會根據(jù)@InstallIn注解來自動生成Component漱病,并把他們注入到相關(guān)的代碼中买雾,這樣就可以全身心的只關(guān)注對象的創(chuàng)建和要把這些對象注入的位置。
Hilt內(nèi)置了針對Android類的一些Component組件杨帽,但是對于我們自己使用的是非Android內(nèi)置類的時候漓穿,還是和Dagger的使用一樣來做。
先來找找編譯后生成的文件:
1) Inject 流程
-
Hilt_MainActivity.java源碼:AndroidEntryPoint 該注解生成的class文件
/** * A generated base class to be extended by the @dagger.hilt.android.AndroidEntryPoint annotated class. If using the Gradle plugin, this is swapped as the base class via bytecode transformation. */ public abstract class Hilt_MainActivity extends AppCompatActivity implements GeneratedComponentManagerHolder { private volatile ActivityComponentManager componentManager; //這個方法調(diào)用了injectMainActivity() protected void inject() { if (!injected) { injected = true; ((MainActivity_GeneratedInjector) this.generatedComponent()).injectMainActivity(UnsafeCasts.<MainActivity>unsafeCast(this)); } } }
-
MainActivity_GeneratedInjector.java源碼:
其實就是我們自己寫的Component注盈,只不過不需要我們寫晃危,而是編譯期幫我們生成了。
@GeneratedEntryPoint @InstallIn(ActivityComponent.class) public interface MainActivity_GeneratedInjector { void injectMainActivity(MainActivity mainActivity); }
-
MainActivity_GeneratedInjector 這個接口的實現(xiàn)在CustomApplication_HiltComponents.java文件中的一個靜態(tài)抽象類:
@ActivityScoped public abstract static class ActivityC implements MainActivity_GeneratedInjector, ActivityComponent, DefaultViewModelFactories.ActivityEntryPoint, HiltWrapper_HiltViewModelFactory_ActivityCreatorEntryPoint, FragmentComponentManager.FragmentComponentBuilderEntryPoint, ViewComponentManager.ViewComponentBuilderEntryPoint, GeneratedComponent { @Subcomponent.Builder abstract interface Builder extends ActivityComponentBuilder { } }
-
在DaggerCustomApplication_HiltComponents_SingletonC.java文件中看看ActvityC的實例類:activityCImpl
private static final class ActivityCImpl extends CustomApplication_HiltComponents.ActivityC { private final HttpResponseModule httpResponseModule; //這個我們在注冊流程里面來看 private final DaggerCustomApplication_HiltComponents_SingletonC singletonC; private ActivityCImpl(DaggerCustomApplication_HiltComponents_SingletonC singletonC, ActivityRetainedCImpl activityRetainedCImpl, HttpResponseModule httpResponseModuleParam, Activity activityParam) { this.singletonC = singletonC; this.activityRetainedCImpl = activityRetainedCImpl; this.httpResponseModule = httpResponseModuleParam; } @Override public void injectMainActivity(MainActivity mainActivity) { injectMainActivity2(mainActivity); } private MainActivity injectMainActivity2(MainActivity instance) { MainActivity_MembersInjector.injectMHttpResponseModule(instance, HttpResponseModule_GetHttpModuleFactory.getHttpModule(httpResponseModule)); MainActivity_MembersInjector.injectMHttpResponseModule2(instance, HttpResponseModule_GetHttpModuleFactory.getHttpModule(httpResponseModule)); return instance; } }
-
繼續(xù)進入MainActivity_MembersInjector.java文件:
看到這個文件就都明了了老客,這就是Dagger里面Inject生成的代碼僚饭。
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> { private final Provider<HttpResponseModule> mHttpResponseModuleProvider; private final Provider<HttpResponseModule> mHttpResponseModule2Provider; public MainActivity_MembersInjector(Provider<HttpResponseModule> mHttpResponseModuleProvider, Provider<HttpResponseModule> mHttpResponseModule2Provider) { this.mHttpResponseModuleProvider = mHttpResponseModuleProvider; this.mHttpResponseModule2Provider = mHttpResponseModule2Provider; } public static MembersInjector<MainActivity> create( Provider<HttpResponseModule> mHttpResponseModuleProvider, Provider<HttpResponseModule> mHttpResponseModule2Provider) { return new MainActivity_MembersInjector(mHttpResponseModuleProvider, mHttpResponseModule2Provider); } @Override public void injectMembers(MainActivity instance) { injectMHttpResponseModule(instance, mHttpResponseModuleProvider.get()); injectMHttpResponseModule2(instance, mHttpResponseModule2Provider.get()); } @InjectedFieldSignature("com.leon.study_jetpack_hilt.MainActivity.mHttpResponseModule") public static void injectMHttpResponseModule(MainActivity instance, HttpResponseModule mHttpResponseModule) { instance.mHttpResponseModule = mHttpResponseModule; } @InjectedFieldSignature("com.leon.study_jetpack_hilt.MainActivity.mHttpResponseModule2") public static void injectMHttpResponseModule2(MainActivity instance, HttpResponseModule mHttpResponseModule2) { instance.mHttpResponseModule2 = mHttpResponseModule2; } }
2) 注冊流程
-
Hilt_CustomApplication.java
這里主要就是創(chuàng)建了一個DaggerCustomApplication_HiltComponents_SingletonC 的實例,并與當前app的應(yīng)用上下文綁定胧砰;
public abstract class Hilt_CustomApplication extends Application implements GeneratedComponentManagerHolder { private final ApplicationComponentManager componentManager = new ApplicationComponentManager(new ComponentSupplier() { @Override public Object get() { return DaggerCustomApplication_HiltComponents_SingletonC.builder() .applicationContextModule(new ApplicationContextModule(Hilt_CustomApplication.this)) .build(); } }); } //ApplicationContextModule 類的實現(xiàn) @Module @InstallIn(SingletonComponent.class) public final class ApplicationContextModule { private final Context applicationContext; public ApplicationContextModule(Context applicationContext) { this.applicationContext = applicationContext; } @Provides @ApplicationContext Context provideContext() { return applicationContext; } @Provides Application provideApplication() { return Contexts.getApplication(applicationContext); } }
-
DaggerCustomApplication_HiltComponents_SingletonC .java
先來看看該類的結(jié)構(gòu):大量的靜態(tài)內(nèi)部類
其實就是將app的上下文保存下來鳍鸵,并創(chuàng)建了自己的實例:
public final class DaggerCustomApplication_HiltComponents_SingletonC extends CustomApplication_HiltComponents.SingletonC {
private final ApplicationContextModule applicationContextModule;
private final DaggerCustomApplication_HiltComponents_SingletonC singletonC = this;
private DaggerCustomApplication_HiltComponents_SingletonC(
ApplicationContextModule applicationContextModuleParam) {
this.applicationContextModule = applicationContextModuleParam;
}
public static Builder builder() {
return new Builder();
}
@Override
public void injectCustomApplication(CustomApplication customApplication) {
}
@Override
public ActivityRetainedComponentBuilder retainedComponentBuilder() {
return new ActivityRetainedCBuilder(singletonC);
}
@Override
public ServiceComponentBuilder serviceComponentBuilder() {
return new ServiceCBuilder(singletonC);
}
//靜態(tài)內(nèi)部類,來創(chuàng)建DaggerCustomApplication_HiltComponents_SingletonC的實例
public static final class Builder {
private ApplicationContextModule applicationContextModule;
private Builder() {
}
public Builder applicationContextModule(ApplicationContextModule applicationContextModule) {
this.applicationContextModule = Preconditions.checkNotNull(applicationContextModule);
return this;
}
public CustomApplication_HiltComponents.SingletonC build() {
Preconditions.checkBuilderRequirement(applicationContextModule, ApplicationContextModule.class);
return new DaggerCustomApplication_HiltComponents_SingletonC(applicationContextModule);
}
}
}
-
ActivityRetainedBuilder 這個類實現(xiàn)尉间,其實就是創(chuàng)了ActivityRetainedCImpl的實例對象