ldlywt / FastJetpack

基于Kotlin、协程、Retrofit的网络请求封装,快速简单轻便。
615 stars 93 forks source link

关于架构设计上有些问题想请教下 #10

Closed RDSunhy closed 2 years ago

RDSunhy commented 2 years ago

1.如:登录;用户输入框的 username、password这种字段 长度、是否为空 之类的参数判断 放在 activity/fragment 中好 还是 viewModel 中好;根据谷歌官方架构指南以及他们提供的demo;我自己的理解好像是放到viewModel中更好;对项目结构设计不太懂。。。

2.如果在当前库的设计的基础上增加 room 缓存 可以提供下思路吗? 我开始参考google提供的demo中的设计;官方项目repo 都是返回的livedata 我觉得按照那样写模板代码太多了;就去掉了livedata 又借鉴您的项目 自己写了个四不像。。。 我自己尝试的写法: 抽象接口 Fetcher

interface Fetcher<ResultType> {

    suspend fun loadFromNetWork(): ResultType

    fun loadFromDb(): ResultType?

    fun shouldFetch(data: ResultType?): Boolean

    fun saveResult(data: ResultType)

    suspend fun getResult(): Result<ResultType>

    //异常返回的body处理
    fun parseException(err: HttpException): Result.ErrorBody
}

实现类 SmartFetcher

abstract class SmartFetcher<RequestType> : Fetcher<RequestType> {

    override suspend fun getResult(): Result<RequestType> {
        var dbSource = loadFromDb()
        return if (shouldFetch(dbSource)){
            try {
                var result = loadFromNetWork()
                saveResult(result)
                dbSource = loadFromDb()
                Result.success(dbSource)
            }catch (e: HttpException){
                var error = parseException(e)
                Result.error(error.message?:e.message(), dbSource, error)
            }catch (e: Exception){
                Result.error(e.message?:"unknown error", dbSource)
            }
        }else{
            Result.success(dbSource)
        }
    }

}

调用示例,在Repo中 定义请求方法

    suspend fun request(): Result<Bean> {
        return object : SmartFetcher<Bean>() {
            override suspend fun loadFromNetWork(): Bean {
                TODO("Not yet implemented")
            }

            override fun loadFromDb(): Bean? {
                TODO("Not yet implemented")
            }

            override fun shouldFetch(data: Bean?): Boolean {
                TODO("Not yet implemented")
            }

            override fun saveResult(data: Bean) {
                TODO("Not yet implemented")
            }
        }.getResult()
    }

viewModel 中调用 repo 层:

    var mData = MutableLiveData<Result<Bean>>()

    fun sendEmailCode(){
        viewModelScope.launch(Dispatchers.IO) {
            mData.postValue(commonRepo.request())
        }
    }

activity 中监听

    mViewModel.mSendCode.observe(this, Observer { res ->
            dismissLoadingDialog()
    })
RDSunhy commented 2 years ago

另外 还想请教下 如何取消请求呢?希望大佬抽空能回复下 先谢谢了~

ldlywt commented 2 years ago

对于第一点:"登录;用户输入框的 username、password这种字段 长度、是否为空 之类的参数判断" 我个人的做法是倾向于怎么简单怎么来,直接在 Activity 中判断,也就是点击按钮后进行判断,因为我觉得如果把这个放到 VidewModel 中,通过 LiveData 来交互,太麻烦了,并且什么都放到 ViewModel 中,会导致ViewModel存在很多的 LiveData。

对与第二点:我看了下你的写法, 其实你现在的写法跟我的这三种方式的思想都不一样。你可以尝试在 Repo 中返回一个 Flow包裹的对象,这样貌似简单点。对于用完即丢的请求,你也可以在 ViewModel 中用 livedata 的扩展函数,这样可以少很多的模板代码。

对于第三点:"如何取消请求" viewmodel 跟 livedata 结合不会导致内存泄露,我暂时没遇到需要取消请求的场景,你可以到 stackoverflow 上搜下

个人觉得,没必要完全遵循谷歌的官方写法,适合自己项目才是最好的。。

RDSunhy commented 2 years ago

对于第一点:"登录;用户输入框的 username、password这种字段 长度、是否为空 之类的参数判断" 我个人的做法是倾向于怎么简单怎么来,直接在 Activity 中判断,也就是点击按钮后进行判断,因为我觉得如果把这个放到 VidewModel 中,通过 LiveData 来交互,太麻烦了,并且什么都放到 ViewModel 中,会导致ViewModel存在很多的 LiveData。

对与第二点:我看了下你的写法, 其实你现在的写法跟我的这三种方式的思想都不一样。你可以尝试在 Repo 中返回一个 Flow包裹的对象,这样貌似简单点。对于用完即丢的请求,你也可以在 ViewModel 中用 livedata 的扩展函数,这样可以少很多的模板代码。

对于第三点:"如何取消请求" viewmodel 跟 livedata 结合不会导致内存泄露,我暂时没遇到需要取消请求的场景,你可以到 stackoverflow 上搜下

个人觉得,没必要完全遵循谷歌的官方写法,适合自己项目才是最好的。。

感谢大佬回复~