初始化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思想的代码执行流程就完毕了。
-
这是完整的目录结构,以及[项目地址]
相关教程
Android基础系列教程:
Android基础课程UI-布局_哔哩哔哩_bilibili
Android基础课程UI-控件_哔哩哔哩_bilibili
Android基础课程UI-动画_哔哩哔哩_bilibili
Android基础课程-activity的使用_哔哩哔哩_bilibili
Android基础课程-Fragment使用方法_哔哩哔哩_bilibili
Android基础课程-热修复/热更新技术原理_哔哩哔哩_bilibili
本文转自 https://juejin.cn/post/7028151530704338980,如有侵权,请联系删除。