【Kotlin】RetrofitでHTTPリクエスト【Androidアプリ開発】
Retrofitを使った説明サイトはたくさんあるが、デザインパターンを埋め込んで説明しているものが多く、肝心のRetrofitの役割がどこからどこまでなのかイメージし辛く分かりづらかった。そこで今回、Retrofitを動かすために必要な最小限のコードで、Androidのサンプルプロジェクトを作ってみた。最小限のサンプルではあるが、動作を理解する上ではとてもわかりやすくなったのでこの記事で紹介してく。
Retrofitを使ったサーバー通信の完成イメージ
Retrofitとは、APIから取得するJSONデータをオブジェクト化して簡単に操作できるようにしてくれるHTTP clientである。APIから拾ったデータを図のようにViewへ表示させることを目標に、Retrofitを使っていく。
いきなりだが全コードを先に記しておく。以外に短くて、拍子抜けしたかもしれない。次では、コードの内容を順に追って解説していく。
package com.apppppp.retrofitsample
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.util.Log
import android.widget.TextView
import retrofit2.Call
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.GET
import kotlin.concurrent.thread
data class JoJo(val name: String = "", val stand: String = "")
interface JoJoService {
// -------レスポンスの中身-------
// {
// "name":"Jyotaro",
// "stand":"The World"
// }
// -------おわり-----------
@GET("jojo.json")
fun fetchJoJo(): Call<JoJo>
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val retrofit = Retrofit.Builder()
.baseUrl("https://apppppp.com/")
.addConverterFactory(GsonConverterFactory.create())
// ↑↑↑↑↑↑↑↑
// Gsonのファクトリーメソッド必須!
// これがないと次のエラーが出てハマる
// java.lang.IllegalArgumentException: Unable to create converter for class com.apppppp.retrofitsample.JoJo
// for method JoJoService.fetchJoJo
//
.build()
val handler = Handler()
thread { // Retrofitはメインスレッドで処理できない
try {
val service:JoJoService = retrofit.create(JoJoService::class.java)
val jojo = service.fetchJoJo().execute().body() ?: throw IllegalStateException("bodyがnullだよ!")
handler.post(Runnable {
// メインスレッドで処理
val nameTextView = findViewById<TextView>(R.id.name)
val standTextView = findViewById<TextView>(R.id.stand)
nameTextView.text = jojo.name
standTextView.text = jojo.stand
})
} catch (e: Exception) {
Log.d("mopi", "debug $e")
}
}
}
}
APIの準備
まず自前のサーバー上に、次の簡単な内容のJSONファイルを置いてみた。 http://apppppp.com/jojo.json
{
"name":"Jyotaro",
"stand":"The World"
}
できるだけ簡単な構造のJSONファイルを用意することをおすすめする。いきなりGitHubやQiitaのAPIのように大規模なものを使ってしまうと、Retrofitの理解に集中できないからだ。だからデータはシンプルで単純なものが良い。それに、自前で用意したものなら仕様を変更できるし興味もわいてくるだろう。
Retrofitの実装 (implementation)
Retrofitを使えるようにするため、新規プロジェクトのbuild.gradle (Module: app)に、次のように追加した。これは、retrofit2最新版と、GSON、そしてRetrofit用のGSONコンバーターを使用するためだ。
dependencies {
...
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
implementation 'com.google.code.gson:gson:2.8.5'
}
データクラスの作成
JSONファイルの内容と合わせて、次のデータクラスを定義した。ただし、JSONのキーの名前とプロパティの名前は同じものでなければならない。
data class JoJo(val name: String = "", val stand: String = "")
インタフェースの作成
次に、インタフェースを作成する。ドメイン以下のパスをGETアノテーションの引数に渡す。つまりこの場合だと、ドメイン直下にファイルを置いたのでjojo.jsonを渡すことになる。
interface JoJoService {
@GET("jojo.json")
fun fetchJoJo(): Call<JoJo>
}
fetchJoJoメソッドは、APIリクエストを行う関数。定義をするだけで、実装をやる必要はない。なんと素晴らしいことに、Retrofixが実装をやってくれるのだ!fetchJoJoメソッドで返されるCallオブジェクトは、 import retrofit2.Call でインポートしているものだ。どんなものなのか一度のぞいておくと良いだろう。
MainActivity
最後にMainActivityクラスを見ていこう。ここでは、Retrofitオブジェクトを次のようにして生成する。
val retrofit = Retrofit.Builder()
.baseUrl("https://apppppp.com/")
.addConverterFactory(GsonConverterFactory.create())
.build()
注意してほしいのは addConverterFactory(GsonConverterFactory.create()) 。今回GSONを使用するので、ファクトリーメソッドを設定しないとJSONをオブジェクトに変換できない。次のエラーが出たら、このファクトリーメソッドを疑ってほしい。
java.lang.IllegalArgumentException: Unable to create converter for class com.apppppp.retrofitsample.JoJo
for method JoJoService.fetchJoJo
それでは、さきほどのRetrofitオブジェクトを実際に使っていこう。メインスレッドでは実行できないので、thread内で実行していく。retrofit.create(JoJoService::class.java)では、先ほど作ったインタフェースのクラスオブジェクトを渡している。
val handler = Handler()
thread { // Retrofitはメインスレッドで処理できない
try {
val service:JoJoService = retrofit.create(JoJoService::class.java)
val jojo = service.fetchJoJo().execute().body() ?: throw IllegalStateException("bodyがnullだよ!")
....
} catch (e: Exception) {
Log.d("mopi", "debug $e")
}
}
スレッド内から、UIを更新したい場合は次の記述をする。
handler.post(Runnable {
// メインスレッドで処理
val nameTextView = findViewById<TextView>(R.id.name)
val standTextView = findViewById<TextView>(R.id.stand)
nameTextView.text = jojo.name
standTextView.text = jojo.stand
})
以上でRetrofitを使ったサンプルコードのでき上がりだ。おっと、忘れていた!次の記述がマニフェストにないと、インターネットアクセス出来ないので注意しよう。
<uses-permission android:name="android.permission.INTERNET" />
httpアドレスへアクセスできない場合は、次の記事を参考に。
▼ こんな記事も書いてます。