从零开始学安卓(kotlin)九——网络技术

WebView的用法

使用HTTP访问网络

HttpURLConnection

官方建议使用的HttpURLConnection的用法
首先需要获取HttpURLConnection的实例,一般只需创建一个URL对象,并传入目标的网络地 址,然后调用一下openConnection()方法即可,如下所示:

val url = URL("https://www.baidu.com")
val connection = url.openConnection() as HttpURLConnection
//在得到了HttpURLConnection的实例之后,我们可以设置一下HTTP请求所使用的方法。常用 的方法主要有两个:GET和POST。GET表示希望从服务器那里获取数据,而POST则表示希望提 交数据给服务器。
connection.requestMethod = "GET"
//接下来就可以进行一些自由的定制了,比如设置连接超时、读取超时的毫秒数,以及服务器希望得到的一些消息头等。这部分内容根据自己的实际情况进行编写
connection.connectTimeout = 8000
connection.readTimeout = 800
//之后再调用getInputStream()方法就可以获取到服务器返回的输入流了,剩下的任务就是对 输入流进行读取
val input = connection.inputStream
//最后可以调用disconnect()方法将这个HTTP连接关闭
connection.disconnect()

如果想要提交数据给服务器应该怎么办呢?其实也不复杂,只需要将HTTP请求的方法改成 POST,并在获取输入流之前把要提交的数据写出即可。注意,每条数据都要以键值对的形式存 在,数据与数据之间用“&”符号隔开。比如说我们想要向服务器提交用户名和密码,就可以这样写:

connection.requestMethod = "POST"
val output = DataOutputStream(connection.outputStream)
output.writeBytes("username=admin&password=123456")

OkHttp

有许多出色的网络通信库都可以替代原生的HttpURLConnection,而其中OkHttp无疑 是做得最出色的一个。
OkHttp的具体用法:

//首先需要创建一个OkHttpClient的实例
val client = OkHttpClient()
//发起一条HTTP请求,就需要创建一个Request对象
//val request = Request.Builder().build()
//上述代码只是创建了一个空的Request对象,并没有什么实际作用,我们可以在最终的 build()方法之前连缀很多其他方法来丰富这个Request对象。比如可以通过url()方法来设 置目标的网络地址
val request = Request.Builder()
    	.url("https://www.baidu.com")
		.build()
//之后调用OkHttpClient的newCall()方法来创建一个Call对象,并调用它的execute()方法 来发送请求并获取服务器返回的数据
val response = client.newCall(request).execute()
//Response对象就是服务器返回的数据了,我们可以使用如下写法来得到返回的具体内容
val responseData = response.body?.string()

如果是发起一条POST请求,会比GET请求稍微复杂一点,我们需要先构建一个Request Body 对象来存放待提交的参数

val requestBody = FormBody.Builder()
        .add("username", "admin")
        .add("password", "123456")
        .build()
//然后在Request.Builder中调用一下post()方法,并将RequestBody对象传入
val request = Request.Builder()
        .url("https://www.baidu.com")
        .post(requestBody)
        .build()
//接下来的操作就和GET请求一样了,调用execute()方法来发送请求并获取服务器返回的数据 即可。

网络请求回调的实现方式

 
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()
        } finally {
            connection?.disconnect()
        }
} }

以后每当需要发起一条HTTP请求的时候,就可以这样写

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

需要使用编程语言的回调机制
首先需要定义一个接口,比如将它命名成HttpCallbackListener

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

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的实例传入

HttpUtil.sendHttpRequest(address, object : HttpCallbackListener {
    override fun onFinish(response: String) {
// 得到服务器返回的具体内容 
}
override fun onError(e: Exception) { // 在这里对异常情况进行处理
} })

在HttpUtil 中加入一个sendOkHttpRequest()方法
OkHttp在 enqueue()方法的内部已经帮我们开好子线程了,然后会在子线程中执行HTTP请求,并将最 终的请求结果回调到okhttp3.Callback当中。

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

调用sendOkHttpRequest()方法的时候就可以这样写

HttpUtil.sendOkHttpRequest(address, object : Callback {
    override fun onResponse(call: Call, response: Response) {
// 得到服务器返回的具体内容
        val responseData = response.body?.string()
    }
override fun onFailure(call: Call, e: IOException) { // 在这里对异常情况进行处理
} })

不管是使用HttpURLConnection还是OkHttp,最终的回调接口都还是 在子线程中运行的,因此我们不可以在这里执行任何的UI操作,除非借助runOnUiThread()

最好用的网络库:Retrofit

Retrofit就是Square 公司在OkHttp的基础上进一步开发出来的应用层网络通信库,使得我们可以用更加面向对象的 思维进行网络操作.

  • Retrofit是基于OkHttp开发的,因此添加上述第一条依赖会自动将Retrofit、OkHttp和 Okio这几个库一起下载,我们无须再手动引入OkHttp库。
  • Retrofit还会将服务器返回的 JSON数据自动解析成对象,所以会自动将GSON库一起下载下来,这样我们也不用手动引入GSON库 了.
  • 通常Retrofit的接口文件建议以具体的功能种类名开头,并以Service结尾,这是一种比较好的 命名习惯。
val retrofit = Retrofit.Builder()
                .baseUrl("http://10.0.2.2/")
                .addConverterFactory(GsonConverterFactory.create())
                .build()
            val appService = retrofit.create(AppService::class.java)
            appService.getAppData().enqueue(object : Callback<List<App>> {
                override fun onResponse(call: Call<List<App>>,
                    response: Response<List<App>>) {
                    val list = response.body()
                    if (list != null) {
                        for (app in list) {
                            Log.d("MainActivity", "id is ${app.id}")
                            Log.d("MainActivity", "name is ${app.name}")
                            Log.d("MainActivity", "version is ${app.version}")
} }
}
                override fun onFailure(call: Call<List<App>>, t: Throwable) {
                    t.printStackTrace()
} })

在“Get App Data”按钮的点击事件当中,首先使用了Retrofit.Builder来构建 一个Retrofit对象,其中baseUrl()方法用于指定所有Retrofit请求的根路径, addConverterFactory()方法用于指定Retrofit在解析数据时所使用的转换库,这里指定成 GsonConverterFactory。注意这两个方法都是必须调用的。

当调用了AppService的getAppData()方法时,会返回一个 Call<List>对象,这时我们再调用一下它的enqueue()方法,Retrofit就会根据注解 中配置的服务器接口地址去进行网络请求了,服务器响应的数据会回调到enqueue()方法中传 入的Callback实现里面。需要注意的是,当发起请求的时候,Retrofit会自动在内部开启子线 程,当数据回调到Callback中之后,Retrofit又会自动切换回主线程,整个操作过程中我们都 不用考虑线程切换问题。在Callback的onResponse()方法中,调用response.body()方 法将会得到Retrofit解析后的对象,也就是List类型的数据,最后遍历List,将其中的数 据打印出来即可。

处理复杂的接口地址类型

这里我们在createData()方法中声明了一个Data类型的参数,并给它加上了 @Body注解。这样当Retrofit发出POST请求时,就会自动将Data对象中的数据转换成JSON格 式的文本,并放到HTTP请求的body部分,服务器在收到请求之后只需要从body中将这部分数 据解析出来即可。这种写法同样也可以用来给PUT、PATCH、DELETE类型的请求提交数据。

interface ExampleService {
    @POST("data/create")
    fun createData(@Body data: Data): Call<ResponseBody>
}

Retrofit构建器的最佳写法

新建一个ServiceCreator单例类
使用object关键字让ServiceCreator成为了一个单例类,并在它的内部定义了一 个BASE_URL常量,用于指定Retrofit的根路径。然后同样是在内部使用Retrofit.Builder 构建一个Retrofit对象,注意这些都是用private修饰符来声明的,相当于对于外部而言它们都 是不可见的。
最后,我们提供了一个外部可见的create()方法,并接收一个Class类型的参数。当在外部调 用这个方法时,实际上就是调用了Retrofit对象的create()方法,从而创建出相应Service接 口的动态代理对象。

object ServiceCreator {
    private const val BASE_URL = "http://10.0.2.2/"
    private val retrofit = Retrofit.Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .build()
    inline fun <reified T> create(): T = create(T::class.java)
}

经过这样的封装之后,Retrofit的用法将会变得异常简单,比如我们想获取一个AppService接 口的动态代理对象,只需要使用如下写法即可:

val appService = ServiceCreator.create<AppService>()
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值