【Android 入門開發實戰:口罩地圖】OkHttp 獲取網路資料方式

【Android 入門開發實戰:口罩地圖】線上免費講義課程目錄

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/

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

程式碼範例

範例名稱:OkHttp 基本拉資料方式 (GET 請求範例)
開發人員:HKT (侯光燦)
程式語言:Kotlin
開發環境:Android Studio 4.1.1 & Android 11 & Kotlin 1.4.21
授權範圍:使用時必須註明出處且不得為商業目的之使用
範例下載點:點我下載

那這次的課程就介紹到這邊囉~

順帶一提,KT 線上教室,臉書粉絲團,會不定期發佈相關資訊,不想錯過最新資訊,不要忘記來按讚,加追蹤喔!也歡迎大家將這套課程分享給更多人喔。

我們下次再見囉!!!掰掰~

這個網誌中的熱門文章

2023 最新入門零基礎 Kotlin教學【從零開始學 Kotlin 程式設計】Kotlin 教學課程目錄 (Android Kotlin, IntelliJ IDEA, Android Studio, Android APP 開發教學)

2022 最新入門零基礎 Flutter教學 【Flutter 程式設計入門實戰 30 天】Flutter 教學課程目錄 (IntelliJ IDEA 開發教學)

nano 文字編輯器

Android Studio 歷代版本下載點

16天記下7000單字