一、简介
年初开始我们公司的项目上开始使用MVVM与Jetpack,但是我们并没有使用Kotlin,最近想学习一下Kotlin的协程,所以写了个Demo,然后就寻思写篇博客。最开始并没有想用hilt,感觉最近挺火的就试了一下~
注:
- hilt木有考虑多模块情况
- 没有在生产项目中使用过~
- 主要说了用法,基础知识很少讲,不熟悉的可以看下最下面的参考文章,讲的比较详细。
二、依赖配置
根目录build(hilt需要加一个依赖)
1
2
3
4
5
6
7
8
9ext {
kotlin_version = '1.4.0'
hilt_version = '2.28.3-alpha'
}
dependencies {
...
// hilt
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
}模块build
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply plugin: 'dagger.hilt.android.plugin'
dependencies {
...
// 协程
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.7'
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
// hilt
implementation "com.google.dagger:hilt-android:$hilt_version"
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha02"
kapt "androidx.hilt:hilt-compiler:1.0.0-alpha02"
}
三、Hilt
使用Arouter遇到的一个坑
arguments
后面不能能用=
,要用+=
!!!,要不然会提示[Hilt] Processing did not complete. See error above for details.
1
2
3
4
5
6
7
8
9
10defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
// fix hilt
arguments += [AROUTER_MODULE_NAME: project.getName()]
}
}
}Application
@HiltAndroidApp 会触发 Hilt 的代码生成操作,生成的代码包括应用的一个基类,该基类充当应用级依赖项容器。
1
2
class AppKtApplication : SampleApplication()AppModule(重点)
这里与Dagger2类似,
@Provides
注解的方法命名规则(好像)是provide+返回值类名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
object AppModule {
fun provideWeatherService(retrofit: Retrofit): WeatherService = retrofit.create(WeatherService::class.java)
fun provideRetrofit(okHttp: OkHttpClient): Retrofit {
return Retrofit.Builder()
.baseUrl(Constants.BASE_URL) // 设置OkHttpclient
.client(okHttp) // RxJava2
.addCallAdapterFactory(RxJava3CallAdapterFactory.create()) // 字符串
.addConverterFactory(ScalarsConverterFactory.create()) // Gson
.addConverterFactory(GsonConverterFactory.create())
.build()
}
fun provideOkHttpClient(): OkHttpClient {
val builder = OkHttpClient.Builder()
if (BuildConfig.DEBUG) {
// OkHttp日志拦截器
builder.addInterceptor(HttpLoggingInterceptor())
builder.addInterceptor(HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger {
override fun log(message: String) {
val strLength: Int = message.length
var start = 0
var end = 2000
for (i in 0..99) {
//剩下的文本还是大于规定长度则继续重复截取并输出
if (strLength > end) {
Log.d("okhttp", message.substring(start, end))
start = end
end += 2000
} else {
Log.d("okhttp", message.substring(start, strLength))
break
}
}
}
}).setLevel(HttpLoggingInterceptor.Level.BODY))
}
return builder.build()
}
}
四、Hilt+协程
ServiceApi
Retrofit2.6开始原生支持
suspend
1
2
3
4
5interface WeatherService {
suspend fun getWeather(Map<String, Any>) maps: : WeatherBean
}Repository
WeatherService是通过hilt注入的,使用时不需要传构造参数
1
2
3
4
5
6
7class WeatherRepository constructor(
private val mClient: WeatherService
) {
suspend fun getWeather(map: Map<String, Any>) = mClient.getWeather(map)
}ViewModel
- WeatherRepository是通过hilt注入的,使用时不需要传构造参数,但是要给使用的Activiti添加一个
@AndroidEntryPoint
注解 block: suspend () -> Unit
是一个高阶函数viewModelScope
来自androidx.lifecycle:c:2.2.0
,他会替我们处理协程的生命周期isLoading
与networkError
是在BaseViewModel
中定义的MutableLiveData
,会在BaseMvvmActivity
或BaseMvvmFragment
中处理Loading窗与异常,也可以在当前Activity
重写,具体请看Demo- 我这里没有处理服务器返回错误,直接通过
DataBinding
展示到页面上了,需要的话可以先判断一下,如果返回错误可以使用networkError
post一个自定义ServerException
由Activity
处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32class WeatherViewModel constructor(
private val repository: WeatherRepository
) : BaseViewModel() {
val weatherBean = MutableLiveData<WeatherBean>()
fun loadWeather() {
isLoading.postValue(true)
val map: Map<String, Any> = HashMap<String, Any>()
launch({
weatherBean.postValue(repository.getWeather(map))
}, {
LogUtils.e(it)
networkError.postValue(it)
}, {
isLoading.postValue(false)
})
}
private fun launch(block: suspend () -> Unit, error: suspend (Throwable) -> Unit, complete: suspend () -> Unit) = viewModelScope.launch {
try {
block()
} catch (e: Throwable) {
error(e)
} finally {
complete()
}
}
}- WeatherRepository是通过hilt注入的,使用时不需要传构造参数,但是要给使用的Activiti添加一个
Activity
- 刚刚说过了WeatherRepository是通过hilt注入的,使用时不需要传构造参数,但是要给使用的Activiti添加一个
@AndroidEntryPoint
注解
1
ViewModelProvider(this).get(WeatherViewModel::class.java)
- 刚刚说过了WeatherRepository是通过hilt注入的,使用时不需要传构造参数,但是要给使用的Activiti添加一个
五、公共代码
我的Base代码是用Java写的,简单写一下供大家参考~
BaseViewModel
1
2
3
4
5
6
7
8
9
10
11
12public class BaseViewModel extends ViewModel {
/**
* 加载窗状态
*/
public final MutableLiveData<Boolean> isLoading = new MutableLiveData<>();
/**
* 通用网络请求异常
*/
public final MutableLiveData<Throwable> networkError = new MutableLiveData<>();
}BaseMvvmActivity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30public abstract class BaseMvvmActivity<V extends ViewBinding, VM extends BaseViewModel> extends BaseActivity<V> {
protected VM mVm;
protected void initViewModel() {
mVm = getViewModel();
mVm.isLoading.observe(this, isLoading -> {
if (isLoading) {
showProgress();
} else {
hideProgress();
}
});
mVm.networkError.observe(this, this::commonNetworkErrorListener);
}
/**
* 获取ViewModel
*/
protected abstract VM getViewModel();
/**
* 通用网络异常回掉
*/
protected void commonNetworkErrorListener(Throwable throwable) {
// TODO 其实这里可以写一下默认处理方式,可以在业务模块写网络异常处理
}
}