IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 一步步封装实现自己的网络请求框架 3,免费Android高级工程师学习资源 -> 正文阅读

[移动开发]一步步封装实现自己的网络请求框架 3,免费Android高级工程师学习资源

上文有说到,ReactiveHttp 提供了在网络请求过程中自动完成 showLoading、dismissLoading、showToast 等行为的能力。首先,BaseRemoteDataSource 在网络请求过程中会通过 IUIActionEvent 接口来通知 BaseReactiveViewModel 需要触发的行为,从而连锁触发 ShowLoadingLiveData、DismissLoadingLiveData、ShowToastLiveData 值的变化,BaseReactiveActivity 就通过监听 LiveData 值的变化来完成 UI 层操作

四、惯常做法

以下步骤应该是大部分应用目前进行网络请求时的惯常做法了

服务端返回给移动端的数据使用具有特定格式的 Json 来进行通信,用整数 status 来标明本次请求是否成功,在失败时则直接 showToast(msg)data则需要用泛型来声明了,最终就对应移动端的一个泛型类,类似于 HttpWrapBean

{
“status”:200,
“msg”:“success”,
“data”:""
}

data class HttpWrapBean(val status: Int, val msg: String, val data: T)

然后在 interface 中声明 Api 接口,这也是使用 Retrofit 的惯常用法。根据项目中的实际情况,开发者可能是使用 Call 或者 Observable 作为每个接口返回值的最外层的数据包装类,然后再使用 HttpWrapBean 来作为具体数据类的包装类

interface ApiService {

@POST(“api1”)
fun api1(): Observable<HttpWrapBean>

@GET(“api2”)
fun api2(): Call<HttpWrapBean>

}

然后项目中使用的是 RxJava,那么就需要像以下这样来完成网络请求

val retrofit = Retrofit.Builder()
.baseUrl(“https://xxx.com”)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
val service = retrofit.create(ApiService::class.java)
val call: Observable<HttpWrapBean> = service.api1()
call.subscribe(object : Consumer<HttpWrapBean> {
override fun accept(userBean: HttpWrapBean?) {

}

}, object : Consumer {
override fun accept(t: Throwable?) {

}
})

五、简单入门

ReactiveHttp 在使用上会比上面给出的例子简单很多,下面就来看下通过 ReactiveHttp 如何完成网络请求

ReactiveHttp 需要知道网络请求的结果,但不知道外部会使用什么字段名来标识 HttpWrapBean 中的三个值,所以需要外部实现 IHttpWrapBean 接口来进行标明。例如,你可以这样来实现:

data class HttpWrapBean(val status: Int, val msg: String, val data: T) : IHttpWrapBean {

override val httpCode: Int
get() = status

override val httpMsg: String
get() = msg

override val httpData: T
get() = data

//网络请求是否成功
override val httpIsSuccess: Boolean
get() = status == 200

}

suspend来修饰接口方法,且不需要其它的外层包装类。suspend是 kotlin 协程引入的,当用该关键字修饰接口方法时,Retrofit 内部就会使用协程的方式来完成该网络请求

interface ApiService {

@GET(“config/district”)
suspend fun getProvince(): HttpWrapBean<List>

}

ReactiveHttp 提供了 RemoteExtendDataSource 交由外部来继承实现。RemoteExtendDataSource 包含了所有的网络请求方法,外部仅需要根据实际情况来实现三个必要的字段和方法即可

  • releaseUrl。即应用的 BaseUrl
  • createRetrofit。用于创建 Retrofit,开发者可以在这里自定义 OkHttpClient
  • showToast。当网络请求失败时,通过该方法来向用户提示失败原因

例如,你可以像以下这样来实现你自己项目的专属 DataSource

《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》

【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享

,当中就包含了开发者整个项目的全局网络请求配置

class SelfRemoteDataSource(iActionEvent: IUIActionEvent?) : RemoteExtendDataSource(iActionEvent, ApiService::class.java) {

companion object {

private val httpClient: OkHttpClient by lazy {
createHttpClient()
}

private fun createHttpClient(): OkHttpClient {
val builder = OkHttpClient.Builder()
.readTimeout(1000L, TimeUnit.MILLISECONDS)
.writeTimeout(1000L, TimeUnit.MILLISECONDS)
.connectTimeout(1000L, TimeUnit.MILLISECONDS)
.retryOnConnectionFailure(true)
.addInterceptor(FilterInterceptor())
.addInterceptor(MonitorInterceptor(MainApplication.context))
return builder.build()
}

}

/**

  • 由子类实现此字段以便获取 release 环境下的接口 BaseUrl
    */
    override val releaseUrl: String
    get() = “https://restapi.amap.com/v3/”

/**

  • 允许子类自己来实现创建 Retrofit 的逻辑
  • 外部无需缓存 Retrofit 实例,ReactiveHttp 内部已做好缓存处理
  • 但外部需要自己判断是否需要对 OKHttpClient 进行缓存
  • @param baseUrl
    */
    override fun createRetrofit(baseUrl: String): Retrofit {
    return Retrofit.Builder()
    .client(httpClient)
    .baseUrl(baseUrl)
    .addConverterFactory(GsonConverterFactory.create())
    .build()
    }

override fun showToast(msg: String) {
Toast.makeText(MainApplication.context, msg, Toast.LENGTH_SHORT).show()
}

}

之后,我们就可以依靠 SelfRemoteDataSource 在任意地方发起网络请求了,按需声明 Callback 方法。此外,由于使用到了扩展函数,所以 SelfRemoteDataSource 中可以直接调用 ApiService 中的接口方法,无需特意引用和导包

private val remoteDataSource = SelfRemoteDataSource(null)

val provinceLiveData = MutableLiveData<List>()

fun reqProvince() {
//enqueueLoading 方法会在发起网络请求的时候同时弹出 loading 框
//enqueue 方法则不会弹出 loading 框
remoteDataSource.enqueueLoading({
getProvince()
}) {
/**

  • 在显示 Loading 之后且开始网络请求之前执行
    */
    onStart {

}
/**

  • 当网络请求成功时回调
    /
    onSuccess {
    provinceLiveData.value = it
    }
    /
    *
  • 当取消网络请求时就会回调此方法
    */
    onCancelled {

}
/**

  • 当网络请求失败时会调用此方法,在 onFinally 被调用之前执行
    /
    onFailed {
    val httpException = it
    val realException = httpException.realException
    val errorCode = httpException.errorCode
    }
    /
    *
  • 用于控制是否当网络请求失败时 Toast 失败原因
  • 默认为 true,即会 Toast 提示
    /
    onFailToast {
    true
    }
    /
    *
  • 在网络请求结束之后(不管请求成功与否)且隐藏 Loading 之前执行
    */
    onFinally {

}
}
}

六、进阶使用

上述在使用 SelfRemoteDataSource 发起网络请求时虽然调用的是 enqueueLoading 方法,但实际上并不会弹出 loading 框,因为完成 ShowLoading、DismissLoading、ShowToast 等 UI 行为是需要 RemoteDataSource、ViewModel 和 Activity 这三者一起进行配合的,即 SelfRemoteDataSource 需要和其它两者关联上,将需要触发的 UI 行为反馈给 Activity

这可以通过直接继承于 BaseReactiveActivity 和 BaseReactiveViewModel 来实现,也可以通过实现相应接口来完成关联。当然,如果你不需要 ReactiveHttp 的各个自动化行为的话,也可以不做以下任何改动

总的来说,ReactiveHttp 具有极低的接入成本

1、BaseReactiveActivity

BaseReactiveActivity 是 ReactiveHttp 提供的一个默认 BaseActivity,其实现了 IUIActionEventObserver 接口,用于提供一些默认参数和默认行为,例如 CoroutineScope 和 showLoading。但在大多数情况下,我们自己的项目是不会去继承外部 Activity 的,而是会有一个自己实现的全局统一的 BaseActivity,所以如果你不想继承 BaseReactiveActivity 的话,可以自己来实现 IUIActionEventObserver 接口,就像以下这样

@SuppressLint(“Registered”)
abstract class BaseActivity : AppCompatActivity(), IUIActionEventObserver {

protected inline fun getViewModel(
factory: ViewModelProvider.Factory? = null,
noinline initializer: (VM.(lifecycleOwner: LifecycleOwner) -> Unit)? = null
): Lazy where VM : ViewModel, VM : IViewModelActionEvent {
return getViewModel(VM::class.java, factory, initializer)
}

override val lifecycleSupportedScope: CoroutineScope
get() = lifecycleScope

override val lContext: Context?
get() = this

override val lLifecycleOwner: LifecycleOwner
get() = this

private var loadDialog: ProgressDialog? = null

override fun showLoading(job: Job?) {
dismissLoading()
loadDialog = ProgressDialog(lContext).apply {
setCancelable(true)
setCanceledOnTouchOutside(false)
//用于实现当弹窗销毁的时候同时取消网络请求
// setOnDismissListener {
// job?.cancel()
// }
show()
}
}

override fun dismissLoading() {
loadDialog?.takeIf { it.isShowing }?.dismiss()
loadDialog = null
}

override fun showToast(msg: String) {
if (msg.isNotBlank()) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
}
}

override fun finishView() {
finish()
}

override fun onDestroy() {
super.onDestroy()
dismissLoading()
}

}

2、BaseReactiveViewModel

类似地,BaseReactiveViewModel 是 ReactiveHttp 提供的一个默认的 BaseViewModel,其实现了 IViewModelActionEvent 接口,用于接收 RemoteDataSource 发起的 UI 层行为。如果你不希望继承于 BaseReactiveViewModel 的话,可以自己来实现 IViewModelActionEvent 接口,就像以下这样

open class BaseViewModel : ViewModel(), IViewModelActionEvent {

override val lifecycleSupportedScope: CoroutineScope
get() = viewModelScope

override val showLoadingEventLD = MutableLiveData()

override val dismissLoadingEventLD = MutableLiveData()

override val showToastEventLD = MutableLiveData()

override val finishViewEventLD = MutableLiveData()

}

3、关联上

完成以上两步后,开发者就可以像如下所示这样将 RemoteDataSource、ViewModel 和 Activity 这三者给关联起来。WeatherActivity 通过 getViewModel 方法来完成 WeatherViewModel 的初始化和内部多个 UILiveData 的绑定,并在 lambda 表达式中完成对 WeatherViewModel 内部和具体业务相关的 DataLiveData 的数据监听,至此所有自动化行为就都已经绑定上了

class WeatherViewModel : BaseReactiveViewModel() {

private val remoteDataSource by lazy {
SelfRemoteDataSource(this)
}

val forecastsBeanLiveData = MutableLiveData()

fun getWeather(city: String) {
remoteDataSource.enqueue({
getWeather(city)
}) {
onSuccess {
if (it.isNotEmpty()) {
forecastsBeanLiveData.value = it[0]
}
}
}
}

}

class WeatherActivity : BaseReactiveActivity() {

private val weatherViewModel by getViewModel {
forecastsBeanLiveData.observe(this@WeatherActivity, {
showWeather(it)
})
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_weather)
weatherViewModel.getWeather(“adCode”)
}

private fun showWeather(forecastsBean: ForecastsBean) {

}

}

七、其它

1、BaseRemoteDataSource

RemoteExtendDataSource 提供了许多个可以进行复写的方法,既可用于配置 OkHttp 的各个网络请求参数,也用于交由外部进行流程控制。例如,你可以这样来实现自己项目的 BaseRemoteDataSource

class BaseRemoteDataSource(iActionEvent: IUIActionEvent?) : RemoteExtendDataSource(iActionEvent, ApiService::class.java) {

companion object {

private val httpClient: OkHttpClient by lazy {
createHttpClient()
}

private fun createHttpClient(): OkHttpClient {
val builder = OkHttpClient.Builder()
.readTimeout(1000L, TimeUnit.MILLISECONDS)
.writeTimeout(1000L, TimeUnit.MILLISECONDS)
.connectTimeout(1000L, TimeUnit.MILLISECONDS)
.retryOnConnectionFailure(true)
.addInterceptor(FilterInterceptor())
.addInterceptor(MonitorInterceptor(MainApplication.context))
return builder.build()
}
}

/**

  • 由子类实现此字段以便获取 release 环境下的接口 BaseUrl
    */
    override val releaseUrl: String
    get() = HttpConfig.BASE_URL_MAP

/**

  • 允许子类自己来实现创建 Retrofit 的逻辑
  • 外部无需缓存 Retrofit 实例,ReactiveHttp 内部已做好缓存处理
  • 但外部需要自己判断是否需要对 OKHttpClient 进行缓存
  • @param baseUrl
    */
    override fun createRetrofit(baseUrl: String): Retrofit {
    return Retrofit.Builder()
    .client(httpClient)
    .baseUrl(baseUrl)
    .addConverterFactory(GsonConverterFactory.create())
    .build()
    }

/**

  • 如果外部想要对 Throwable 进行特殊处理,则可以重写此方法,用于改变 Exception 类型
  • 例如,在 token 失效时接口一般是会返回特定一个 httpCode 用于表明移动端需要去更新 token 了
  • 此时外部就可以实现一个 BaseException 的子类 TokenInvalidException 并在此处返回
  • 从而做到接口异常原因强提醒的效果,而不用去纠结 httpCode 到底是多少
    */
    override fun generateBaseException(throwable: Throwable): BaseHttpException {
    return if (throwable is BaseHttpException) {
    throwable
    } else {
    LocalBadException(throwable)
    }
    }

/**

  • 用于由外部中转控制当抛出异常时是否走 onFail 回调,当返回 true 时则回调,否则不回调
  • @param httpException
    */
    override fun exceptionHandle(httpException: BaseHttpException): Boolean {
    return true
    }

/**

  • 用于将网络请求过程中的异常反馈给外部,以便记录
  • @param throwable
    */
    override fun exceptionRecord(throwable: Throwable) {
    Log.e(“SelfRemoteDataSource”, throwable.message ?: “”)
    }

/**

  • 用于对 BaseException 进行格式化,以便在请求失败时 Toast 提示错误信息
  • @param httpException
    */
    override fun exceptionFormat(httpException: BaseHttpException): String {
    return when (httpException.realException) {
    null -> {
    httpException.errorMessage
    }
    is ConnectException, is SocketTimeoutException, is UnknownHostException -> {
    “连接超时,请检查您的网络设置”
    }
    else -> {
    “请求过程抛出异常:” + httpException.errorMessage
    }
    }
    }

override fun showToast(msg: String) {
Toast.makeText(MainApplication.context, msg, Toast.LENGTH_SHORT).show()
}

}

此外,开发者可以直接在自己的 BaseViewModel 中声明一个 BaseRemoteDataSource 变量实例,所有子 ViewModel 都全局统一使用同一份 DataSource 配置。如果有某些特定接口需要使用不同的 BaseUrl 的话,也可以再多声明一个 BaseRemoteDataSource

open class BaseViewModel : BaseReactiveViewModel() {

/**

  • 正常来说单个项目中应该只有一个 RemoteDataSource 实现类,即全局使用同一份配置
  • 但父类也应该允许子类使用一个独有的 RemoteDataSource,即允许子类复写此字段
    */
    protected open val remoteDataSource by lazy {
    BaseRemoteDataSource(this)
    }

}

2、BaseHttpException

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章           查看所有文章
加:2021-12-14 16:03:59  更:2021-12-14 16:07:18 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/28 3:11:49-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码