一、简介
年初开始我们公司的项目上开始使用MVVM与Jetpack,但是我们并没有使用Kotlin,最近想学习一下Kotlin的协程,所以写了个Demo,然后就寻思写篇博客。最开始并没有想用hilt,感觉最近挺火的就试了一下~
注:
- hilt木有考虑多模块情况
- 没有在生产项目中使用过~
- 主要说了用法,基础知识很少讲,不熟悉的可以看下最下面的参考文章,讲的比较详细。
二、依赖配置
-
根目录build(hilt需要加一个依赖)
ext { kotlin_version = '1.4.0' hilt_version = '2.28.3-alpha' } dependencies { ... // hilt classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version" } 复制代码
-
模块build
apply 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.
defaultConfig { ... javaCompileOptions { annotationProcessorOptions { // fix hilt arguments += [AROUTER_MODULE_NAME: project.getName()] } } } 复制代码
-
Application
@HiltAndroidApp 会触发 Hilt 的代码生成操作,生成的代码包括应用的一个基类,该基类充当应用级依赖项容器。
@HiltAndroidApp class AppKtApplication : SampleApplication() 复制代码
-
AppModule(重点)
这里与Dagger2类似,
@Provides
注解的方法命名规则(好像)是provide+返回值类名
@Module @InstallIn(ApplicationComponent::class) object AppModule { @Provides fun provideWeatherService(retrofit: Retrofit): WeatherService = retrofit.create(WeatherService::class.java) @Singleton @Provides 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() } @Provides 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
interface WeatherService { @GET("free/day") suspend fun getWeather(@QueryMap maps: Map<String, @JvmSuppressWildcards Any>): WeatherBean } 复制代码
-
Repository
WeatherService是通过hilt注入的,使用时不需要传构造参数
class WeatherRepository @Inject 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
处理
class WeatherViewModel @ViewModelInject 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
注解
ViewModelProvider(this).get(WeatherViewModel::class.java) 复制代码
- 刚刚说过了WeatherRepository是通过hilt注入的,使用时不需要传构造参数,但是要给使用的Activiti添加一个
五、公共代码
我的Base代码是用Java写的,简单写一下供大家参考~
-
BaseViewModel
public class BaseViewModel extends ViewModel { /** * 加载窗状态 */ public final MutableLiveData<Boolean> isLoading = new MutableLiveData<>(); /** * 通用网络请求异常 */ public final MutableLiveData<Throwable> networkError = new MutableLiveData<>(); } 复制代码
-
BaseMvvmActivity
public abstract class BaseMvvmActivity<V extends ViewBinding, VM extends BaseViewModel> extends BaseActivity<V> { protected VM mVm; @Override 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 其实这里可以写一下默认处理方式,可以在业务模块写网络异常处理 } } 复制代码
有用的话,点个赞吧ღ( ´・ᴗ・` )比心