前言
在我们的日常开发中,不可避免的会使用到网络请求,而网络请求库多种多样,如OKHTTP、volley、rxjava+retrofit等等,我们只会用到其中一种,但业务需求改变时,比如之前用的是volley,现在要改成OKHTTP,总不能每个网络请求都要重写吧,直接原地爆炸了,所以需要一个网络隔离库来实现这个需求。
动态代理
举个例子,假设这是一个买卖房的需求:
- 房产公司是卖房的人
- 有房的人买房回去房产公司登记自己的房子
- 客户买房需要去房产公司
- 业务员持有房子信息,代理房产公司去卖房
这样实际的业务行为就是客户买房就去找业务员
客户需要买什么样的房(用什么的样网络请求库),直接去找业务员就行了,业务员会搞定好一切的
这就是代理模式
Client就相当于客户,Subject就是房产公司,Proxy就是业务员,RealSubject就是我们的有房的人,RealSubject会向Subject提供一个真实的需求,就是卖房
正文
首先我们需要一个房产公司,也就是抽象主题类Subject(网络抽象层接口),房产公司有什么功能先给他列出来(网络请求库有什么功能)
IHttpProcessor
这就是房产公司
现在只有一个查房的功能(post请求)
interface IHttpProcessor {
//网络访问 post get del update put ...
fun post(url: String,params: Map<String,Any>,callback: ICallBack)
}
一般网络请求需要url,传递的参数(键值对),还有一个回调
ICallBack
interface ICallBack {
fun onSuccess(result: String)
fun onFailure(e: String)
}
HttpCallback
- ICallBack回调接口的实现
- 调用者传入的数据类型是未知的,通过泛型接受,反射获取到对应的类型
- 对返回JSON格式数据进行处理
abstract class HttpCallback<Result> : ICallBack{
override fun onSuccess(result: String) {
var gson = Gson()
//clz为用户输入的类型,如该类是String类,就返回String类
var clz: Class<*> = analysisClassInfo(this)
var objResult: Result = gson.fromJson(result,clz) as Result
//将处理的结果返回调用者
onSuccess(objResult)
}
override fun onFailure(e: String) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
abstract fun onSuccess(result: Result)
companion object {
private fun analysisClassInfo(any: Any): Class<*>{
//getGenericSuperclass()可以得到该类包含的原始类型,参数化类型,数组类型,类型变量,基本数据类型
var getType: Type = any.javaClass.genericSuperclass
//获取Result的参数化类型
var params: Array<Type> = (getType as ParameterizedType).actualTypeArguments
return params[0] as Class<*>
}
}
}
HttpHelper
公司有了,这时候就需要一个业务员了(代理类)
通过上面的代理图可以发现,有房的人的信息是被房产公司持有的
而业务员持有房产公司持有的有房的人的信息
所以业务员持有有房子的人的引用
class HttpHelper : IHttpProcessor {
override fun post(url: String, params: Map<String, Any>, callback: ICallBack) {
//业务员找到有房子的人
mIHttpProcessor!!.post(url, params, callback)
}
companion object {
//有房子的人(持有具体那套网络请求库的引用)
private var mIHttpProcessor: IHttpProcessor? = null
fun init(httpProcessor: IHttpProcessor){
mIHttpProcessor = httpProcessor
}
//单例模式
private var instance: HttpHelper? = null
@Synchronized fun getInstance():HttpHelper?{
if (instance == null){
instance = HttpHelper()
}
return instance
}
}
}
VolleyProcessor
具体的网络请求库实现(Volley的标准用法)
class VolleyProcessor constructor(var context: Context): IHttpProcessor{
init {
mQueue = Volley.newRequestQueue(context)
}
companion object {
private var mQueue: RequestQueue? = null
}
override fun post(url: String, params: Map<String, Any>,callback: ICallBack) {
var stringRequest = StringRequest(Request.Method.POST, url,
Response.Listener { callback.onSuccess(it) },
Response.ErrorListener { callback.onFailure(it.toString()) })
mQueue!!.add(stringRequest)
}
}
用MOCKY模拟一个接口来请求数据
这里MOCK的接口不需要传参数,参数为空
通用的框架初始化
标准写法,新建个App类,继承Application,在Mainfest清单文件声明一下配置
class App : Application() {
override fun onCreate() {
super.onCreate()
//初始化网络选择框架
HttpHelper.init(VolleyProcessor(this))
}
}
具体使用
ResponeseData是对应的Bean,这里就不贴出来了
class MainActivity : AppCompatActivity() {
var url = "http://www.mocky.io/v2/5c7a18b74900007e00a5a695"
var params: HashMap<String,Any> = HashMap()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//hello world 这个TextView弄了个点击事件
text.setOnClickListener {
HttpHelper.getInstance()!!.post(url,params,object : HttpCallback<ResponseData>(){
override fun onSuccess(result: ResponseData) {
Toast.makeText(application,result.hello,Toast.LENGTH_LONG).show()
}
})
}
}
}
红色划线部分传入的数据类型是什么,蓝色划线部分生产的数据类型就是什么,这是HttpCallbcak通过java的反射实现了,并定义了抽象类,让具体的业务实现交付到实际使用时再实现,这里只实现了onSuccess,onFailure同理
效果验证
如果成功,应该会弹出个吐司显示world
可以看到这是成功的
那么我们不用Volley进行网络请求呢?
改成OKHttp要怎么操作?
改用OKHttp进行同样网络请求
和VolleyProcess一样,实现来自IhttpProcessor的接口即可,再通过HttpHelper来代理使用OKHttp进行网络请求
class OKHttpPrpcessor : IHttpProcessor{
private var mOkHttpClient: OkHttpClient? = null
private var myHandler: Handler? = null
init {
mOkHttpClient = OkHttpClient()
myHandler = Handler()
}
override fun post(url: String, params: Map<String, Any>, callback: ICallBack) {
var request: Request = Request.Builder().url(url).build()
mOkHttpClient!!.newCall(request).enqueue(object : Callback{
override fun onFailure(call: Call?, e: IOException?) {
callback.onFailure(e.toString())
}
override fun onResponse(call: Call?, response: Response?) {
if (response!!.isSuccessful){
val result: String = response.body().string()
myHandler!!.post {
callback.onSuccess(result)
}
}
}
})
}
}
在.App里切换网络请求框架
class App : Application() {
override fun onCreate() {
super.onCreate()
//初始化网络选择框架
//HttpHelper.init(VolleyProcessor(this))
HttpHelper.init(OKHttpPrpcessor())
}
}
见证奇迹
可以看到效果一模一样,只需通过一行代码就能进行网络请求库的切换!
同理,这种写法适用于切换不同的图片加载框架等等