Kotlin 封装OKHttp请求

现在越来越多的朋友使用OKHttp作为网络请求框架,我自己也不例外。那么在Kotlin中具体是怎么实现的呢?下面介绍下对其的封装。

封装OKHttp请求

因为项目中使用到OKHttp3,在这个周末,抽点时间使用Kotlin封装了一下,主要是使用Kotlin语法进行简化。

Gradle添加相关依赖包:

compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
compile 'com.squareup.okhttp3:okhttp:3.8.1'
compile 'com.squareup.okio:okio:1.13.0'

先定义一下自己的Response,内部可以做一些自己的处理,也可以直接使用OKHttp的Response。

/**
 * Created by ngudream 2017-06-20
 */
@Keep //不混淆此类
open class AccountResponse {

    var statusCode: Int = 0
        private set
    private var responseAsString: String? = null
    private var responseAsBytes: ByteArray? = null
    private var headers: MutableMap<String, MutableList<String>>? = null

    /**
     * 创建获取InputStream中数据的结果结构。
     * <br></br>注意,这个构造方法会直接从输入流中读取数据并且关闭输入流,后续不能再对输入流进行操作(同时,输入流可能在其他地方被关闭,所以不要在构造方法之外操作它)。
     */
    @Throws(IOException::class)
    constructor(statusCode: Int, datas: ByteArray?, headers: MutableMap<String, MutableList<String>>?) {
        this.statusCode = statusCode
        // 在这里获取stream的数据,因为该方法之后stream会close掉
        this.responseAsBytes = datas
        this.headers = headers
    }

    constructor(content: String, responseCode: Int) {
        responseAsString = content
        statusCode = responseCode
    }

    fun getResponseAsString(): String? {
        responseAsString = responseAsBytes?.toString(Charset.forName("utf-8"))
        return responseAsString
    }

    fun getHeadersByName(name: String): MutableList<String>? {
        if (headers != null) {
            return headers?.get(name)
        }
        return null
    }

    override fun toString(): String {
        if (null != getResponseAsString()) {
            return responseAsString?: ""
        }
        return "Response{" +
                "statusCode=" + statusCode +
                ", responseString='" + responseAsString +
                '}'
    }
}

接下来我们使用Kotlin进行OKHttp网络请求的封装,主要是使用companion object创建单例,这样可以复用OkHttpClient的实例

/**
 * Created by ngudream 2017-06-20
 * 封装okhttp请求
 */
class AccountHttpClient private constructor() {

    private val TAG = "AccountHttpClient"

    /**
     * 只使用一个实例的,以便重用response cache、thread pool、connection re-use 等
     */
    private val mOkHttpCilent: OkHttpClient

    private object Holder { val INSTANCE = AccountHttpClient()}

    companion object {//构造单例
        private var CONNECTION_TIME_OUT = 30 * 1000
        private var READ_TIME_OUT = 30 * 1000
        private var WRITE_TIME_OUT = 30 * 1000
        val  instance: AccountHttpClient by lazy { Holder.INSTANCE }
    }

    //由于primary constructor不能包含任何代码,因此使用 init 代码块对其初始化,同时可以在初始化代码块中使用构造函数的参数
    init {
        mOkHttpCilent = OkHttpClient.Builder()
                .connectTimeout(CONNECTION_TIME_OUT.toLong(), TimeUnit.SECONDS)//注意显示转化toLong
                .readTimeout(READ_TIME_OUT.toLong(), TimeUnit.SECONDS)
                .writeTimeout(WRITE_TIME_OUT.toLong(), TimeUnit.SECONDS)
                .hostnameVerifier(AccountHostnameVerifier())
                .build()
    }

    /**
     * 发起get请求
     */
    @Throws(Exception::class)
    operator fun get(url: String, requestParams: List<Pair<String, String>>,
                     requestHeaders: List<Pair<String, String>>): AccountResponse? {
        return execute(Type.GET, url, requestParams, requestHeaders)
    }

    /**
     * 发起post请求
     */
    @Throws(Exception::class)
    fun post(url: String, requestParams: List<Pair<String, String>>?,
             requestHeaders: List<Pair<String, String>>?): AccountResponse? {
        return execute(Type.POST, url, requestParams, requestHeaders)
    }

    /**
     * 上传文件
     */
    @Throws(Exception::class)
    fun uploadFile(url: String, requestParams: List<Pair<String, String>>,
                   requestHeaders: List<Pair<String, String>>, filePath: String): AccountResponse? {
        return execute(url, requestParams, requestHeaders, filePath)
    }

    /**
     * 下载文件
     */
    @Throws(Exception::class)
    fun downloadFile(url: String, requestParams: List<Pair<String, String>>,
                     requestHeaders: List<Pair<String, String>>): AccountResponse? {
        return execute(Type.POST, url, requestParams, requestHeaders)
    }

    /**
     * 执行请求
     */
    @Throws(Exception::class)
    private fun execute(type: Type, url: String, requestParams: List<Pair<String, String>>?,
                        requestHeaders: List<Pair<String, String>>?): AccountResponse? {
        var result: AccountResponse? = null
        try {
            val okResponse = performRequest(OKRequest(type, url, requestParams, requestHeaders))//进行参数封装,发起网络请求
            if (okResponse != null) {
                result = AccountResponse(okResponse.responseCode, okResponse.body, okResponse.headers)
            }
        } catch (e: Exception) {
            e.printStackTrace()
            throw e
        }

        return result
    }

    @Throws(Exception::class)
    private fun execute(url: String, requestParams: List<Pair<String, String>>,
                        requestHeaders: List<Pair<String, String>>, filePath: String): AccountResponse? {
        var result: AccountResponse? = null
        try {
            val okResponse = performRequest(OKRequest(Type.POST, url, requestParams, requestHeaders, filePath))
            if (okResponse != null) {
                result = AccountResponse(okResponse.responseCode, okResponse.body, okResponse.headers)
            }
        } catch (e: Exception) {
            throw e
        }

        return result
    }

    internal enum class Type {
        GET,
        POST
    }

    /**
     * 请求参数类
     */
    private inner class OKRequest {
        private var mType: Type? = null
        var mUrl: String? = null
            private set
        private var mRequestParams: List<Pair<String, String>>? = null
        private var mRequestHeaders: List<Pair<String, String>>? = null
        private var mFilePath: String? = null

        constructor(type: Type, url: String,
                    requestParams: List<Pair<String, String>>?,
                    requestHeaders: List<Pair<String, String>>?) {
            mType = type
            mUrl = url
            mRequestParams = requestParams
            mRequestHeaders = requestHeaders
        }

        constructor(type: Type, url: String,
                    requestParams: List<Pair<String, String>>,
                    requestHeaders: List<Pair<String, String>>, filePath: String) {
            mType = type
            mUrl = url
            mRequestParams = requestParams
            mRequestHeaders = requestHeaders
            mFilePath = filePath
        }

        fun getDataRequest(): Request{//此处可以改成request的get()方法
            val request: Request
            val builder = Request.Builder()
            builder.url(mUrl!!)
            builder.header("Connection", "Close")
            if (mRequestHeaders?.isNotEmpty() ?: false) {
                for (header in mRequestHeaders!!) {
                    builder.addHeader(header.first, header.second)
                }
            }
            if (mType == Type.GET) {
                builder.get()
            } else {
                if (mFilePath?.isNotEmpty() ?: false) {//设置上传文件
                    val bodyBuilder = MultipartBody.Builder()
                            .setType(MultipartBody.FORM)
                    val file = File(mFilePath)
                    if (file.exists()) {
                        bodyBuilder.addFormDataPart("file", file.name, RequestBody.create(null, file))
                    }
                    mRequestParams?.let {
                        for (param in it) {
                            bodyBuilder.addFormDataPart(param.first, param.second)
                        }
                    }
                    builder.post(bodyBuilder.build())
                } else {//设置参数
                    mRequestParams?.let {
                        val formBuilder = FormBody.Builder()
                        for (param in it) {
                            formBuilder.add(param.first, param.second)
                        }
                        builder.post(formBuilder.build())
                    }
                }
            }
            request = builder.build()
            return request
        }
    }

    /**
     * 响应类
     */
    private inner class OKResponse(private val response: Response?) {

        val responseCode: Int
            get() = mResponse!!.code()

        val body: ByteArray?
            get() {
                var result: ByteArray? = null
                if (response != null && response.body() != null) {
                    try {
                        result = mResponse.body()!!.bytes()
                    } catch (e: IOException) {
                        Log.e(TAG, e.message)
                    }
                    headers
                }
                return result
            }

        val headers: MutableMap<String, MutableList<String>>?
            get() {
                var headers: MutableMap<String, MutableList<String>>? = null
                if (response != null && response.headers() != null) {
                    try {
                        headers = response.headers().toMultimap()
                    } catch (e: Exception) {
                    }

                }
                return headers
            }
    }

    @Throws(IOException::class)
    private fun performRequest(request: OKRequest): OKResponse? {
        val call = mOkHttpCilent.newCall(request.getDataRequest())//new一个call,通过okhttp发起请求
        return OKResponse(call.execute())
    }

    /**
     * 证书验证
     */
    private inner class AccountHostnameVerifier : HostnameVerifier {

        override fun verify(hostname: String, session: SSLSession): Boolean {
            var result = false
            try {
                val certs = session.peerCertificates as Array<X509Certificate>
                if (certs.isNotEmpty()) {
                    for (i in certs.indices) {//把证书取出,一个一个验证
                        result = OkHostnameVerifier.INSTANCE.verify(hostname, certs[i])
                        if (result) {
                            break
                        }
                    }
                } else {
                    result = true
                }
            } catch (e: SSLPeerUnverifiedException) {
                e.printStackTrace()
            }

            return result
        }
    }
}

外调用就比较简单了,直接使用单例调用

Thread(Runnable {
                    var response: AccountResponse? = AccountHttpClient.instance.post("http://www.baidu.com", null, null)
                    val result: String? = response?.getResponseAsString()
                    runOnUiThread {
                        Toast.makeText(this, result, Toast.LENGTH_SHORT).show()
                    }
                }).start()
文章目录
  1. 1. 封装OKHttp请求
|