继续创作,加快生长!这是我参加「日新计划 6 月更文应战」的第1天,点击检查活动概况

前语

间隔上一次更文已经过去了一个月了,由于这段时刻有点忙,公司需要开发一个新的包给主项目导流,经过时不时的加加班,上星期终于上线了。这个项目其实在上个月写完最终一篇更文后就开始搭建了,但由于时刻不是很足够,过去一个月中也经常零零散散的写着,所以拖到了现在,所以说坚持写博客真的挺难的…做WanAndroid这个项目首要是对过去几个月KotlinJetpack系列更文学习的总结,这个项意图功用并未全部都做了,由于咱们的意图不是再造一个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封装首要完成了ViewBindingDefaultLifecycleObserverDefaultLifecycleObserver完成和生命周期绑定,关于内存走漏能够放心了。

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…