Weather app: Coroutines and Retrofit2

Table of Contents

Table of Contents


[上一篇文章](https://cyublog.com/articles/android/zh-weatherapp00/)介紹了Open Weather相關的內容,然後定義了物件資料。

接著,這篇文章將介紹使用Retrofit與Coroutines來做網路API的處理。


Github

https://github.com/scobin/Android_WeatherApp/tree/feature/weatherData


Build.gradle

為了加入CoroutinesRetrofit工具庫,請先開啟app資料夾下的build.gradle文件,並將以下的程式碼添入dependencies內。 (請小心注意版本號,以免程式執行中出現錯誤!)

dependencies {
...
    // Coroutines
    def coroutines_version = '1.3.0'
    implementation "org.jetbrains.kotlinx:kotlinx-outins-core:$coroutines_version"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"

    // Retrofit2
    implementation 'com.squareup.retrofit2:retrofit:2.6.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.6.0'
    implementation 'com.squareup.okhttp3:logging-interceptor:3.8.1'
}

AndroidManifest.xml

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

</manifest>

WeatherAPIService

手動生成WeatherAPIService.kt文件,內容如下方程式所示。 設置Retrofit工具,並定義了getForecast函數來取得網路資料。

為了方便觀察網路通訊的狀況,okHttpClient的設定中使用logging-interceptor

const val BASE_URL = "https://api.openweathermap.org/"
interface WeatherAPIService {

    @GET("data/2.5/forecast")
    suspend fun loadWeather(cityId: String): WeatherResponse? {
        return runCatching { weatherAPIService.getForecast(cityId) }.fold(
            onSuccess = {
                if (it.isSuccessful) {
                    var body = it.body()
                    if (body == null || body.cod != "200") {
                        return null
                    }
                    return body
                } else {
                    return null
                }
            },
            onFailure = { null }
        )
    }

    companion object {
        operator fun invoke(): WeatherAPIService {
            val requestInterceptor = Interceptor { chain ->
                val url = chain.request()
                    .url()
                    .newBuilder()
                    .addQueryParameter("appid", APP_ID)
                    .addQueryParameter("mode", "json")
                    .build()

                val request = chain.request()
                    .newBuilder()
                    .url(url)
                    .build()

                return@Interceptor chain.proceed(request)
            }

            val okHttpClient = OkHttpClient.Builder()
                .addInterceptor(requestInterceptor)
                // logging settings
                .addInterceptor(HttpLoggingInterceptor().apply {
                    level = HttpLoggingInterceptor.Level.BODY
                })
                .build()

            return Retrofit.Builder()
                .client(okHttpClient)
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build()
                .create(WeatherAPIService::class.java)
        }
    }
}

參考資料:Kotlin-runCatching


使用例

val weatherAPIService = WeatherAPIService()
GlobalScope.launch() {
    val response = weatherAPIService.getForecast("7280291")
    // 畫面顯示結果
    testText.text = response.toString()
}