持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
前言
距离上一次更文已经过去了一个月了,因为这段时间有点忙,公司需要开发一个新的包给主项目导流,经过时不时的加加班,上周终于上线了。这个项目其实在上个月写完最后一篇更文后就开始搭建了,但因为时间不是很充足,过去一个月中也经常零零散散的写着,所以拖到了现在,所以说坚持写博客真的挺难的…做WanAndroid这个项目主要是对过去几个月Kotlin
和Jetpack
系列更文学习的总结,这个项目的功能并未全部都做了,因为我们的目的不是再造一个WanAndroid客户端,只是学习搭建和使用Kotlin+MVVM这一种架构。当然,项目只有功能未完全,但是其他配置,如混淆、多渠道打包都实现了的。
项目简介
项目是采用Kotlin
编写,采用MVVM架构,结合ViewMdel、Lifecycle、paging、LiveData、navigation等Jetpack组件以及Retrofit使用。API来自鸿神的WanAndroid ,项目的大部分资源文件及部分代码来自WanAndroid站内的开源WanAndroid项目,UI效果也是参照这个项目来实现的。
项目效果
项目Github地址:github.com/Jeremyzwc/W…
封装介绍
基类封装
BaseActivity对ViewBinding封装
BaseActivity
主要是对ViewBinding
的封装:
abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity(){
核心代码主要是对ViewBinding
的处理:
private val _viewBinding: VB by lazy {
val type = javaClass.genericSuperclass
val vbClass: Class<VB> = type!!.saveAs<ParameterizedType>().actualTypeArguments[0].saveAs()
val method = vbClass.getDeclaredMethod("inflate", LayoutInflater::class.java)
method.invoke(this, layoutInflater)!!.saveAsUnChecked()
}
BaseVMActivity对ViewModel封装
核心代码:
abstract class BaseVMActivity<VB : ViewBinding, VM : BaseViewModel> : BaseActivity<VB>() {
protected val viewModel: VM by lazy {
val type = javaClass.genericSuperclass
val vmClass: Class<VM> = type!!.saveAs<ParameterizedType>().actualTypeArguments[1].saveAs()
ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).get(vmClass)
}
}
BaseVMActivity
中的initCommObserver
方法主要是处理一些公共事件:
protected fun initCommObserver() {
viewModel.dialogLoadingEvent.observe(this) {
if (it.loadingState) {
if (TextUtils.isEmpty(it.loadingMsg)) showDialogloading() else showDialogloading(it.loadingMsg)
} else {
loadingDialog.dismiss()
}
}
viewModel.layoutLoadingEvent.observe(this) {
val rlLoading = viewBinding?.root?.findViewById<RelativeLayout>(R.id.rl_loading)
rlLoading?.visibility = if(it) View.VISIBLE else View.GONE
}
viewModel.loadErrorEvent.observe(this) {
isShowErrorLayout = it.loadingErrorState
errorMsg = it.loadingErrorMsg
val llError = viewBinding?.root?.findViewById<LinearLayout>(R.id.ll_error)
val tvError = viewBinding?.root?.findViewById<TextView>(R.id.tv_error)
llError?.visibility = if(it.loadingErrorState) View.VISIBLE else View.GONE
tvError?.text = it.loadingErrorMsg
}
viewModel.requestErrorEvent.observe(this) {
ToastUtils.show(it)
}
}
BaseFragment和BaseVMFragment的作用和Base的activity基本一致
BaseViewModel
BaseViewMode
主要是处理一些公共View的逻辑,实现ViewModel
。
BaseLifecycleDialog
BaseLifecycleDialog
封装主要实现了ViewBinding
和DefaultLifecycleObserver
,DefaultLifecycleObserver
实现和生命周期绑定,对于内存泄漏可以放心了。
BasePagingSource
BasePagingSource
主要封装了paging
的分页加载处理,不用每一个实现的PagingSource
都要去处理page
的逻辑。
BasePagingDataAdapter
对PagingDataAdapter
的封装。
如果觉得
paging
难用,BaseRecyclerViewAdapterHelper
也是很香的。
BaseRvAdapter
对RecyclerView.Adapter
的封装,在不使用分页的列表时使用。
Room数据库封装实现浏览记录功能:WanDB
@Database(entities = [ScanRecordEnity::class], version = 1, exportSchema = false)
abstract class WanDB : RoomDatabase(){
abstract fun getScanRecordDao(): ScanRecordDao
companion object {
@Volatile
private var instantce: WanDB? = null
private const val DB_NAME = "wan_android.db"
fun getInstance(context: Context): WanDB? {
if (instantce == null) {
synchronized(WanDB::class.java) {
if (instantce == null) {
instantce = createInstance(context)
}
}
}
return instantce
}
private fun createInstance(context: Context): WanDB {
return Room.databaseBuilder(context.applicationContext, WanDB::class.java, DB_NAME)
.allowMainThreadQueries()
.build()
}
}
}
http请求封装
对于网络请求封装这一块,应该是花的时间是最多的,主要是一开始是根据官网那样做,加一层Repository
去管理网络请求调用,后面参考关于Flow封装网络库的这篇文章juejin.cn/post/696355… ,觉得非常的方便,对比官网的做法是更加简洁的,所以我就基本用他的这种方法来修改。核心代码在FlowVmKtx.kt文件,如下:
suspend fun <T> BaseViewModel.launchFlow(showLayoutLoading: Boolean = true, isToastError :Boolean = true, request: suspend WanAndroidApiService.() -> BaseResponse<T>): Flow<BaseResponse<T>> {
if (showLayoutLoading) {
showLayoutLoading()
}
return flow {
val response = request.invoke(apiService)
if (!response.isSuccess) {
throw ApiException(response.errorMsg ?: "", response.errorCode!!)
}
emit(response)
}.flowOn(Dispatchers.IO)
.onCompletion { throwable ->
if(showLayoutLoading){
hideLayoutLoading()
}
throwable?.let { throw catchException(this@launchFlow, throwable,isToastError) }
}
}
suspend fun <T> BaseViewModel.postFlow(showDialogLoading: Boolean = true, isToastError :Boolean = true,loadingStr: String = "加载中...", request: suspend WanAndroidApiService.() -> BaseResponse<T>): Flow<BaseResponse<T>> {
if (showDialogLoading) {
showDialogLoading(DialogLoadingEvent(loadingStr, true))
}
return flow {
val response = request.invoke(apiService)
if (!response.isSuccess) {
throw ApiException(response.errorMsg ?: "", response.errorCode!!)
}
emit(response)
}.flowOn(Dispatchers.IO)
.onCompletion { throwable ->
if (showDialogLoading) {
cloaseDialogLoading(DialogLoadingEvent("", false))
}
throwable?.let { throw catchException(this@postFlow, throwable,isToastError) }
}
}
这里我分了两个方法,一种是加载数据的情况,另外一种是如登陆这种接口,是往后代post的动作,体现两种加载的动画,也可以写成一个方法,这里我是想区分开来。
在ViewModel中使用:
fun getBanner(){
viewModelScope.launch {
launchFlow(false) {
getBanners()
}.next {
bannerLiveData.postValue(data)
}
}
}
关于网络请求库这一块,建议可以看看RxHttp,它也是可以支持协程的,让我们更易于封装,在我们公司的项目中也在使用它,作者一直在维护更新,这个项目也让我第一次donattion。因为这里我们做学习分享,所以是基于Retrofit来做封装。
总结
特别感谢
感谢鸿神WanAndroid网站提供的开放API。
参考资料:
今天的文章基于Kotlin+Flow+Retrofit+Jetpack+MVVM架构实现WanAndroid客户端开发搭建分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/18819.html