初始化 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,如有侵权,请分割删除。