接触Android以来,也没有使用过什么特别高端的技术,像一些对象的创建基本上都是使用new来创建的。后来因为公司的项目使用到了dagger,才对Android的依赖注入框架有所接触,众所周知的是,dagger这个框架相当灵活(上手成本实在是太高),导致一些activity的注入或者context的注入都很麻烦,为了提高工作的效率,重新选择一个注入框架就显得很有必要了。
在Android中,dagger、koin和hilt是三个比较出名的注入框架,dagger已经排除在外了,那么也就剩下koin和hilt,这两者之间之所以还是选择了hilt,毕竟它是jetpack库里的,算是google的亲儿子了,使用难易度上多少会比koin好很多吧(当然这是我的猜测,毕竟koin我也没使用过),总的来说,就hilt和dagger相比,hilt真香...
好了,废话不多说。在hilt的使用中,官网上(这里吐槽一下官网上的教程,每个字我都认识,但是组合起来就很难看懂...英文好的同学直接看英文吧,中文太难理解了)基本上都是告诉我们如何在单模块中使用,但在实际开发的时候,多模块往往才是我们的选择(别问,问就是灵活哈哈...),本文就主要介绍一下怎么在多模块中集成hilt
首先先简单介绍一下项目的组织结构,如图所示:
其中,config.gradle中声明了我们所需要的依赖,这里贴一份config.gradle的伪代码,大伙可以自行的去修改
ext {
isDebug = false
android = [
compileSdk : 31,
minSdk : 21,
targetSdk : 31,
versionCode: 2,
versionName: "1.0"
]
libVersion = [
hilt : '2.38.1',
database: '2.4.2',
hutool : '5.7.21',
okhttp : '4.9.3',
arouter : '1.5.2'
]
libs = [
publicLibs : [
'androidx.core:core-ktx:1.6.0',
'androidx.appcompat:appcompat:1.3.0',
'com.google.android.material:material:1.3.0',
'androidx.constraintlayout:constraintlayout:2.0.4',
],
localLibs : [
//本地的基础库
':moduleBase:common',
':moduleBase:basic',
':moduleBase:database',
':screenMatch',
],
core : [
//逻辑代码库
':moduleCore:login',
':moduleCore:start',
':moduleCore:main'
],
otherLibs : [
//其他一些三方的库
],
jsonLib : [
core : [
"com.squareup.moshi:moshi-kotlin:1.13.0",
"com.squareup.moshi:moshi:1.13.0",
"com.squareup.retrofit2:converter-moshi:2.9.0"
],
compiler: "com.squareup.moshi:moshi-kotlin-codegen:1.13.0"
],
routerLib : [
core : "com.alibaba:arouter-api:${libVersion.arouter}",
compiler: "com.alibaba:arouter-compiler:${libVersion.arouter}"
],
//****************重点:这里声明了hilt
injectLib : [
core : [
"com.google.dagger:hilt-android:${libVersion.hilt}",
],
compiler: "com.google.dagger:hilt-android- compiler:${libVersion.hilt}",
],
dataBaseLib: [
core : [
"androidx.room:room-runtime:${libVersion.database}",
"androidx.room:room-ktx:${libVersion.database}",
"androidx.room:room-rxjava2:${libVersion.database}",
"androidx.room:room-paging:${libVersion.database}"
],
compiler: "androidx.room:room-compiler:${libVersion.database}"
],
"RoomigrantLib":[
core:'com.github.MatrixDev.Roomigrant:RoomigrantLib:0.3.4',
compiler:'com.github.MatrixDev.Roomigrant:RoomigrantCompiler:0.3.4'
]
]
}
那么怎么使用这个文件呢?打开项目级的build.gradle,添加如下代码
apply from:"config.gradle"
如图所示:
OK,到此我们就将config.gradle这个文件引入到了工程中,还需要在模块的build.gradle中调用一下,这里我们以common这个模块举例。
首先我们在common模块的build.gradle文件的头部定义一个变量,
def cfg = rootProject.ext
如图所示:
再在该文件中的denpendencies中引用
解释一下,这里我们对hilt的依赖使用implementation,这里必须每个模块都对hilt依赖,而不能采用common依赖hilt,其他模块依赖common的方式间接的依赖hilt,否则同步gradle的时候会报如下错,如图:
不要忘了头部的hilt插件的引用:
plugins {
id 'xxx'
id 'xxx'
id 'xxx'
...
id 'dagger.hilt.android.plugin'
}
OK,到这里我们便能在项目中使用Hilt了,接下来便是如何使用hilt了。官网上首先提到的是我们需要一个@HiltAndroidApp去标注一个Application类
在项目中我们一般会使用一个类去继承Application并扩展它,这里我们叫它BaseApplication, 那么问题来了,这个BaseApplication到底应该放在哪里呢?是放在某个模块下面?还是放在app壳上?答案是放在App壳上,否则就会报错,如图:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.vegle.RobotConfigurator, PID: 6061
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.vegle.RobotConfigurator/com.vegle.start.ui.activity.StartActivity}: java.lang.ClassCastException: com.vegle.common.DaggerBaseApplication_HiltComponents_SingletonC$ActivityCImpl cannot be cast to com.vegle.start.ui.activity.StartActivity_GeneratedInjector
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3449)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
Caused by: java.lang.ClassCastException: com.vegle.common.DaggerBaseApplication_HiltComponents_SingletonC$ActivityCImpl cannot be cast to com.vegle.start.ui.activity.StartActivity_GeneratedInjector
at com.vegle.start.ui.activity.Hilt_StartActivity.inject(Hilt_StartActivity.java:63)
at com.vegle.start.ui.activity.Hilt_StartActivity$1.onContextAvailable(Hilt_StartActivity.java:34)
at androidx.activity.contextaware.ContextAwareHelper.dispatchOnContextAvailable(ContextAwareHelper.java:99)
at androidx.activity.ComponentActivity.onCreate(ComponentActivity.java:322)
at androidx.fragment.app.FragmentActivity.onCreate(FragmentActivity.java:249)
at com.vegle.basic.base.BaseActivity.onCreate(BaseActivity.kt:33)
at android.app.Activity.performCreate(Activity.java:8000)
at android.app.Activity.performCreate(Activity.java:7984)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3422)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
这一点没有特别好的办法,如果有知道怎么解决这个问题的同学麻烦告知一下,至于如何避免在app壳上写过多的初始化之类的代码,倒是有很多解决方法,例如app壳中以来common模块,并使用回调的方式,将代码在common模块中实现即可,当然也有使用反射的方式实例化common中的实现类,这样就省去了依赖这个步骤,各有所长,都可以
这里我贴一下我的app壳中的BaseApplication:
@HiltAndroidApp
open class BaseApplication :Application(){
companion object{
const val TAG="BaseApplication"
var IS_DEBUG = BuildConfig.DEBUG
}
//实例化一个接口的实现,并在各个周期中调用这个实现的方法
private val applicationLifecycleListener = ApplicationLifecycleListenerImpl
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base)
// multiDexEnabled true
applicationLifecycleListener.attachBaseContext(base, IS_DEBUG)
}
override fun onCreate() {
super.onCreate()
applicationLifecycleListener.onCreate(this)
Log.e(TAG,"BaseApplication--->")
}
/**
* This method is for use in emulated process environments. It will
* never be called on a production Android device, where processes are
* removed by simply killing them; no user code (including this callback)
* is executed when doing so.
*/
override fun onTerminate() {
super.onTerminate()
applicationLifecycleListener.onTerminate()
}
}
到这里对hilt的初始化就完成了,不要忘了在AndroidManifest.xml中对BaseApplication的引用
OK,开始使用hilt,我们在common模块下建个包,包名随便,自己知道就好,但最好是顾名思义,我这里就叫它di,并在di目录下建立一个AppModule类:
内容的话就看图吧,就是按照官网的使用手册食用即可:
在其他模块下也是一样的,只要把@HiltAndroidApp初始化的位置弄好其他模块下只需要弄个Module出来就可以了,这也是hilt方便的地方,hilt帮我们创建并管理component,免去了我们手动去创建和维护component,我们就只需要关注Module里有啥就行了,而不用关心他们如何创建 。
OK!搞定收工!第一次写博客,可能有些地方组织的不是特别好,多多指教,不喜勿喷~