本篇文章同步發表在 HKT 線上教室 部落格,線上影音教學課程已上架至 Udemy 和 Youtube 頻道。另外,想追蹤更多相關技術資訊,歡迎到 臉書粉絲專頁 按讚追蹤喔~
範例名稱:OkHttp 基本拉資料方式 (GET 請求範例)
開發人員:HKT (侯光燦)
程式語言:Kotlin
開發環境:Android Studio 4.1.1 & Android 11 & Kotlin 1.4.21
授權範圍:使用時必須註明出處且不得為商業目的之使用
範例下載點:點我下載
在 Part 1 介紹當中,我們簡單介紹完 Kotlin 基礎語法與口罩開發背景與環境之後,接下來幾天,將要來跟大家介紹網路資料處理的部分,如何將政府公開的口罩庫存資料,從雲端伺服器,下載到本地手機裝置上。然後進一步去了解,資料下載回來,口罩 Json 資料格式,如何去做使用與處理,大致上規劃,如下:
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,必須額外宣告,在 GRADLE (Module) 層級的 dependencies 中內加入:
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
若想使用最新版本可以到 MVN REPOSITORY 查看最新版號:
目前 Android Studio 很強大,若有新版本,會在你宣告的當下,該條項目反灰呈現,鼠標移動過去,即可以立即查看到當下最新版號。
連線網路,需在 AndroidManifest.xml 中宣告:
<uses-permission android:name="android.permission.INTERNET" />
OkHttp 主要可以分成三個部分:
設定連線基底(SSL、TLS、連線憑證)。
設定 URL 連線網址、請求方式(GET、POST、PUT和DELETE方法)、Header 資訊
設定 execute 同步(Synchronous)或 enqueue 非同步(Asynchronous),執行連線後,可獲取到回應的結果資料。
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 功能,這裡 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"
}
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 更多詳細使用方式,可以參考官網介紹:https://square.github.io/okhttp/
Trying to parse JSON data from URL : Unable to resolve host “jsonplaceholder.typicode.com”: No address associated with hostname
解答:
若發生以上類似錯誤,有可能你忘記在 AndroidManifest 宣告,使用網路權限,另外一個就是你的網路不通。
okhttp3.internal.http.RealResponseBody@2f58b42
解答:
若印出來的結果資料,類似上方文字,需改取出 body 內容資料,並加上 ? 問號
${response.body?.string()}
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,否則會發生類似以上錯誤。
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 線上教室,臉書粉絲團,會不定期發佈相關資訊,不想錯過最新資訊,不要忘記來按讚,追蹤喔!也歡迎大家將這篇文章分享給更多人喔。
我們明天再見囉!!!掰掰~