Initialize the 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)
						// Can be customized according to their own taste
            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)
    }
}

Copy the code
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))
    }

}
Copy the code

Create a new business 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) // Switch threads
    .onStart {
        emit(BaseViewModel.BaseUiModel(showLoading = true))}.catch {
        emit(BaseViewModel.BaseUiModel(showError = it.message, showLoading = false, showEnd = false))}Copy the code

New ViewModel

  • Instantiate in HomeViewModelHomeRepository, the call 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.valisError: String? =null
    )


    open class BaseUiModel<T>(
        var showLoading: Boolean = false.var showError: String? = null.var showSuccess: T? = null.var showEnd: Boolean = false.// Load more
        var isRefresh: Boolean = false / / refresh

    )

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

}

Copy the code

Go back to the UI layer and present the UI based on the data

HomeFragment


  override fun startObserve(a) {
        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()
                    }
                }
            })
        }
    }
Copy the code

This completes a complete code execution flow based on MVVM ideas.

  • This is the complete directory structure, along with the project address