一、背景
在日常开发过程中,网络请求功能是必不可少的,因此从中衍生出了一系列网络加载库,如URLConnection,Volley,OkHttp,Retrofit等。而在项目的开发过程中,随着需求的改变,我们使用的网络加载库也可能会随着改变(替换网络加载库)。因此,本章介绍的是如何设计一种网络库隔离的框架,当出现网络加载库替换的情况时,尽可能小的改动源代码(即符合开闭原则,扩展是开放的,修改是封闭的)。
二、设计思路
首先要明白的是,使用网络请求功能的界面入口是非常多的(例如登录,各种数据获取,文件上传等),因此,第一个需要处理的问题就是,如何避免网络加载库与页面请求直接交互。当出现网络库替换时,大量的直接交互,带来的后果必然是大量的源代码修改,这显示是违法了我们的开闭原则。
接着是,如何引入新替换的网络加载库,这相当于是新添加了另外一个网络库的各种请求功能。最终的效果就是我们使用着不同的网络库来完成相同的功能,既然功能是一致的,那么我们就需要考虑如何规范他们的功能(函数)定义。
基于以上两点的考虑,我们采用代理模式来实现我们的网络库隔离框架。
三、设计模式
1.架构图
2.说明
(1)代理模式:为其他对象提供一种代理以控制对这个对象的访问
(2)Proxy代理类:用来替代实际的网络加载库,避免界面代码与实际的网络加载直接交互
(3)RealSubject:真实请求类,在我们的案例中,就是一种网络加载库。每添加一种网络请求库,即添加一个对应的真实请求类即可(这里就是根据不同的网络加载库,实际进行请求功能的地方)
(4)Subject:用来规范新添加的各种网络加载库以及代理类的功能使用。为什么要规范代理的功能?因为代理类,代理的是真实类的功能行为,因此代理类需要与真实类的功能保持一致。
四、Kotlin实现
1.Subject
// 代理模式中,用于规范代理类与真实类的功能接口
// 我们暂且只定义了get和post功能
// ICallBack函数是自定义的请求回调类,后续介绍
interface IHttpProxy {
fun getHttp(url: String, callback: ICallBack)
fun postHttp(url: String, params: Map, callback: ICallBack)
}
2.Proxy
// object修饰,是一种饿汉式单例模式
object HttpHelper:IHttpProxy {
// 代理类中,持有真实对象的引用
private var httpProxyImpl :IHttpProxy? = null
// 初始化真实代理对象
fun init(httpImpl:IHttpProxy){
httpProxyImpl = httpImpl
}
override fun getHttp(url: String, callback: ICallBack) {
// 运行时,调用真实对象方法
httpProxyImpl!!.getHttp(url,callback)
}
override fun postHttp(url: String, params: Map, callback: ICallBack)
{
// 运行时,调用真实对象方法
httpProxyImpl!!.postHttp(url,params,callback)
}
}
(1)不了解Kotlin语法的,请查看以下相关文章
【Kotlin_第一行代码】 https://www.jianshu.com/nb/35111692
(2)代理类,实现了上述定义的接口,并实现了对应的功能,并且可以看出,其功能都是直接调用真实类对象对应功能函数
(3)代理类必须持有真实类的引用,否则无法实现对真实类的代理作用
(4)init方法表示的是传入当前需要被代理的真实类对象
3.RealSubject
// 实现代理模式中的接口
class OkHttpProxyImpl : IHttpProxy {
// 声名主线程handler
val handler = Handler(Looper.getMainLooper())
//实现对应的get功能函数
override fun getHttp(url: String, callback: ICallBack) {
// 创建okHttpClient对象
val mOkHttpClient = OkHttpClient()
//创建一个Request
val request = Request.Builder()
.url(url)
.build()
//new call
val call = mOkHttpClient.newCall(request)
//请求加入调度
call.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
handler.post {
callback.onFailure(e.toString())
}
}
override fun onResponse(call: Call, response: Response) {
if (response.isSuccessful) {
val string = response.body()?.string()
handler.post {
callback.onSuccess(string!!)
}
} else {
handler.post {
callback.onFailure(response.message())
}
}
}
})
}
override fun postHttp(url: String, params: Map, callback: ICallBack) {
}
}
(1)不对OkHttp的使用做介绍
(2)该类是我们使用OkHttp网络加载库实现的真实类。实现了对应的接口,并在对应的函数上,实现真实的网络请求功能,并利用自定义的回调函数,将结果回调到使用的地方
(3)目前仅实现get函数的逻辑功能,post函数同理。
4.自定义回调函数(ICallBack,IHttpCallBack)
// 最底层的回调类,String类型,表示网络请求的返回的json,xml的格式文件,即网络请求返回的第一手数据,未进行任何操作的数据
interface ICallBack {
fun onSuccess(result: String)
fun onFailure(result: String)
}
//基于ICallBack之上,再次封装的抽象回调类,并对泛型进行处理
abstract class IHttpCallBack : ICallBack {
// 直接实现对应的onSuccess函数,并对json进行解析以及泛型处理
override fun onSuccess(result: String) {
val obj = (Gson().fromJson(result, getRealType(this)))
val realObj: T? = try {
obj as T
} catch (e: Exception) {
null
}
onSuccess(realObj!!) // 返回最终以及解析完成的泛型对象
}
abstract fun onSuccess(result: T) // 最终解析后的回调函数
/**
* 获取泛型的真实对象
*/
private fun getRealType(any: Any): Class<*> {
val genType = any.javaClass.genericSuperclass
val params = (genType as ParameterizedType).actualTypeArguments
return params[0] as Class<*>
}
}
5.界面请求
// 一个TextView + 一个Button的简单布局
class MainActivity : AppCompatActivity() {
// wanandroid开放的api,非常感谢鸿洋大神,获取公众号列表
val URL = "https://wanandroid.com/wxarticle/chapters/json"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// button的点击事件
json_get_btn.setOnClickListener {
// 这里使用的是代理对象。直接与界面交互的是代理对象,而非具体的网络加载类对象。
// Author是自定义的JavaBean,根据对应的json编写即可,不做介绍
HttpHelper.getHttp(URL, object : IHttpCallBack() {
// 请求成功,返回的是已经经过泛型处理的回调类
// 因为传入的回调类是IHttpCallBack,不是ICallBack
override fun onSuccess(result: Author) {
// json_result_tv是布局中的TextView,用于显示结果
json_result_tv.text = result.getInfo()
Toast.makeText(
this@MainActivity,
"请求成功", Toast.LENGTH_SHORT
).show()
}
// 请求失败后的回调类
override fun onFailure(result: String) {
json_result_tv.text = result
Toast.makeText(
this@MainActivity,
"请求失败", Toast.LENGTH_SHORT
).show()
}
})
}
}
}
(1)需要注意,使用代理类前,需要先传入被代理类的对象,该案例是在Application初始化时设置
class MyApp :Application() {
override fun onCreate() {
super.onCreate()
HttpHelper.init(OkHttpProxyImpl()) // 设置真实代理对象
}
}
6.替换网络加载库步骤
(1)仿照OkHttpProxyImpl,实现对应网络加载库的真实类,如VolltyProxyImpl。
// Volley网络加载库真实请求类
class VolleyProxyImpl :IHttpProxy {
override fun postHttp(url: String, params: Map, callback: ICallBack) {
// 具体的volley post请求
}
override fun getHttp(url: String, callback: ICallBack) {
// 具体的volley get请求
}
}
(2)替换代理类中被代理的对象,即修改MyApp中的代码
class MyApp :Application() {
override fun onCreate() {
super.onCreate()
HttpHelper.init(VolltyProxyImpl()) // 设置为新网络加载类对象
}
}
7.最后
至此,我们的网络隔离库框架雏形已搭建完毕,更多的功能请自定扩展。如有任何不正确地方,欢迎批评指正。
非常感谢【腾讯课堂-Android高级开发专题课】
【项目地址】:https://github.com/y0000c/HttpProxyMode