【Android】Retrofit2を使ったAPI通信について

投資戦略システム事業部にて、Androidエンジニアをやっている佐野と申します、ちなみに柴犬が好きです。

持ち前の強運により、あみだくじで見事に当たり(?)を引き当ててしまったため、今回記事を書かせていただくこととなりました。

何について書こうか迷いましたが、今回はRetrofit2を使ったAPI通信の方法について書かせていただきたいと思います。

■Retrofit2とは?

OkHttp3の開発元が作成した、API通信を簡単に行なうことに特化したライブラリ。

公式サイト:https://square.github.io/retrofit/

他にも、HttpURLConnectionを使ってイチから実装する方法や Volleyなどがありますが最近はRetrofit2が使われることが多いみたいです。 ただ、それぞれ良さはあるので用途によって実装方法を検討するほうがいいと思います。

■導入

まずはAndroidManifest.xmlに、ネット接続のためのパーミッション設定を追加します。

※追加されている場合は次へ

AndroidManifest.xml

<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="パッケージ名" >
    
  <!-- インターネット接続の許可 -->
    <uses-permission android:name="android.permission.INTERNET"/>

</manifest>

次にRetrofit2を使うために、/app/build.gradleに以下を追記します。

/app/build.gradle

dependencies {
  // API通信用
    def retrofit_version = '2.6.2' // このバージョンは2019/12時点での最新です
    implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
    implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
}

これで前準備は完了です。

■実装

まず、APIを定義するためのinterfaceを作成します。

今回はサンプルとして、livedoorで配信されている天気取得用のフリーAPIを使っていきます。

公式サイト:http://weather.livedoor.com/forecast/webservice/json/v1

interface側で設定する部分は"v1"部分とクエリパラメータ部分です。

ApiService.kt

interface ApiService {
    @GET("v1")
    fun getWeather(@Query("city") city : String) : Call<WeatherData>
}

@GETはもちろん、@POSTや@PUTなども用意されているので簡単に定義することが出来ます。

このフリーAPIでは「city(定めされた都市コード)」のリクエストパラメータが必要になります。 [@Query("パラメータ名") セットするパラメータ : 型]とすることで、URLの最後に付与される?以降の要素をセットすることが出来ます。

引数としておくことで、getWeatherを外から呼び出すときに「city」を動的に指定することができるようになります。

尚、getWeatherの戻り値に指定されているCallはRetrofit2で用意されているレシーバークラスです。 <>に、自身で作成したEntityデータクラスをセットする必要があります。

雑に今回のAPIのデータクラスを定義するとこんな感じです。

WeatherData.kt

data class WeatherData(
    /** 予報の発表日時 */
    val publicTime : String,
    /** タイトル・見出し */
    val title : String,
    /** 天気概況文 */
    val description : Description,
    /** リクエストされたデータの地域に該当するlivedoor 天気情報のURL */
    val link : String,
    /** 府県天気予報の予報日毎の配列 */
    val forecasts: List<Forecasts>,
    /** 予報を発表した地域を定義 */
    val location: WeatherLocation,
    /** ピンポイント予報の発表地点の配列 */
    val pinpointLocations : List<PinpointLocations>
) : Serializable

data class Description(
    val text : String,
    val publicTime : String
) : Serializable

data class Forecasts(
    val date : String,
    val dateLabel : String,
    val telop : String,
    val image: Image,
    val temperature: Temperature
) : Serializable

data class Image(
    val title : String,
    val link : String,
    val url : String,
    val width : Int,
    val height : Int
) : Serializable

data class Temperature(
    val max : MaxMin,
    val min : MaxMin
) : Serializable

data class MaxMin(
    val celsius : Int,
    val fahrenheit : Float
) : Serializable

data class WeatherLocation(
    val area : String,
    val pref : String,
    val city : String
) : Serializable

data class PinpointLocations(
    val link : String,
    val name : String
) : Serializable

ここで定義した変数名とjsonの項目がキーとなっており、後述するGsonでのパースの際に一致している必要があります。

変数名とjsonの項目名を別にしたい場合はこのようにします。

@SerializedName("jsonの項目名") val 変数名 : 型

次はRetrofitクライアントの実装を行います。

RetrofitClient.kt

object RetrofitClient {
    /** APIインターフェイス */
    private var service: ApiService

    /** 初期化 */
    init {
    val gsonFactory = GsonConverterFactory.create(GsonBuilder().serializeNulls().create())

        val retrofit = Retrofit.Builder()
            .baseUrl("http://weather.livedoor.com/forecast/webservice/json/")
            .addConverterFactory(gsonFactory)
            .build()

        service = retrofit.create(ApiService::class.java)
    }

  /** 生成されたサービスを取得 */
  fun getService(): ApiService {
        return service
    }
}

簡単に説明しますと「.baseUrl」では接続先APIのURL指定、 「.addConverterFactory」ではJsonをパースするためのコンバータの指定をしています。

今回コンバータにはGsonを使用しますが、他にもMoshiやJacksonなどあるので、 ご自身が慣れている使いやすいものを使用してください。※個人的にはGson推し

尚、serializeNulls()とすることで、対象がnullのフィールドもシリアライズするようにしています。 デフォルトではシリアライズされませんのでご注意ください。

それではクライアントの実装が完了したので、これを使っていきましょう。

// サービスの生成
private val mService = ApiService.getService()

/**
 * APIリクエストを実施する
 */
fun requestWeatherData(cityId : String) {
  service.getWeather(cityId).enqueue(object : Callback<WeatherData> {
    override fun onResponse(call: Call<WeatherData>, response: Response<WeatherData>) {
      // 通信が成功したか判別
          if (response.isSuccessful) {
            // response.body()でパースされたWeatherDataが取得可能
            }
        }

        override fun onFailure(call: Call<WeatherData>, t: Throwable) {
              // エラー処理を記述
        }
  }
}

requestWeatherData()をボタンクリックなどから呼び出して使います。

この使い方は本当に基礎的なものなので、本来はViewModel+Repositoryを通してMVVMやMVC形式で使うことが望ましいです。

その辺りの説明はかなり長くなるので、割愛します。

尚、APIの実行にはcityIdが必要です。 実行する場合は固定でidをセットするか、EditTextなどで入力してください。

cityIdとして使えるidは以下で確認出来ます。

URL:http://weather.livedoor.com/forecast/rss/primary_area.xml

■おわり

ざっくりでしたがいかがでしたでしょうか?

AndroidでAPI通信する際はぜひRetrofit2を使ってみてください!

最後におすすめの柴犬Youtubeチャンネルを貼っておきます!

毎日更新されているのでぜひみてください!

【よりめのはちくん。】

www.youtube.com

tecotec.co.jp