Hilt - SMAXLYB/JetpackPractice GitHub Wiki
Hilt是一个依赖注入库,具体什么是依赖注入、怎么使用请看官方文档,这里只进行大致的原理讲解。
在Application类中标记@@HiltAndroidApp触发代码生成,例如:
@HiltAndroidApp
public final class MainApplication extends Application {
}
编译后生成的代码:
public abstract class Hilt_MainApplication extends Application implements GeneratedComponentManagerHolder {
// 这里创建了application组件管理类的对象
private final ApplicationComponentManager componentManager = new ApplicationComponentManager(new ComponentSupplier() {
@Override
public Object get() {
// component负责把module提供的依赖注入到@inject标记的地方
return DaggerMainApplication_HiltComponents_SingletonC.builder()
// 保存了applicationContext,后续如果要访问applicationContext,将会从这里取
// ApplicationContextModule是hilt自带的类,不是用户写的,因为只提供appilcationContext,没有其他逻辑
.applicationContextModule(new ApplicationContextModule(Hilt_MainApplication.this))
.build();
}
});
@Override
public final ApplicationComponentManager componentManager() {
return componentManager;
}
@Override
public final Object generatedComponent() {
return this.componentManager().generatedComponent();
}
@CallSuper
@Override
public void onCreate() {
// 这里MainApplication_GeneratedInjector是一个接口,和Dragger是一样的,如果application本身也有字段要依赖注入,那么赋值的操作就是在这里完成的
((MainApplication_GeneratedInjector) generatedComponent()).injectMainApplication(UnsafeCasts.<MainApplication>unsafeCast(this));
super.onCreate();
}
}
注意MainApplication也会变化,在transform阶段被替换:
@HiltAndroidApp
public final class MainApplication extends Hilt_MainApplication {
@Override
public void onCreate() {
super.onCreate(); // 如果application内还有字段注入,那么注入的时机就在这里
}
}
依赖注入的方式可以分为3种,一种是构造函数注入(constructor),另一种是字段注入(setter,注入的时机要尽可能的早,避免访问到null),还有就是方法注入(和字段注入差不多),使用@Inject注解指定注入依赖项的方式,但具体谁来提供这个依赖暂时不用管。例子:
public class Car {
// 当构造函数的权限是public+仅有一个无参构造函数时,可以省略@Inject
@Inject public Car(Engine engine) { ... }
// 字段不能为final
@Inject private Provider<Seat> seatProvider;
// Injectable package-private method
@Inject void install(Windshield windshield, Trunk trunk) { ... }
}
在Android中,可以为以下类提供注入:
- Application(其中通过使用 @HiltAndroidApp)
- Activity
- Fragment
- View
- Service
- BroadcastReceiver
@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() { ... }
生成的代码如下:
@Generated("dagger.hilt.android.processor.internal.androidentrypoint.ActivityGenerator")
public abstract class Hilt_MainActivity extends AppCompatActivity implements GeneratedComponentManager<Object> {
private volatile ActivityComponentManager componentManager;
private final Object componentManagerLock = new Object();
@CallSuper
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
// 这里同样生成了一个component,用来注入activity里面的字段
((MainActivity_GeneratedInjector) generatedComponent()).injectMainActivity(UnsafeCasts.<MainActivity>unsafeCast(this));
super.onCreate(savedInstanceState);
}
@Override
public final Object generatedComponent() {
return componentManager().generatedComponent();
}
protected final ActivityComponentManager componentManager() {
if (componentManager == null) {
synchronized (componentManagerLock) {
if (componentManager == null) {
componentManager = new ActivityComponentManager(this);
}
}
}
return componentManager;
}
// 这里复写了viewmodelFractory, fragment也是
@Override
public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
return DefaultViewModelFactories.getActivityFactory(this);
}
}
在activity或者fragment注入viewmodel和注入其他字段不一样,viewmodel是要通过宿主拿到的。在创建的时候使用了by viewmodels:
@HiltViewModel
public class DonutViewModel {
public DonutViewModel(@Assisted SavedStateHandle handle, RecipeRepository repository) {
...
}
}
@AndroidEntryPoint
class StatisticsFragment : Fragment() {
private val viewModel by viewModels<DonutViewModel>()
...
}
@MainThread
public fun <VM : ViewModel> Fragment.createViewModelLazy(
viewModelClass: KClass<VM>,
storeProducer: () -> ViewModelStore,
// 这里一直传的是空
factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
val factoryPromise = factoryProducer ?: {
// 会使用这个来反射调构造函数创建viewmoel, activity也是一样的
defaultViewModelProviderFactory
}
return ViewModelLazy(viewModelClass, storeProducer, factoryPromise)
}
如果在module里面提供相同的返回值,就要用限定符进行区分:
@Module
@InstallIn(ActivityComponent::class)
object AnalyticsModule {
@Named(NAME_TITLE)
@Provides
fun testNamed(): String {
return "原始Named限定符标题"
}
@Named(NAME_DESC)
@Provides
fun test1Named(): String {
return "原始Named限定符描述"
}
}
@Module
@InstallIn(ActivityComponent::class)
object AnalyticsModule {
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class QualifierTitle
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class QualifierDesc
@QualifierTitle
@Provides
fun testQualifier(): String {
return "原始Qualifier标题"
}
@QualifierDesc
@Provides
fun test1Qualifier(): String {
return "原始Qualifier描述"
}
}
// 第一种 Name方式
data class FakeService @Inject constructor(@Named(NAME_TITLE) var title: String, @Named(NAME_DESC) val des: String)
// 第二种
data class Fake1Service @Inject constructor(@AnalyticsModule.QualifierTitle var title: String, @AnalyticsModule.QualifierDesc var des: String)
//第一步:提供了一个接口`AnalyticsService`
interface AnalyticsService {
fun analyticsMethods()
}
//第二步:`AnalyticsServiceImpl`实现`AnalyticsService`接口,并实现内部方法
class AnalyticsServiceImpl @Inject constructor(
...
) : AnalyticsService { ... }
//第三步:可能还存在一个`AnalyticsServiceImpl_Test`也实现`AnalyticsService`接口,并实现内部方法
class AnalyticsServiceImpl_Test @Inject constructor(
...
) : AnalyticsService { ... }
@Module
@InstallIn(ActivityComponent::class)
abstract class AnalyticsModule {
//第三步:通过`@Binds`将告知`AnalyticsService`的实现为`AnalyticsServiceImpl`而不是‘AnalyticsServiceImpl_Test’
@Binds
abstract fun bindAnalyticsService(
analyticsServiceImpl: AnalyticsServiceImpl
): AnalyticsService
}
如果无法通过构造函数进行注入,比如第三方库,或者通过builder来创建等等:
@Provides
fun provideOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.build()
}
@Provides
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
}