乐趣区

关于android:coroutineflowRetrofit的接口调用

初始化 Retrofit,Okhttp

abstract class BaseRetrofitClient {

    companion object {
        private const val TIME_OUT = 5
        const val BASE_URL = "https://www.wanandroid.com"
    }

    private val client: OkHttpClient
        get() {val builder = OkHttpClient.Builder()
            val logging = HttpLoggingInterceptor()
            if (BuildConfig.DEBUG) {logging.level = HttpLoggingInterceptor.Level.BODY} else {logging.level = HttpLoggingInterceptor.Level.BASIC}

            builder.addInterceptor(logging)
                .addNetworkInterceptor(ResponseInterceptor())
                .connectTimeout(TIME_OUT.toLong(), TimeUnit.SECONDS)
                        // 能够依据本人的口味自行定制
            handleBuilder(builder)

            return builder.build()}

    protected abstract fun handleBuilder(builder: OkHttpClient.Builder)

    fun <S> getService(serviceClass: Class<S>, baseUrl: String? = null): S {return Retrofit.Builder()
            .client(client)
            .addConverterFactory(GsonConverterFactory.create())
//                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
//                .addCallAdapterFactory(CoroutineCallAdapterFactory.invoke())
            .baseUrl(if (baseUrl.isNullOrBlank()) {BASE_URL} else baseUrl
            )

            .build().create(serviceClass)
    }
}

object RetrofitClient : BaseRetrofitClient() {
    val userService by lazy{getService(UserService::class.java, BASE_URL)
    }
    val wanService by lazy {getService(WanService::class.java, BASE_URL)
    }


    private val cookieJar by lazy {PersistentCookieJar(SetCookieCache(), SharedPrefsCookiePersistor(App.CONTEXT)) }

    override fun handleBuilder(builder: OkHttpClient.Builder) {val httpCacheDirectory = File(App.CONTEXT.cacheDir, "responses")
        val cacheSize = 10 * 1024 * 1024L // 10 MiB
        val cache = Cache(httpCacheDirectory, cacheSize)
        builder.cache(cache)
                .cookieJar(cookieJar)
                .addInterceptor { chain ->
                    var request = chain.request()
                    if (!NetWorkUtils.isNetworkAvailable(App.CONTEXT)) {request = request.newBuilder()
                                 .cacheControl(CacheControl.FORCE_CACHE)
                                .build()}
                    val response = chain.proceed(request)
                    if (!NetWorkUtils.isNetworkAvailable(App.CONTEXT)) {
                        val maxAge = 60 * 60
                        response.newBuilder()
                                .removeHeader("Pragma")
                                .header("Cache-Control", "public, max-age=$maxAge")
                                .build()} else {
                        val maxStale = 60 * 60 * 24 * 7 // tolerate 4-weeks stale
                        response.newBuilder()
                                .removeHeader("Pragma")
                                .header("Cache-Control", "public, only-if-cached, max-stale=$maxStale")
                                .build()}
                    response
                }
    }
    val mCookieJar:PersistentCookieJar by lazy {PersistentCookieJar(SetCookieCache(), SharedPrefsCookiePersistor(App.CONTEXT))
    }

}

新建业务 registory

    suspend fun getArticleList(page: Int, isRefresh: Boolean) = flow<BaseViewModel.BaseUiModel<ArticleList>> {RetrofitClient.wanService.getHomeArticles(page).doSuccess {emit(BaseViewModel.BaseUiModel(showSuccess = it, showLoading = false, isRefresh = isRefresh))
        }
    }.flowOn(Dispatchers.IO) // 切换线程
    .onStart {emit(BaseViewModel.BaseUiModel(showLoading = true))
    }.catch {emit(BaseViewModel.BaseUiModel(showError = it.message, showLoading = false, showEnd = false))
    }

新建 ViewModel

  • 在 HomeViewModel 中实例化 HomeRepository, 调用 getArticleList
  class HomeViewModel : BaseViewModel() {val repository = HomeRepository()
    val articleState = UnPeekLiveData<BaseUiModel<ArticleList>>()

    fun getArticleList(page: Int, isRefresh: Boolean) {
        launchOnUI {repository.getArticleList(page, isRefresh).collect {articleState.postValue(it)
            }
        }
    }
}

open class BaseViewModel : ViewModel() {fun launchOnUI(block: suspend CoroutineScope.() -> Unit) {viewModelScope.launch { block() }

    }
    suspend fun <T> launchOnIO(block: suspend CoroutineScope.() -> T) {withContext(Dispatchers.IO) {block}
    }
    open class UiState<T>(
        val isLoading: Boolean = false,
        val isRefresh: Boolean = false,
        val isSuccess: T? = null,
        val isError: String?= null
    )


    open class BaseUiModel<T>(
        var showLoading: Boolean = false,
        var showError: String? = null,
        var showSuccess: T? = null,
        var showEnd: Boolean = false, // 加载更多
        var isRefresh: Boolean = false // 刷新

    )

    override fun onCleared() {super.onCleared()
        viewModelScope.cancel()}

}

回到 UI 层,依据数据展现 ui

HomeFragment


  override fun startObserve() {
        homeViewModel.run {
            articleState.observe(this@HomeFragment, Observer {

                it.showSuccess?.let { list ->
                    if (isRefresh) {mBinding.refreshLayout.finishRefresh()
                        homeAdapter.data.clear()
                        homeAdapter.data.addAll(list.datas)
                    } else {mBinding.refreshLayout.finishLoadMore()
                        homeAdapter.data.addAll(list.datas)
                    }
                    currentPage = list.curPage
                    homeAdapter.notifyDataSetChanged()
                    if (list.over) {mBinding.refreshLayout.setEnableLoadMore(false)
                    } else {mBinding.refreshLayout.setEnableLoadMore(true)
                    }

                }
                it.showError?.let {if (isRefresh) {mBinding.refreshLayout.finishRefresh()
                    } else {mBinding.refreshLayout.finishLoadMore()
                    }
                }
            })
        }
    }

这样,一个残缺的基于 MVVM 思维的代码执行流程就结束了。

  • 这是残缺的目录构造,以及 [我的项目地址]

本文转自 https://juejin.cn/post/7028151530704338980,如有侵权,请分割删除。

退出移动版