1. 程式人生 > 其它 >學習Android之網路請求會回撥

學習Android之網路請求會回撥

網路請求回撥的實現方式

  雖然掌握了HttpURLConnection和OkHttp的用法,但是傳送HTTP請求的程式碼基本是相同的,所以我們需要將這些通用的網路操作提取到一個公共的類裡,並提供一個通用方法:

HttpURLConnection的寫法

object HttpUtil {
    fun sendHttpRequest(address: String): String {
        var connection: HttpURLConnection? = null
        try {
            val response = StringBuilder()
            val url 
= URL(address) connection = url.openConnection() as HttpURLConnection connection.connectTimeout = 8000 connection.readTimeout = 8000 val input = connection.inputStream val reader = BufferedReader(InputStreamReader(input)) reader.use { reader.forEachLine { response.append(it) } }
return response.toString() } catch (e: Exception) { e.printStackTrace() return e.message.toString() } } }

  以後每當需要發起一條HTTP請求,就這樣寫:

        val address = "https://www.baidu.com"
        val response = HttpUtil.sendHttpRequest(address)

  在獲取伺服器相應資料後,就可以進行解析和處理了。

  注意:網路請求屬於耗時操作,而sendHttpRequest()方法中並沒有開啟執行緒,如果我們在其內部開啟執行緒,那麼伺服器相應的資料是無法進行返回的。為什麼呢?

  因為所有的耗時邏輯都是在子執行緒裡進行的,sendHttpRequest()方法會在伺服器還沒來得及相應的時候就執行結束了,所以就無法返回響應的資料了。

  這個時候我們就需要使用回撥機制了,首先定義一個介面:

interface HttpCallbackListener {
    fun onFinish(response: String)
    fun onError(e: Exception)
}

  介面中有兩個方法:onFinish()方法表示當伺服器成功響應我們請求的時候呼叫,onError()表示當進行網路操作出現錯誤的時候呼叫。

  接著修改HttpUtil中的程式碼:

object HttpUtil {
    fun sendHttpRequest(address: String, listener: HttpCallbackListener){
        thread {
            var connection: HttpURLConnection? = null
            try {
                val response = StringBuilder()
                val url = URL(address)
                connection = url.openConnection() as HttpURLConnection
                connection.connectTimeout = 8000
                connection.readTimeout = 8000
                val input = connection.inputStream
                val reader = BufferedReader(InputStreamReader(input))
                reader.use {
                    reader.forEachLine {
                        response.append(it)
                    }
                }
                //  回撥onFinish()方法
                listener.onFinish(response.toString())
            } catch (e: Exception) {
                e.printStackTrace()
                //  回撥onError()方法
                listener.onError(e)
            } finally {
                connection?.disconnect()
            }
        }
    }
}

 

  首先給sendHttpRequest()方法添加了一個HttpCallbackListener引數,並在內部開啟一個子執行緒,然後在子執行緒裡執行具體的網路操作。

  注意:子執行緒是無法通過return語句返回資料的,因此這裡將伺服器響應的資料傳入了HttpCallbackListener的onFinish()方法中,如果出現了異常,就將異常原因傳入onError()方法中。

  現在sendHttpRequest()方法接收兩個引數,所以在呼叫的時候需要將HttpCallbackListener的例項傳入:

        HttpUtil.sendHttpRequest(address, object : HttpCallbackListener {
            override fun onFinish(response: String) {
                //  得到伺服器返回的具體內容
            }

            override fun onError(e: java.lang.Exception) {
                //  在這裡對異常情況進行處理
            }
        })

 

  如此一來,我們就巧妙地利用回撥機制將響應資料成功返回給呼叫方了。

 

OkHttp的寫法

  但是上述使用HttpURLConnection的寫法還是比較複雜的,現在換成OkHttp:

object HttpUtil {   
    fun sendOkHttpRequest(address: String, callback: okhttp3.Callback) {
        val client = OkHttpClient()
        val request = Request.Builder()
            .url(address)
            .build()
        client.newCall(request).enqueue(callback)
    }
}

 

  此方法中有一個okhttp3.Callback引數,這是OkHttp庫中自帶的回撥介面,類似於我們剛才編寫的HttpCallbackListener。在client.newCall()之後沒有呼叫execute()方法,而是呼叫了enqueue()方法,並把okhttp3.Callback引數傳入。因為OkHttp在enqueue()方法的內部已經開好了子執行緒,然後會在子執行緒中執行HTTP請求,並將最終的請求結果回撥到okhttp3.Callback中。

  那麼呼叫sendOkHttpRequest()方法就可以這樣寫:

        HttpUtil.sendOkHttpRequest(address, object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                // 得到伺服器返回的具體內容
            }

            override fun onResponse(call: Call, response: Response) {
                //  對異常進行處理
            }
        })

 

  注意:不管是使用HttpURLConnection還是OkHttp,最終的回撥介面都還是在子執行緒中執行的,因此我們不可以在這裡執行任何的UI操作,除非藉助runOnUiThread()方法來進行執行緒轉換。