关于Hilt与Koin的性能对比,之前有过分析。Hilt可以在Java/Kotin的语言环境中使用,内存使用、启动时间、体积大小等都小有优势,但是使用比较复杂,这里把常用的几种类型分别讲解。哪一种情况下使用哪一种方式定义和注入。
最后关于Hilt的前世今生和详细Api说明,郭神的文章早有详细说明。我就直接上场景和代码了。
郭神与网上绝大部分都是基于2.28-alpha版本。我这里使用的是新版2.38.1版本。使用的流程和部分Api注解与老版本有所区别。
1、依赖版本
依赖版本,这里注意和老版本不同,无需再集成ViewModel拓展依赖了
implementation 'com.google.dagger:hilt-android:2.38.1'
kapt 'com.google.dagger:hilt-android-compiler:2.38.1'
复制代码
根目录依赖插件
//Hilt插件
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.38.1'
复制代码
运行模块加入插件
apply plugin: 'dagger.hilt.android.plugin'
复制代码
2、入口定义
Application还是要定义注解
@HiltAndroidApp
class MyApplication : BaseApplication() {
}
复制代码
3、单例的定义
单例的定义和老版本有所不同
/**
* 全局的DI注入
*/
@Module
@InstallIn(SingletonComponent::class)
class ApplicationModule {
//如果是自己定义的MyApplication,通过一个Module向下转型
@Provides
fun provideMyApplication(application: Application): MyApplication {
return application as MyApplication
}
//全局的Gson 使用框架进行容错处理
@Provides
@Singleton
fun provideGson(): Gson {
return GsonFactory.getSingletonGson()
}
}
复制代码
使用单例也非常简单,直接注入,它会自己去Scope中一层一层往上找。
@AndroidEntryPoint //如果有东西注入必加的注解
class UserLoginActivity : YYBaseVDBActivity() {
@Inject //直接注入-一层一层找ActivityScope中找不到 就去ApplicationScop中找 就找到上面定义的那个Gson提供者了
lateinit var mGson: Gson
}
复制代码
4、ViewModel的注入
ViewModel的注入注意和老版本有差异!
Repository:
@Singleton //构造方法注入 并保持单例
class DemoRepository @Inject constructor() : BaseRepository() {
suspend inline fun getIndustry(): OkResult> {
// xxx
}
}
@Singleton //构造方法注入 并保持单例
class SchoolRepository @Inject constructor() : BaseRepository() {
suspend inline fun getSchool(): OkResult> {
//xxx
}
}
复制代码
ViewModel:
@HiltViewModel
class UserLoginViewModel @Inject constructor(
private val mRepository: DemoRepository,
private val mSchoolRepository: SchoolRepository, //通过构造方法注入 会自动去找上面的@Inject的构造对象 初始化出来
private val savedStateHandle: SavedStateHandle //系统的类 帮助Activity重建保存和恢复状态用的 也是自动注入的
private val application: Application //新版本Application是可以直接使用的
) : BaseViewModel() {
}
复制代码
Activity
@AndroidEntryPoint
class UserLoginActivity : YYBaseVDBActivity() {
@Inject
lateinit var mGson: Gson
}
复制代码
5、自定义对象的注入
自定义对象的注入也是比较常用的,比如Adapter,自定义View,Layout等。
分为简单类注入和复杂类注入。
简单类注入 直接构造方法Inject就可以了
@Singleton
class UserDao @Inject constructor(
//3.找到这里 发现这个构造是空参数 直接构造并完成返回
) {
fun printUser(): String {
return this.toString()
}
}
复制代码
@Singleton
class UserServer @Inject constructor(
private val userDao: UserDao //2.找到这里,发现这个构造需要一个UserDao对象,继续查找
) {
fun testUser() {
YYLogUtils.w(userDao.printUser())
toast(userDao.printUser())
}
fun getDaoContent(): String {
return userDao.printUser()
}
}
复制代码
@AndroidEntryPoint
class UserLoginActivity : YYBaseVDBActivity() {
@Inject
lateinit var userServer: UserServer //1.由这里开始发起查找
}
复制代码
复杂类的注入,我们可以定义Module
public class Pencil {
public void sayPencil() {
YYLogUtils.w("Pencil - i'm pencil");
}
}
复制代码
public class Book {
private Pencil pencil;
public Book(Pencil pencil) {
this.pencil = pencil;
}
public void sayBook() {
YYLogUtils.w("Book:" + this.toString());
pencil.sayPencil();
}
}
复制代码
开始注入的中间类,这个是Activity中的生命周期保持单例,如果想全局保持单例 需要@InstallIn(SingletonComponent::class) @Singleton注解方法。
这样虽然复杂一点但是更灵活了
@Module
@InstallIn(ActivityComponent::class)
class AuthDIModel {
//Activity级别同一个实例
@Provides
fun providePencil(): Pencil {
return Pencil()
}
@Provides
fun provideBook(pencil: Pencil): Book { //找到这里的时候 发现需要一个Pencil 然后再去找Pencil
return Book(pencil)
}
}
复制代码
使用:
@AndroidEntryPoint
class UserLoginActivity : YYBaseVDBActivity() {
@Inject
lateinit var userServer: UserServer //和下面是两种不同的注入方式 都可以
@Inject
lateinit var mBook: Book //一样的可以用
}
复制代码
6、接口对象的注入
上面的几种注入方式,已经含括了绝大部分场景,另一种比较少见的是接口的实现注入,也是分2中类型,一种是接口单实例的注入和接口多实例的注入
单实例注入:
interface IBook {
suspend fun saveBook(name: String)
suspend fun getBookAllSum(): Int
}
复制代码
class BookImpl @Inject constructor() : IBook {
@Inject
lateinit var dao: BookDao
override suspend fun saveBook(name: String) {
//这里可能会有一些自己的处理...
dao.insertAll(Book(name))
}
override suspend fun getBookAllSum(): Int {
return dao.queryBookAll()
}
}
复制代码
注意这里的 @Module
这里的Class是抽象类,提供实例的注解是 @Binds
而不是 @Provides
@InstallIn(ApplicationComponent::class)
@Module
abstract class BookModel {
@Binds
abstract fun bindIBook(impl: BookImpl): IBook //直接指定到单实例上面
}
复制代码
使用
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var iBook: IBook
}
复制代码
多实例的注入: 如果是一个接口的多实例,如何注入呢?
interface Engine {
fun start()
fun shutdown()
}
复制代码
多实例:
class GasEngine() @Inject constructor() : Engine {
override fun start() {
println("Gas engine start.")
}
override fun shutdown() {
println("Gas engine shutdown.")
}
}
class ElectricEngine() @Inject constructor() : Engine {
override fun start() {
println("Electric engine start.")
}
override fun shutdown() {
println("Electric engine shutdown.")
}
}
复制代码
当然要定义标识的自定义注解来标识他们啦 不然怎么知道谁是谁。 当然还有别的方式来标识,这里只是推荐使用这种方式
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class BindGasEngine
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class BindElectricEngine
复制代码
和上面的Mudule一样,只是标识了自定义注解标识
@Module
@InstallIn(ActivityComponent::class)
abstract class EngineModule {
@BindGasEngine
@Binds
abstract fun bindGasEngine(gasEngine: GasEngine): Engine //直接指定到对应的实例上
@BindElectricEngine
@Binds
abstract fun bindElectricEngine(electricEngine: ElectricEngine): Engine //直接指定到对应的实例上
}
复制代码
使用
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@BindGasEngine //指定标识
@Inject
lateinit var gasEngine: Engine
@BindElectricEngine //指定标识
@Inject
lateinit var electricEngine: Engine
}
复制代码
看到这里是不是觉得Hilt的使用其实也不难,我们在Android的开发场景中,也就用到这些功能,其实都是模板代码,大家可以选择对应的场景复制到自己的项目中改一下相关的对象就可以了。
好了,讲到这里就结束了,如有不对,还请指正。
作者:newki
链接:https://juejin.cn/post/7102956956653977637
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。