DSL方式的请求,自由处理各种start、response、error回调,或者交给BaseViewModel统一处理。
回调方式请求,自由处理各种start、response、error回调,或者交给BaseViewModel统一处理。
LiveData方式请求,请求直接返回LiveData。
DSL方式灵活配置OkHttpClient/Retrofit。
请求的声明如下:
interface TestService {
@GET(“/banner/json”)
suspend fun getBanner(): WanResponse<List>
}
可以看到加成了协程以后的retrofit在生命网络请求以后变得异常简单,不需要用Call或者Observable进行包装,直接返回想要的实体类就好。suspend是Kotlin的关键字,修饰方法是表示为挂起函数,只能运行在协程或者其他挂起函数中。
请求的OKHttp、Retrofit的配置示例如下
Request.init(context = this.applicationContext, baseUrl = “https://www.wanandroid.com”) {
okHttp {okhttpBuilder->
//配置okhttp
okhttpBuilder
}
retrofit {retrofitBuilder->
//配置retrofit
retrofitBuilder
}
}
如示例代码所示,通过DSL化的代码书写方式可以灵活的通过okHttp或者retrofit代码块来灵活配置okHttp和retrofit。当然用户可以直接选择不进行任何配置,基本的配置在Request.kt中已经配置完成,在使用默认配置的情况下完全可以不书写okHttp或者retrofit代码块。
需要说明的是初始化过程中传入了context,是由于在Request.kt中存在关于持久化cookie的配置,cookie持久化到SP中时需要context来创建SP。还有okHttp和retrofit看似是代码块其实是带函数类型参数的方法而已,正是利用了kotlin对高阶函数、扩展函数、lambda表达式的友好支持和invoke约定才能写出如上所示的DSL化的保证可读性的整洁灵活的代码。
关于DSL方式请求调用示例如下
class TestViewModel : BaseViewModel() {
private val service by lazy { Request.apiService(TestService::class.java) }
val liveData = MutableLiveData<WanResponse<List>>()
fun loadDSL() {
apiDSL<WanResponse<List>> {
onRequest {
service.getBanner()
}
onResponse {response->
Log.e(“Thread–>onResponse”, Thread.currentThread().name)
Log.e(“onResponse–>”, Gson().toJson(response))
liveData.value = response
}
onStart {
Log.e(“Thread–>onStart”, Thread.currentThread().name)
false
}
onError {
it.printStackTrace()
Log.e(“Thread–>onError”, Thread.currentThread().name)
true
}
}
}
}
如上可见,在onRequest中一股脑塞入请求就可以在onResponse中拿到请求结果。同时也可以在主线程的onStart中自由预处理一些逻辑,可以看到onStart代码块最后默认返回了false,false表示不拦截BaseViewModel中对网络请求开始时的处理(比如弹出统一样式的loading)。
如果返回true则表示该行为完全由自己处理。同理针对onError也是一样的道理,可以自己处理错误也可以交给base处理。当然也可以不写onStart和onError完全交给base来处理相关行为,使网络请求代码更简洁。
关于回调方式请求调用示例如下
fun loadCallback() {
apiCallback({
service.getBanner()
}, {
liveData.value = it//这里是onResponse的回调
}, {
true//这里是onStart的回调
}, onError ={ exception ->
false
})
}
借助函数类型(Any) -> Any来定义请求的不同回调,比如error的回调可以定义为((Exception) -> Boolean)?。接受exception来处理异常,返回bool类型来决定是否继续交给base来继续处理。同时定义成可空类型可以默认交给base出路。但是显而易见的是这种代码书写方式并不如DSL方式的请求美观和可读性高。
关于直接返回LiveData的请求调用示例如下
fun loadLiveData(): LiveData<Result<WanResponse<List>>> {
return apiLiveData(SupervisorJob() + Dispatchers.Main.immediate, timeoutInMs = 2000) {
service.getBanner()
}
}
在V层拿到LiveData后的操作如下:
viewModel.loadLiveData().observe(this, Observer {
when (it) {
is Result.Error -> {
hideLoading()
}
is Result.Response -> {
hideLoading()
it.response.apply {
showToast(Gson().toJson(this))
}
}
is Result.Start -> {
showLoading()
}
else ->{//冗余
}
}
})
显然这种方式的请求更适合轻量化的请求,适合拿到结果直接去渲染view不经过二次数据处理的场景。因为如上图所示在V层处理start、error回调感觉不是很友好,,在reponse中隐藏loading也是比较繁琐。但好处是V层直接可以拿到包含请求数据的LiveData,操作更加便捷。
关于Livedata的封装如下
protected fun apiLiveData(
context: CoroutineContext = EmptyCoroutineContext,
timeoutInMs: Long = 3000L,
request: suspend () -> Response
): LiveData<Result> {
return androidx.lifecycle.liveData(context, timeoutInMs) {
emit(Result.Start())
try {
emit(withContext(Dispatchers.IO) {
Result.Response(request())
})
} catch (e: Exception) {
e.printStackTrace()
emit(Result.Error(e))
} finally {
emit(Result.Finally())
}
}
}
此处的livedata是lifecycle-livedata-ktx,在配置了timeoutInMs后如果没有活跃的observers就会超时自动取消。在IO线程拿到请求的结果后包装成Result,像RxJava那样发射出来即可。为了保证返回的livedata中数据的一致性,start、error也被包装成了Result。
接下来我们以对okhttp和retrofit的请求配置来看下是怎么进行DSL封装的,不多说showcode。
class RequestDsl {
internal var buidOkHttp: ((OkHttpClient.Builder) -> OkHttpClient.Builder)? = null
internal var buidRetrofit: ((Retrofit.Builder) -> Retrofit.Builder)? = null
fun okHttp(builder: ((OkHttpClient.Builder) -> OkHttpClient.Builder)?) {
this.buidOkHttp = builder
}
fun retrofit(builder: ((Retrofit.Builder) -> Retrofit.Builder)?) {
this.buidRetrofit = builder
}
}
首先是DSL的配置类,主要有2个角色,一个是函数类型的buidOkHttp,一个是以buidOkHttp为参数的配置buidOkHttp的高阶函数okHttp。可见buidOkHttp变量是一个可空类型的输入和返回是非空的OkHttpClient.Builder类型的函数,既然是可空类型的我们在初始化调用时就可以选择配置OkHttpClient.Builder与否。
既然输入返回都是OkHttpClient.Builder我们就可以拿到既定的带有初始化配置的OkHttpClient.Builder进行进一部配置,只要最后返回OkHttpClient.Builder就好,同时OkHttpClient.Builder采用了建造者模式我们可以拿到builder引用之后进行二次配置最后原样返回builder的引用。
下面是初始化方法的具体实现
private fun initRequest(okHttpBuilder: OkHttpClient.Builder, requestDSL: (RequestDsl.() -> Unit)? = null) {
val dsl = if (requestDSL != null) RequestDsl().apply(requestDSL) else null
val finalOkHttpBuilder = dsl?.buidOkHttp?.invoke(okHttpBuilder) ?: okHttpBuilder
val retrofitBuilder = Retrofit.Builder()
.baseUrl(this.baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.client(finalOkHttpBuilder.build())
val finalRetrofitBuilder = dsl?.buidRetrofit?.invoke(retrofitBuilder) ?: retrofitBuilder
this.retrofit = finalRetrofitBuilder.build()
}
最后
**一个零基础的新人,我认为坚持是最最重要的。**我的很多朋友都找我来学习过,我也很用心的教他们,可是不到一个月就坚持不下来了。我认为他们坚持不下来有两点主要原因:
他们打算入行不是因为兴趣,而是因为所谓的IT行业工资高,或者说完全对未来没有任何规划。
刚开始学的时候确实很枯燥,这确实对你是个考验,所以说坚持下来也很不容易,但是如果你有兴趣就不会认为这是累,不会认为这很枯燥,总之还是贵在坚持。
技术提升遇到瓶颈了?缺高级Android进阶视频学习提升自己吗?还有大量大厂面试题为你面试做准备!
提升自己去挑战一下BAT面试难关吧
对于很多Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。整理的这些知识图谱希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。
不论遇到什么困难,都不应该成为我们放弃的理由!
如果有什么疑问的可以直接私我,我尽自己最大力量帮助你!
最后祝各位新人都能坚持下来,学有所成。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!
颈了?缺高级Android进阶视频学习提升自己吗?还有大量大厂面试题为你面试做准备!**
提升自己去挑战一下BAT面试难关吧
[外链图片转存中…(img-o2rlaHwH-1715229492592)]
对于很多Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。整理的这些知识图谱希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。
不论遇到什么困难,都不应该成为我们放弃的理由!
如果有什么疑问的可以直接私我,我尽自己最大力量帮助你!
最后祝各位新人都能坚持下来,学有所成。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!