【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 線上教室,臉書粉絲團,會不定期發佈相關資訊,不想錯過最新資訊,不要忘記來按讚,加追蹤喔!也歡迎大家將這套課程分享給更多人喔。

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

留言

這個網誌中的熱門文章

最新入門零基礎【從零開始學 Java 程式設計】線上教學課程目錄

NS - Nintendo Switch 遊戲比價網

【從零開始學 Kotlin 程式設計】Android Kotlin 線上教學課程目錄