3

一看就会 Android中Hilt的各场景使用

 2 years ago
source link: http://www.androidchina.net/12587.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

新版Hilt在Android各种场景的详细使用手册

关于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<UserLoginViewModel, ActivityUserLoginBinding>() {

    @Inject          //直接注入-一层一层找ActivityScope中找不到 就去ApplicationScop中找 就找到上面定义的那个Gson提供者了
    lateinit var mGson: Gson

}

4、ViewModel的注入

ViewModel的注入注意和老版本有差异!

Repository:

@Singleton     //构造方法注入 并保持单例
class DemoRepository @Inject constructor() : BaseRepository() {

    suspend inline fun getIndustry(): OkResult<List<Industry>> {
      // xxx
    }

}

@Singleton   //构造方法注入 并保持单例
class SchoolRepository @Inject constructor() : BaseRepository() {

    suspend inline fun getSchool(): OkResult<List<SchoolBean>> {
      //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<UserLoginViewModel, ActivityUserLoginBinding>() {

    @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<UserLoginViewModel, ActivityUserLoginBinding>() {

    @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<UserLoginViewModel, ActivityUserLoginBinding>() {

    @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的开发场景中,也就用到这些功能,其实都是模板代码,大家可以选择对应的场景复制到自己的项目中改一下相关的对象就可以了。

好了,讲到这里就结束了,如有不对,还请指正。

感谢大家看完,觉得不错记得点赞哦!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK