继续创作,加快生长!这是我参加「日新计划 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封装网络库的这篇文章/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来做封装。
总结
Github下载
特别感谢
感谢鸿神WanAndroid网站供给的开放API。
参阅资料:
github.com/iceCola7/Wa…
/post/696355…