一、背景
在日常開發(fā)過程中齐遵,網(wǎng)絡(luò)請(qǐng)求功能是必不可少的寂玲,因此從中衍生出了一系列網(wǎng)絡(luò)加載庫(kù),如URLConnection梗摇,Volley拓哟,OkHttp,Retrofit等伶授。而在項(xiàng)目的開發(fā)過程中断序,隨著需求的改變,我們使用的網(wǎng)絡(luò)加載庫(kù)也可能會(huì)隨著改變(替換網(wǎng)絡(luò)加載庫(kù))糜烹。因此违诗,本章介紹的是如何設(shè)計(jì)一種網(wǎng)絡(luò)庫(kù)隔離的框架,當(dāng)出現(xiàn)網(wǎng)絡(luò)加載庫(kù)替換的情況時(shí)景图,盡可能小的改動(dòng)源代碼(即符合開閉原則,擴(kuò)展是開放的碉哑,修改是封閉的)挚币。
二、設(shè)計(jì)思路
首先要明白的是扣典,使用網(wǎng)絡(luò)請(qǐng)求功能的界面入口是非常多的(例如登錄妆毕,各種數(shù)據(jù)獲取,文件上傳等)贮尖,因此笛粘,第一個(gè)需要處理的問題就是,如何避免網(wǎng)絡(luò)加載庫(kù)與頁(yè)面請(qǐng)求直接交互湿硝。當(dāng)出現(xiàn)網(wǎng)絡(luò)庫(kù)替換時(shí)薪前,大量的直接交互,帶來的后果必然是大量的源代碼修改关斜,這顯示是違法了我們的開閉原則示括。
接著是,如何引入新替換的網(wǎng)絡(luò)加載庫(kù)痢畜,這相當(dāng)于是新添加了另外一個(gè)網(wǎng)絡(luò)庫(kù)的各種請(qǐng)求功能垛膝。最終的效果就是我們使用著不同的網(wǎng)絡(luò)庫(kù)來完成相同的功能,既然功能是一致的丁稀,那么我們就需要考慮如何規(guī)范他們的功能(函數(shù))定義吼拥。
基于以上兩點(diǎn)的考慮,我們采用代理模式來實(shí)現(xiàn)我們的網(wǎng)絡(luò)庫(kù)隔離框架线衫。
三凿可、設(shè)計(jì)模式
1.架構(gòu)圖
2.說明
(1)代理模式:為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問
(2)Proxy代理類:用來替代實(shí)際的網(wǎng)絡(luò)加載庫(kù),避免界面代碼與實(shí)際的網(wǎng)絡(luò)加載直接交互
(3)RealSubject:真實(shí)請(qǐng)求類授账,在我們的案例中矿酵,就是一種網(wǎng)絡(luò)加載庫(kù)唬复。每添加一種網(wǎng)絡(luò)請(qǐng)求庫(kù),即添加一個(gè)對(duì)應(yīng)的真實(shí)請(qǐng)求類即可(這里就是根據(jù)不同的網(wǎng)絡(luò)加載庫(kù)全肮,實(shí)際進(jìn)行請(qǐng)求功能的地方)
(4)Subject:用來規(guī)范新添加的各種網(wǎng)絡(luò)加載庫(kù)以及代理類的功能使用敞咧。為什么要規(guī)范代理的功能?因?yàn)榇眍惞枷伲淼氖钦鎸?shí)類的功能行為休建,因此代理類需要與真實(shí)類的功能保持一致。
四评疗、Kotlin實(shí)現(xiàn)
1.Subject
// 代理模式中测砂,用于規(guī)范代理類與真實(shí)類的功能接口
// 我們暫且只定義了get和post功能
// ICallBack函數(shù)是自定義的請(qǐng)求回調(diào)類,后續(xù)介紹
interface IHttpProxy {
fun getHttp(url: String, callback: ICallBack)
fun postHttp(url: String, params: Map<String, Any>, callback: ICallBack)
}
2.Proxy
// object修飾百匆,是一種餓漢式單例模式
object HttpHelper:IHttpProxy {
// 代理類中砌些,持有真實(shí)對(duì)象的引用
private var httpProxyImpl :IHttpProxy? = null
// 初始化真實(shí)代理對(duì)象
fun init(httpImpl:IHttpProxy){
httpProxyImpl = httpImpl
}
override fun getHttp(url: String, callback: ICallBack) {
// 運(yùn)行時(shí),調(diào)用真實(shí)對(duì)象方法
httpProxyImpl!!.getHttp(url,callback)
}
override fun postHttp(url: String, params: Map<String, Any>, callback: ICallBack)
{
// 運(yùn)行時(shí)加匈,調(diào)用真實(shí)對(duì)象方法
httpProxyImpl!!.postHttp(url,params,callback)
}
}
(1)不了解Kotlin語(yǔ)法的存璃,請(qǐng)查看以下相關(guān)文章
【Kotlin_第一行代碼】 http://www.reibang.com/nb/35111692
(2)代理類,實(shí)現(xiàn)了上述定義的接口雕拼,并實(shí)現(xiàn)了對(duì)應(yīng)的功能纵东,并且可以看出,其功能都是直接調(diào)用真實(shí)類對(duì)象對(duì)應(yīng)功能函數(shù)
(3)代理類必須持有真實(shí)類的引用啥寇,否則無法實(shí)現(xiàn)對(duì)真實(shí)類的代理作用
(4)init方法表示的是傳入當(dāng)前需要被代理的真實(shí)類對(duì)象
3.RealSubject
// 實(shí)現(xiàn)代理模式中的接口
class OkHttpProxyImpl : IHttpProxy {
// 聲名主線程handler
val handler = Handler(Looper.getMainLooper())
//實(shí)現(xiàn)對(duì)應(yīng)的get功能函數(shù)
override fun getHttp(url: String, callback: ICallBack) {
// 創(chuàng)建okHttpClient對(duì)象
val mOkHttpClient = OkHttpClient()
//創(chuàng)建一個(gè)Request
val request = Request.Builder()
.url(url)
.build()
//new call
val call = mOkHttpClient.newCall(request)
//請(qǐng)求加入調(diào)度
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<String, Any>, callback: ICallBack) {
}
}
(1)不對(duì)OkHttp的使用做介紹
(2)該類是我們使用OkHttp網(wǎng)絡(luò)加載庫(kù)實(shí)現(xiàn)的真實(shí)類偎球。實(shí)現(xiàn)了對(duì)應(yīng)的接口,并在對(duì)應(yīng)的函數(shù)上辑甜,實(shí)現(xiàn)真實(shí)的網(wǎng)絡(luò)請(qǐng)求功能衰絮,并利用自定義的回調(diào)函數(shù),將結(jié)果回調(diào)到使用的地方
(3)目前僅實(shí)現(xiàn)get函數(shù)的邏輯功能磷醋,post函數(shù)同理岂傲。
4.自定義回調(diào)函數(shù)(ICallBack,IHttpCallBack)
// 最底層的回調(diào)類子檀,String類型镊掖,表示網(wǎng)絡(luò)請(qǐng)求的返回的json,xml的格式文件褂痰,即網(wǎng)絡(luò)請(qǐng)求返回的第一手?jǐn)?shù)據(jù)亩进,未進(jìn)行任何操作的數(shù)據(jù)
interface ICallBack {
fun onSuccess(result: String)
fun onFailure(result: String)
}
//基于ICallBack之上,再次封裝的抽象回調(diào)類缩歪,并對(duì)泛型進(jìn)行處理
abstract class IHttpCallBack<T> : ICallBack {
// 直接實(shí)現(xiàn)對(duì)應(yīng)的onSuccess函數(shù)归薛,并對(duì)json進(jìn)行解析以及泛型處理
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!!) // 返回最終以及解析完成的泛型對(duì)象
}
abstract fun onSuccess(result: T) // 最終解析后的回調(diào)函數(shù)
/**
* 獲取泛型的真實(shí)對(duì)象
*/
private fun getRealType(any: Any): Class<*> {
val genType = any.javaClass.genericSuperclass
val params = (genType as ParameterizedType).actualTypeArguments
return params[0] as Class<*>
}
}
5.界面請(qǐng)求
// 一個(gè)TextView + 一個(gè)Button的簡(jiǎn)單布局
class MainActivity : AppCompatActivity() {
// wanandroid開放的api,非常感謝鴻洋大神,獲取公眾號(hào)列表
val URL = "https://wanandroid.com/wxarticle/chapters/json"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// button的點(diǎn)擊事件
json_get_btn.setOnClickListener {
// 這里使用的是代理對(duì)象。直接與界面交互的是代理對(duì)象,而非具體的網(wǎng)絡(luò)加載類對(duì)象主籍。
// Author是自定義的JavaBean习贫,根據(jù)對(duì)應(yīng)的json編寫即可,不做介紹
HttpHelper.getHttp(URL, object : IHttpCallBack<Author>() {
// 請(qǐng)求成功千元,返回的是已經(jīng)經(jīng)過泛型處理的回調(diào)類
// 因?yàn)閭魅氲幕卣{(diào)類是IHttpCallBack苫昌,不是ICallBack
override fun onSuccess(result: Author) {
// json_result_tv是布局中的TextView,用于顯示結(jié)果
json_result_tv.text = result.getInfo()
Toast.makeText(
this@MainActivity,
"請(qǐng)求成功", Toast.LENGTH_SHORT
).show()
}
// 請(qǐng)求失敗后的回調(diào)類
override fun onFailure(result: String) {
json_result_tv.text = result
Toast.makeText(
this@MainActivity,
"請(qǐng)求失敗", Toast.LENGTH_SHORT
).show()
}
})
}
}
}
(1)需要注意幸海,使用代理類前祟身,需要先傳入被代理類的對(duì)象,該案例是在Application初始化時(shí)設(shè)置
class MyApp :Application() {
override fun onCreate() {
super.onCreate()
HttpHelper.init(OkHttpProxyImpl()) // 設(shè)置真實(shí)代理對(duì)象
}
}
6.替換網(wǎng)絡(luò)加載庫(kù)步驟
(1)仿照OkHttpProxyImpl物独,實(shí)現(xiàn)對(duì)應(yīng)網(wǎng)絡(luò)加載庫(kù)的真實(shí)類袜硫,如VolltyProxyImpl。
// Volley網(wǎng)絡(luò)加載庫(kù)真實(shí)請(qǐng)求類
class VolleyProxyImpl :IHttpProxy {
override fun postHttp(url: String, params: Map<String, Any>, callback: ICallBack) {
// 具體的volley post請(qǐng)求
}
override fun getHttp(url: String, callback: ICallBack) {
// 具體的volley get請(qǐng)求
}
}
(2)替換代理類中被代理的對(duì)象挡篓,即修改MyApp中的代碼
class MyApp :Application() {
override fun onCreate() {
super.onCreate()
HttpHelper.init(VolltyProxyImpl()) // 設(shè)置為新網(wǎng)絡(luò)加載類對(duì)象
}
}
7.最后
至此婉陷,我們的網(wǎng)絡(luò)隔離庫(kù)框架雛形已搭建完畢,更多的功能請(qǐng)自定擴(kuò)展官研。如有任何不正確地方秽澳,歡迎批評(píng)指正。
非常感謝【騰訊課堂-Android高級(jí)開發(fā)專題課】
【項(xiàng)目地址】:https://github.com/y0000c/HttpProxyMode