在平常開發過程當中,網絡請求功能是必不可少的,所以從中衍生出了一系列網絡加載庫,如URLConnection,Volley,OkHttp,Retrofit等。而在項目的開發過程當中,隨着需求的改變,咱們使用的網絡加載庫也可能會隨着改變(替換網絡加載庫)。所以,本章介紹的是如何設計一種網絡庫隔離的框架,當出現網絡加載庫替換的狀況時,儘量小的改動源代碼(即符合開閉原則,擴展是開放的,修改是封閉的)。java
首先要明白的是,使用網絡請求功能的界面入口是很是多的(例如登陸,各類數據獲取,文件上傳等),所以,第一個須要處理的問題就是,如何避免網絡加載庫與頁面請求直接交互。當出現網絡庫替換時,大量的直接交互,帶來的後果必然是大量的源代碼修改,這顯示是違法了咱們的開閉原則。android
接着是,如何引入新替換的網絡加載庫,這至關因而新添加了另一個網絡庫的各類請求功能。最終的效果就是咱們使用着不一樣的網絡庫來完成相同的功能,既然功能是一致的,那麼咱們就須要考慮如何規範他們的功能(函數)定義。git
基於以上兩點的考慮,咱們採用代理模式來實現咱們的網絡庫隔離框架。github
(1)代理模式:爲其餘對象提供一種代理以控制對這個對象的訪問 (2)Proxy代理類:用來替代實際的網絡加載庫,避免界面代碼與實際的網絡加載直接交互 (3)RealSubject:真實請求類,在咱們的案例中,就是一種網絡加載庫。每添加一種網絡請求庫,即添加一個對應的真實請求類便可(這裏就是根據不一樣的網絡加載庫,實際進行請求功能的地方) (4)Subject:用來規範新添加的各類網絡加載庫以及代理類的功能使用。爲何要規範代理的功能?由於代理類,代理的是真實類的功能行爲,所以代理類須要與真實類的功能保持一致。json
// 代理模式中,用於規範代理類與真實類的功能接口
// 咱們暫且只定義了get和post功能
// ICallBack函數是自定義的請求回調類,後續介紹
interface IHttpProxy {
fun getHttp(url: String, callback: ICallBack)
fun postHttp(url: String, params: Map<String, Any>, callback: ICallBack)
}
複製代碼
// 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<String, Any>, callback: ICallBack)
{
// 運行時,調用真實對象方法
httpProxyImpl!!.postHttp(url,params,callback)
}
}
複製代碼
(1)不瞭解Kotlin語法的,請查看如下相關文章 【Kotlin_第一行代碼】 www.jianshu.com/nb/35111692 (2)代理類,實現了上述定義的接口,並實現了對應的功能,而且能夠看出,其功能都是直接調用真實類對象對應功能函數 (3)代理類必須持有真實類的引用,不然沒法實現對真實類的代理做用 (4)init方法表示的是傳入當前須要被代理的真實類對象設計模式
// 實現代理模式中的接口
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<String, Any>, callback: ICallBack) {
}
}
複製代碼
(1)不對OkHttp的使用作介紹 (2)該類是咱們使用OkHttp網絡加載庫實現的真實類。實現了對應的接口,並在對應的函數上,實現真實的網絡請求功能,並利用自定義的回調函數,將結果回調到使用的地方 (3)目前僅實現get函數的邏輯功能,post函數同理。api
// 最底層的回調類,String類型,表示網絡請求的返回的json,xml的格式文件,即網絡請求返回的第一手數據,未進行任何操做的數據
interface ICallBack {
fun onSuccess(result: String)
fun onFailure(result: String)
}
複製代碼
//基於ICallBack之上,再次封裝的抽象回調類,並對泛型進行處理
abstract class IHttpCallBack<T> : 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<*>
}
}
複製代碼
// 一個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<Author>() {
// 請求成功,返回的是已經通過泛型處理的回調類
// 由於傳入的回調類是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初始化時設置bash
class MyApp :Application() {
override fun onCreate() {
super.onCreate()
HttpHelper.init(OkHttpProxyImpl()) // 設置真實代理對象
}
}
複製代碼
(1)仿照OkHttpProxyImpl,實現對應網絡加載庫的真實類,如VolltyProxyImpl。網絡
// Volley網絡加載庫真實請求類
class VolleyProxyImpl :IHttpProxy {
override fun postHttp(url: String, params: Map<String, Any>, 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()) // 設置爲新網絡加載類對象
}
複製代碼
}
至此,咱們的網絡隔離庫框架雛形已搭建完畢,更多的功能請自定擴展。若有任何不正確地方,歡迎批評指正。 很是感謝【騰訊課堂-Android高級開發專題課】
【項目地址】:github.com/y0000c/Http…