前言:GifFun是第一行代码作者郭霖大神的开源作品GifFun地址,对于像作者这样的应届生来说,寻找工作的痛点是技术门槛和项目经验,由于新手的技术限制与见识广度,显然GifFun提供了一个学习的机会。在刚开始看的时候,笔者是一头雾水看不懂,学习 Kotlin后还是不懂,直到我对着代码试着重头敲一下,回头一看,便豁然开朗。
因此,学习项目最好的方法是自己敲一遍。
废话不多说,首先我们导入GifFun进Studio中,可以看到GifFun分为了4个模块,其中network显然是网络模块,网络模块下分4个包,exception显然是网络异常包,util包显然是工具包,model包为Json转换模型包,request包为网络请求包。
第一次看到这个结构的时候就跟我第一次看Android的目录结构一样。网络的第一步就是连接,所以首先要从Request入手,可以看到request包下的文件都是继承自Request文件,因此要进入Request文件查看,在进入该文件前,首先需要回顾下okhttp的完整用法:
val formbody = FormBody.Builder()
.add("account", "admin")
.add("password", "123456")
.build(); //1
val request = Request.Builder()
.addHeader("cookie", "session=123")
.url("http://hello.world")
.post(formbody)
.build(); //2
val client = OkHttpClient() //3
client.newCall(request).enqueue(callback) //4
显然这是一个POST请求,我将其分为了4个部分,同样,Request文件我也将其分为对应的4个部分阅读。
第一部分,FormBody的组装
可以看到,Request文件下的formBody方法,具体代码如下:
/**
* 构建POST、PUT、DELETE请求的参数体。
*
* @return 组装参数后的FormBody。
*/
private fun formBody(): FormBody {
val builder = FormBody.Builder()
val params = getParams()
if (params != null) {
val keys = params.keys
if (!keys.isEmpty()) {
for (key in keys) {
val value = params[key]
if (value != null) {
builder.add(key, value)
}
}
}
}
return builder.build()
}
可以看到,FormBody的组装是通过getParams方法获取对应的请求键和参数,而getParams方法是通过params方法来进行,params’的方法如下:
open fun params(): Map<String, String>? {
return null
}
连起来看,那么FormBody的组装就清楚了,params方法获取请求的对应Map集合,通过frombody方法进行遍历组装
第二部分,Request的组装
第一步是headers的组装,可以看到Request文件下的headers的组装
open fun headers(builder: Headers.Builder): Headers.Builder {
builder.add("hello", "world")
//...
return builder
}
接着查看inFight方法,部分代码如下:
val requestBuilder = okhttp3.Request.Builder()
if (method() == GET && getParams() != null) {
requestBuilder.url(urlWithParam())
} else {
requestBuilder.url(url())
}
requestBuilder.headers(headers(Headers.Builder()).build())
when {
method() == POST -> requestBuilder.post(formBody())
method() == PUT -> requestBuilder.put(formBody())
method() == DELETE -> requestBuilder.delete(formBody())
}
很显然,这部分代码分为3个部分查看,第一是url的处理,第二是Headers的处理,最后是对网络请求的处理。其中Headers的处理上边已经讲了,url的处理在代码上可以看到分为2种,urlWithParam中是url方法对params的组装(params见第一部分解说),处理的url是后端地址的一种请求格式,如下所示
?account=hello&password=world
第二种就简单了,直接通过url方法获取,查看url方法的代码,如下所示:
abstract fun url(): String
最后就是对网络请求的处理了,通过method方法进行判断进行formbody的处理。method方法的代码如下:
abstract fun method(): Int
第三部分,okhttp的连接设置
这一部分是对连接的一些设置,没什么好讲的,看代码就行。
init {
connectTimeout(10)
writeTimeout(10)
readTimeout(10)
...
}
第四部分,OkHttp连接结果的处理
众所周知,OkHttp连接后的返回的原封不动的内容,而实际我们需要的是Json转换后的实体类,查看inFight方法的参数,如下所示:
fun <T : com.quxianggif.network.model.Response> inFlight(requestModel: Class<T>)
进入model包下的Response文件查看:
open class Response {
/**
* 请求结果的状态码,这里可以查看所有状态码的含义:https://github.com/sharefunworks/giffun-server#2-状态码
*/
var status: Int = 0
/**
* 请求结果的简单描述。
*/
var msg: String = ""
}
显然GifFun中返回的Json有2个基础参数,回到inFlight方法,可以看到以下代码:
override fun onResponse(call: Call, response: Response) {
try {
if (response.isSuccessful) {
val body = response.body()
val result = if (body != null) {
body.string()
} else {
""
}
logVerbose(LoggingInterceptor.TAG, result)
val gson = GsonBuilder().disableHtmlEscaping().create()
val responseModel = gson.fromJson(result, requestModel)
response.close()
notifyResponse(responseModel)
} else {
notifyFailure(ResponseCodeException(response.code()))
}
} catch (e: Exception) {
notifyFailure(e)
}
显然,通过inFlight方法传入Json实体类的参数,通过接口封装的方法,最后直接返回Json转换好的实体类。
众所周知,OkHttp还有另外一个令人诟病的问题是连接结果并不在UI线程。如果进行UI操作会十分头疼,我们可以查看GitFun是怎么处理的,查看notifyResponse或notifyFailure方法,2个相差不大,以notifyResponse为例,代码如下:
private fun notifyResponse(response: com.quxianggif.network.model.Response) {
callback?.let {
if (it is OriginThreadCallback) {
it.onResponse(response)
callback = null
} else {
GifFun.getHandler().post {
it.onResponse(response)
callback = null
}
}
}
}
很显然这是Kotlin的内联函数,通过判断Callback进行相应的线程切换,可以看到是OriginThreadCallback则继续在子线程运行,不是则通过Handler切换到UI线程。最后将callback赋值为null释放内存。查看core模块GifFun.java文件的initialize方法可以看到handler的获取,代码如下:
public static void initialize(Context c) {
context = c;
handler = new Handler(Looper.getMainLooper());
refreshLoginState();
}
同时在Main模块下的GifFunApplication文件可以看到该方法的初始化。
到此我们就将GifFun的Request封装流程梳理了一遍,接着是Request封装的使用,我们可以随便查看一个继承自Request的文件查看,大致的都如下所示“
override fun url(): String {
return URL
}
override fun method(): Int {
return Request.POST
}
override fun listen(callback: Callback?) {
setListener(callback)
inFlight(FetchVCode::class.java)
}
override fun params(): Map<String, String>? {
val params = HashMap<String, String>()
params[NetworkConst.NUMBER] = number
params[NetworkConst.DEVICE_NAME] = deviceName
params[NetworkConst.DEVICE_SERIAL] = deviceSerial
return params
}
override fun headers(builder: Headers.Builder): Headers.Builder {
buildAuthHeaders(builder, NetworkConst.DEVICE_NAME, NetworkConst.NUMBER, NetworkConst.DEVICE_SERIAL)
return super.headers(builder)
}
其中,url方法,method方法,listen方法是要实现的,params方法要传入网络请求的表单参数,headers为对应的请求验证。在上边讲了Response是Json转换后的实体类,inFlight方法要传入Response类,同时Response类还有一个作用是暴露接口,我们随便进入一个Response类可以看到一致的内容:
companion object {
fun getResponse(number: String, callback: Callback) {
FetchVCodeRequest().number(number).listen(callback)
}
}
在Activity中,只需通过getResponse方法,在回调方法中进行对应处理即可。
到此就将GifFun网络模块的代码梳理了一遍,如果还是不懂,建议对着手敲一遍,模仿其封装原理应用到自己的项目中。
来源:CSDN
作者:miangezuishuai
链接:https://blog.csdn.net/miangezuishuai/article/details/104209085