Android Kotlin 可替换的网络隔离库的实现

前言

在我们的日常开发中,不可避免的会使用到网络请求,而网络请求库多种多样,如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模拟一个接口来请求数据

点击进入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())
    }
}

见证奇迹


可以看到效果一模一样,只需通过一行代码就能进行网络请求库的切换!
同理,这种写法适用于切换不同的图片加载框架等等

项目源码

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,658评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,482评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,213评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,395评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,487评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,523评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,525评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,300评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,753评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,048评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,223评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,905评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,541评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,168评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,417评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,094评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,088评论 2 352

推荐阅读更多精彩内容