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 HomeViewModel
HomeRepository
, 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