
本文是 MAD Skills 系列 中有關(guān) Hilt 的第四篇文章!在本文中,我們將探討如何編寫自定義的 Hilt 擴展。如果您需了解本系列前三篇文章,請查閱:
如果您更喜歡通過視頻了解此內(nèi)容,可以點擊 此處 查看。
案例: WorkManager 擴展
Hilt 擴展是一個生成代碼的庫,常通過注解處理器實現(xiàn)。生成的代碼作為構(gòu)成 Hilt 依賴項注入關(guān)系圖的模塊或入口點。
Jetpack 中 WorkManager 的集成庫就是一個擴展的例子。WorkManager 擴展幫助我們減少向 worker 提供依賴項時所需的模板代碼及配置。該庫由兩部分組成,分別為 androidx.hilt:hilt-work 和 androidx.hilt:hilt-compiler。第一部分包含 HiltWorker 注解以及一些運行時的輔助類,第二部分是一個注解處理器,根據(jù)第一部分中注解提供的信息生成模塊。
擴展的使用非常簡單,僅需在您的 worker 上添加 @HiltWorker 注解:
@HiltWorker
public class ExampleWorker extends Worker {
// ...
}
擴展編譯器會生成一個添加了 @Module 注解的類:
@Generated("androidx.hilt.AndroidXHiltProcessor")
@Module
@InstallIn(SingletonComponent.class)
@OriginatingElement(
topLevelClass = ExampleWorker.class
)
public interface ExampleWorker_HiltModule {
@Binds
@IntoMap
@StringKey("my.app.ExmapleWorker")
WorkerAssistedFactory<? extends ListenableWorker> bind(
ExampleWorker_AssistedFactory factory);
}
該模塊為 worker 定義了一個可以訪問 HiltWorkerFactory 的綁定。然后,配置 WorkerManager 使用該 factory,從而使 worker 的依賴項注入可用。
Hilt 聚合
啟用擴展的一個關(guān)鍵機制是 Hilt 能夠從類路徑中發(fā)現(xiàn)模塊和入口點。這被稱為聚合,因為模塊和入口點被聚合到帶有 @HiltAndroidApp 注解的 Application 中。

由于 Hilt 具有聚合能力,任何通過添加 @InstallIn 注解生成 @Module 及 @EntryPoint 的工具都可以被 Hilt 發(fā)現(xiàn),并在編譯期成為 Hilt DI 圖中的一部分。這使得擴展可以輕松地以插件形式集成到 Hilt,無需開發(fā)者處理任何額外工作。
注解處理器
生成代碼的常規(guī)途徑是使用注解處理器。源文件轉(zhuǎn)換為 class 文件之前,注解處理器會在編譯器中運行。當(dāng)資源帶有處理器所聲明的已支持的注解時,處理器會進(jìn)行處理。處理器可以生成進(jìn)一步需要被處理的方法,因此編譯器會不斷循環(huán)運行注解處理器,直到?jīng)]有新的內(nèi)容產(chǎn)生。一旦所有的環(huán)節(jié)都完成,編譯器才會將源文件轉(zhuǎn)換為 class 文件。

由于循環(huán)機制,處理器可以相互作用。這非常重要,因為這使得 Hilt 的注解處理器可以處理由其他處理器生成的 @Module 或 @EntryPoint 類。這也意味著您的擴展也可以建立在其他人編寫的擴展之上!
WorkManager extension processor 根據(jù)帶有 @HiltWorker 注解的類生成代碼,同時驗證注解用法并使用 JavaPoet 等庫生成代碼。
Hilt 擴展注解
Hilt API 中有兩個重要的注解: @GeneratesRootInput 和 @OriginatingElement。擴展應(yīng)該使用這些注解才能與 Hilt 正確集成。
擴展應(yīng)該使用 @GeneratesRootInput 來啟用代碼生成的注解。這讓 Hilt 注解處理器知道它應(yīng)該在生成組件之前完成擴展注解處理器的工作。例如,@HiltWorker 注解本身是被 @GeneratesRootInput 注解修飾的:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
@GeneratesRootInput
public @interface HiltWorker {
}
所生成的帶有 @Module、@EntryPoint 以及 @InstallIn 注解的類都需要添加 @OriginatingElement 注解,該注解的輸入?yún)?shù)是觸發(fā)模塊或入口點生成的頂層類。這就是 Hilt 判斷生成的模塊和入口點是否在本地測試的依據(jù)。例如,在 Hilt 測試中定義了一個添加 @HiltWorker 注解的內(nèi)部類,模塊的初始元素就是測試值。
測試案例如下:
@HiltAndroidTest
class SampleTest {
@HiltWorker
class TestWorker extends Worker {
// …
}
}
生成的模塊包含 @OriginatingElement 注解:
@Module
@InstallIn(SingletonComponent.class)
@OriginatingElement(
topLevelClass = SampleTest.class
)
public interface SampleTest_TestWorker__HiltModule {
// …
}
心得
Hilt 擴展支持多種可能性,以下是創(chuàng)建擴展的一些心得:
項目中的通用模式
如果您的項目中有創(chuàng)建模塊或入口點的通用模式,那么它們很大概率可以通過使用 Hilt 擴展實現(xiàn)自動化。舉個例子,如果每一個實現(xiàn)特定接口的類都必須創(chuàng)建一個具有多綁定的模塊,那么可以創(chuàng)建一個擴展,只需在實現(xiàn)類上添加注解即可生成多重綁定模塊。
支持非標(biāo)準(zhǔn)成員注入
對于那些 Framework 中已經(jīng)支持帶有實例化能力的成員注入類型,我們需要創(chuàng)建一個 @EntryPoint。如果有多種類型需要被成員注入,那么自動創(chuàng)建入口點的擴展會很有用。例如,需要通過 ServiceLoader 發(fā)現(xiàn)服務(wù)實現(xiàn)的庫負(fù)責(zé)實例化發(fā)現(xiàn)的服務(wù)。為了將依賴項注入到服務(wù)實現(xiàn)中,必須創(chuàng)建一個 @EntryPoint。通過使用 Hilt 擴展,可以使用在實現(xiàn)類上添加注解完成自動生成入口點。擴展可以進(jìn)一步生成代碼以使用入口點,例如由服務(wù)實現(xiàn)擴展的基類。這類似于 @AndroidEntryPoint 為 Activity 創(chuàng)建 @EntryPoint,并創(chuàng)建使用生成的入口點在 Activity 中執(zhí)行成員注入的基類。
鏡像綁定
有時需要使用不同的限定符來鏡像或重新聲明綁定。當(dāng)存在自定義組件時,這可能更常見。為了避免丟失重新聲明的綁定,可以創(chuàng)建 Hilt 擴展以自動生成其他鏡像綁定的模塊。例如,考慮包含不同依賴項實現(xiàn)的應(yīng)用中 "付費" 和 "免費" 訂閱的情況。然后,每一層都有兩個不同的自定義組件,這樣您就可以確定依賴關(guān)系的作用域。當(dāng)添加一個通用的未限定作用域的綁定時,定義綁定的模塊可以在其 @InstallIn 中包含兩個組件,也可以加載在父組件中,通常是單例組件。但是當(dāng)綁定被限定作用域時,模塊必須被復(fù)制,因為需要不同的限定符。實現(xiàn)一個擴展就可以生成兩個模塊,可以避免樣板代碼并確保不會遺漏通用綁定。
總結(jié)
Hilt 的擴展可以進(jìn)一步增強代碼庫中的依賴項注入能力,因為它們可以實現(xiàn)與 Hilt 尚不支持的其他庫集成??偠灾?,擴展通常由兩部分組成,包含擴展注解的運行時部分,以及生成 @Module 或 @EntryPoint 的代碼生成器 (通常是注解處理器)。擴展的運行時部分可能有額外的輔助類,這些輔助類使用聲明在生成的模塊或入口點中綁定。代碼生成器還可能生成與擴展相關(guān)的附加代碼,它們無需專門生成模塊和入口點。
擴展必須使用兩個注解才能與 Hilt 正確交互:
- @GeneratesRootInput 添加在擴展注解上。
- @OriginatingElement 由擴展添加在生成的模塊或入口點上。
最后,您可以查看 hilt-install-binding 項目,這是一個簡單擴展的示例,它展示了本文中提到的概念。
以上便是 MAD Skills 系列關(guān)于 Hilt 的全部內(nèi)容,如需觀看視頻全集,請移步到 Hilt - MAD Skills 播放列表。感謝閱讀本文!
歡迎您 點擊這里 向我們提交反饋,或分享您喜歡的內(nèi)容、發(fā)現(xiàn)的問題。您的反饋對我們非常重要,感謝您的支持!