Day 8:OkHttp 获取网路资料方式

本篇文章同步发表在 HKT 线上教室 部落格,线上影音教学课程已上架至 UdemyYoutube 频道。另外,想追踪更多相关技术资讯,欢迎到 脸书粉丝专页 按赞追踪喔~

程序码范例

范例名称:OkHttp 基本拉资料方式 (GET 请求范例)
开发人员:HKT (侯光灿)
程序语言:Kotlin
开发环境:Android Studio 4.1.1 & Android 11 & Kotlin 1.4.21
授权范围:使用时必须注明出处且不得为商业目的之使用
范例下载点:点我下载

在 Part 1 介绍当中,我们简单介绍完 Kotlin 基础语法与口罩开发背景与环境之後,接下来几天,将要来跟大家介绍网路资料处理的部分,如何将政府公开的口罩库存资料,从云端服务器,下载到本地手机装置上。然後进一步去了解,资料下载回来,口罩 Json 资料格式,如何去做使用与处理,大致上规划,如下:

  • Day 8:OkHttp 获取网路资料方式
  • Day 9:JSON 资料解析
  • Day 10:Gson 资料解析
  • Day 11:ProgressBar 忙碌圈圈
  • Day 12:封装 OkHttp

Android 业界常用获取网路资料方式,有 OkHttp、Retrofit、Volley 和 HttpURLConnection 四种连线方式。这四种写法其实很类似,大同小异,所以今天 KT 就选择业界最多人使用的 OkHttp 连线方式来跟大家介绍,想看其他使用方式,可以参考:

Android Retrofit 教学 (Java 篇)
https://tw-hkt.blogspot.com/2020/03/retrofit-java.html

Android Volley 教学 (Kotlin 篇)
https://tw-hkt.blogspot.com/2020/07/android-volley-kotlin.html

Fuel (另外推荐,很简洁的连线方式)
https://github.com/kittinunf/fuel

添加 OkHttp 依赖库 (dependencies)

要使用 OkHttp,必须额外宣告,在 GRADLE (Module) 层级的 dependencies 中内加入:

implementation 'com.squareup.okhttp3:okhttp:4.9.0'

若想使用最新版本可以到 MVN REPOSITORY 查看最新版号:

目前 Android Studio 很强大,若有新版本,会在你宣告的当下,该条项目反灰呈现,鼠标移动过去,即可以立即查看到当下最新版号。

权限宣告 (permissions)

连线网路,需在 AndroidManifest.xml 中宣告:

<uses-permission android:name="android.permission.INTERNET" />

OkHttp 使用介绍

OkHttp 主要可以分成三个部分:

Part 1. OkHttpClient

设定连线基底(SSL、TLS、连线凭证)。

Part 2. Request

设定 URL 连线网址、请求方式(GET、POST、PUT和DELETE方法)、Header 资讯

Part 3. Call

设定 execute 同步(Synchronous)或 enqueue 非同步(Asynchronous),执行连线後,可获取到回应的结果资料。

OkHttp 基本拉资料方式 (GET 请求范例)

private fun getPharmacyData() {
    //口罩资料网址
    val pharmaciesDataUrl =
            "https://raw.githubusercontent.com/thishkt/pharmacies/master/data/info.json"

    //Part 1: 宣告 OkHttpClient
    val okHttpClient = OkHttpClient().newBuilder().build()

    //Part 2: 宣告 Request,要求要连到指定网址
    val request: Request = Request.Builder().url(pharmaciesDataUrl).get().build()

    //Part 3: 宣告 Call
    val call = okHttpClient.newCall(request)

    //执行 Call 连线後,采用 enqueue 非同步方式,获取到回应的结果资料
    call.enqueue(object : Callback {
        override fun onFailure(call: Call, e: java.io.IOException) {
            Log.d("HKT", "onFailure: $e")
        }

        override fun onResponse(call: Call, response: Response) {
            Log.d("HKT", "onResponse: ${response.body?.string()}")
        }
    })
}

输出结果

{
  "type": "FeatureCollection",
  "features": [
   ...
   ...
   ...,{
            "type": "Feature",
            "properties": {
                "id": "5901024427",
                "name": "博昱仁爱药局",
                "phone": "(02)87739258",
                "address": "台北市大安区仁爱路4段65号",
                "mask_adult": 0,
                "mask_child": 450,
                "updated": "2020\/09\/13 11:32:37",
                "available": "星期一上午看诊、星期二上午看诊、星期三上午看诊、星期四上午看诊、星期五上午看诊、星期六上午看诊、星期日上午看诊、星期一下午看诊、星期二下午看诊、星期三下午看诊、星期四下午看诊、星期五下午看诊、星期六下午看诊、星期日下午看诊、星期一晚上看诊、星期二晚上看诊、星期三晚上看诊、星期四晚上看诊、星期五晚上看诊、星期六晚上看诊、星期日晚上看诊",
                "note": "周间(周一至周五)上午9点发放号码牌收取健保卡,下午2点领取",
                "custom_note": "",
                "website": "",
                "county": "台北市",
                "town": "大安区",
                "cunli": "仁爱里",
                "service_periods": "NNNNNNNNNNNNNNNNNNNNN"
            },
            "geometry": {
                "type": "Point",
                "coordinates": [
                    121.546869,
                    25.038194
                ]
            }
        },
    ...
    ...
    ...
    
  ]
}

POST 请求范例

口罩资料,没有 POST 功能,这里 KT 另外选用 Reqres 网站,来练习 Post 如何使用。Reqres 是一个提供假资料 API 串接的网站,方便练习 RESTful API。

val testUrl =
    "https://reqres.in/api/users"

//Part 1: 宣告 OkHttpClient
val okHttpClient = OkHttpClient().newBuilder().build()

//加入 FormBody 参数 name 和 job 。
val formBody: FormBody = FormBody.Builder()
    .add("name", "HKT")
    .add("job", "Teacher")
    .build()
    
//Part 2: 宣告 Request,要求要连到指定网址
val request: Request = Request.Builder().url(testUrl).post(formBody).build()

//Part 3: 宣告 Call
val call = okHttpClient.newCall(request)

//执行 Call 连线後,采用 enqueue 非同步方式,获取到回应的结果资料
call.enqueue(object : Callback {
    override fun onFailure(call: Call, e: IOException) {
        e.printStackTrace()
    }

    override fun onResponse(call: Call, response: Response) {
        Log.d("QQQ", "response: ${response.body?.string()}")
    }
})

输出结果

{
  "name": "HKTTTT",
  "job": "Teacher",
  "id": "457",
  "createdAt": "2020-09-15T09:10:37.252Z"
}

Log 日志连线资讯

OkHttp 提供的 HttpLoggingInterceptor,加入後,Log 会自动印出连线的网址、回应状态码和连线回应的花费的时间。

需额外添加 interceptor 依赖库 (dependencies),在 GRADLE (Module) 层级的 dependencies 中内加入:

implementation 'com.squareup.okhttp3:logging-interceptor:4.9.0'

interceptor 版号要跟 okhttp 匹配,否则会发生不可预期的错误

在 okHttpClient 加入连线拦截资讯

private val okHttpClient = OkHttpClient().newBuilder().addInterceptor(
    HttpLoggingInterceptor().setLevel(
        if (BuildConfig.DEBUG) {
            HttpLoggingInterceptor.Level.BASIC
        } else {
            HttpLoggingInterceptor.Level.NONE
        }
    )
).build()

OkHttp 官方介绍与使用教学

OkHttp 更多详细使用方式,可以参考官网介绍:https://square.github.io/okhttp/

常见错误

Q1

Trying to parse JSON data from URL : Unable to resolve host “jsonplaceholder.typicode.com”: No address associated with hostname

解答:
若发生以上类似错误,有可能你忘记在 AndroidManifest 宣告,使用网路权限,另外一个就是你的网路不通。

Q2

okhttp3.internal.http.RealResponseBody@2f58b42

解答:
若印出来的结果资料,类似上方文字,需改取出 body 内容资料,并加上 ? 问号

${response.body?.string()}

Q3

E/AndroidRuntime: FATAL EXCEPTION: OkHttp Dispatcher
Process: com.example.test, PID: 20263
java.lang.NoSuchMethodError: No virtual method log(ILjava/lang/String;Ljava/lang/Throwable;)V in class Lokhttp3/internal/platform/Platform; or its super classes (declaration of 'okhttp3.internal.platform.Platform' appears in /data/app/com.example.test-x8l0H_ZbvI-Dn6Lugsdl6Q==/base.apk)
at okhttp3.logging.HttpLoggingInterceptor$Logger$1.log(HttpLoggingInterceptor.java:110)
at okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.java:160)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201)
at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:517)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)

解答:
okhttp 与 interceptor 依赖版本(dependencies) 两个版号不一致所导致。例如 4.9.0,两者的版号,应该都是 4.9.0,否则会发生类似以上错误。

Q4

FATAL EXCEPTION: OkHttp Dispatcher

解答:
Response 只能用一次,你可能使用了两次。应该将资料存到变数中,再去做其他使用。更进一步解释,可参考 OkHttp 官方文件说明:ResponseBody

参考资料

HKT 线上教室
https://tw-hkt.blogspot.com/

Freepik
https://www.freepik.com/

OkHttp
https://square.github.io/okhttp/


那今天【iThome 铁人赛】就介绍到这边罗~

顺带一提,KT 线上教室,脸书粉丝团,会不定期发布相关资讯,不想错过最新资讯,不要忘记来按赞,追踪喔!也欢迎大家将这篇文章分享给更多人喔。

我们明天再见罗!!!掰掰~


<<:  Day 08 Azure cognitive service: object detection- 匡出照片中的喵

>>:  2021-Day3. Postman 实作(一):Line Richmenu API

【Day17】数据展示元件 - Infinite scroll

元件介绍 Infinite scroll 能在面对多笔资料时,让卷轴滑动到底部时再载入下一页面的资料...

Day27 Gin with Colly

What is Colly? Colly是一种Golang的网路爬虫工具,而网路爬虫Web Craw...

Day19:链接串列(Linked List)

链接串列(Linked List) 链接串列是一种线性表,使用Pointer串接资料,好处是找到目标...

如何共用行事历?

公司使用Office365商业基本版 没有自建邮件服务器,是使用中华电信的hibox挂在Outloo...

Day 8. 我在解VR Simulator的Bug的途中,常看到叫人改Active Input Handling的设定

昨天很开心的给大家看我可以用键盘和滑鼠移动视角跟位置就结束了呢,今天来稍微讲讲我的心得吧(?。   ...