GifFun源码学习:网络模块的分析

非 Y 不嫁゛ 提交于 2020-02-08 01:24:34

前言: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网络模块的代码梳理了一遍,如果还是不懂,建议对着手敲一遍,模仿其封装原理应用到自己的项目中。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!