学习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()方法来进行线程转换。

 

posted @ 2022-03-15 16:54  PeacefulGemini  阅读(505)  评论(0)    收藏  举报
回顶部