Retrofit+RxJava+kotlinを使ったAPIコール処理を作ってみた
はじめに
AndroidでWebAPIを呼び出す処理を行う場合、いろいろ方法がありますが、今回はRetrofit+RxJavaを使用して簡単サンプルアプリを作成してみます。
Kotlinの導入
Android Studio 3.2を使用しているのでkotlinの導入は自動化されています。
include Kotlin support にチェックをつけます。
そのままNextと進んで最終的に新規プロジェクトを作成すると
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.2.71'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
:
}
dependencies {
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
:
:
}
のように設定されるので、Kotlinが使用可能となります。
RxJavaの導入
implementation "io.reactivex:rxjava:1.3.8"
implementation "io.reactivex:rxandroid:1.2.1"
Retrofit2の導入
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
implementation 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.9.1'
APIを呼び出してみる
皆様お世話になっている定番の郵便番号から住所を検索できるAPI
http://zipcloud.ibsnet.co.jp/api/search
を使用しました。
ZipCloudさんありがとうございます
ZipResponse(Response用のEntity オブジェクト)
APIを呼び出したときのレスポンスを格納するものになります。
data class ZipResponse(
var message: String? = null,
var status: Int? = null,
var results: ArrayList<Address> = ArrayList()
)
data class Address(
var address1: String ?= null, //"address1": "北海道",
var address2: String ?= null, //"address2": "美唄市",
var address3: String ?= null, //"address3": "上美唄町協和",
var kana1: String ?= null, //"kana1": "ホッカイドウ",
var kana2: String ?= null, //"kana2": "ビバイシ",
var kana3: String ?= null, //"kana3": "カミビバイチョウキョウワ",
var prefcode: String ?= null, //"prefcode": "1",
var zipcode: String ?= null //"zipcode": "0790177"
)
ApiClientを作成
interface ApiClient {
@GET("api/search")
fun getZipCode(@Query("zipcode") zipcode: String): Observable<ZipResponse>
}
今回は1つのAPIコールだけですが、複数ある場合は、ここにAPI毎のメソッドを定義していきます
もちろん、@GET
、@POST
、その他もサポートされています。
https://square.github.io/retrofit/
ApiClientManagerを作成
open class ApiClientManager {
companion object {
private const val ENDPOINT = "http://zipcloud.ibsnet.co.jp/"
private val TAG = ApiClientManager::class.simpleName
val apiClient: ApiClient
get() = Retrofit.Builder()
.client(getClient())
.baseUrl(ENDPOINT)
.addConverterFactory(GsonConverterFactory.create(Gson()))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build()
.create(ApiClient::class.java)
private fun getClient(): OkHttpClient {
return OkHttpClient
.Builder()
.connectTimeout(120, TimeUnit.SECONDS)
.readTimeout(120, TimeUnit.SECONDS)
.addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
})
.build()
}
}
}
呼び出してみる
private val compositeSubscription = CompositeSubscription()
override fun onCreate(savedInstanceState: Bundle?) {
binding.buttonSubmit.setOnClickListener { _ ->
val zipcode = "0790177"
compositeSubscription.clear()
compositeSubscription.add(
ApiClientManager.apiClient.getZipCode(zipcode)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext {
Log.d(TAG, "response=$it")
binding.textViewResponse.text = Gson().toJson(it)
}
.doOnError {
}
.doOnCompleted {
}
.subscribe())
}
}
override fun onDestroy() {
compositeSubscription.clear()
super.onDestroy()
}
onDestroy()
で compositeSubscription.clear()
を呼ぶことでActivity破棄時に未処理のsubscribeを破棄できます
D/OkHttp: --> GET http://zipcloud.ibsnet.co.jp/api/search?zipcode=0790177
D/OkHttp: --> END GET
D/OkHttp: <-- 200 OK http://zipcloud.ibsnet.co.jp/api/search?zipcode=0790177 (598ms)
D/OkHttp: Content-Type: text/plain; charset=utf-8
D/OkHttp: X-Cloud-Trace-Context: fbdffd1b9974a560f3a891322b559248
D/OkHttp: Date: Fri, 28 Sep 2018 14:07:26 GMT
D/OkHttp: Server: Google Frontend
D/OkHttp: Content-Length: 854
D/OkHttp: {
D/OkHttp: "message": null,
D/OkHttp: "results": [
D/OkHttp: {
D/OkHttp: "address1": "北海道",
D/OkHttp: "address2": "美唄市",
D/OkHttp: "address3": "上美唄町協和",
D/OkHttp: "kana1": "ホッカイドウ",
D/OkHttp: "kana2": "ビバイシ",
D/OkHttp: "kana3": "カミビバイチョウキョウワ",
D/OkHttp: "prefcode": "1",
D/OkHttp: "zipcode": "0790177"
D/OkHttp: },
D/OkHttp: {
D/OkHttp: "address1": "北海道",
D/OkHttp: "address2": "美唄市",
D/OkHttp: "address3": "上美唄町南",
D/OkHttp: "kana1": "ホッカイドウ",
D/OkHttp: "kana2": "ビバイシ",
D/OkHttp: "kana3": "カミビバイチョウミナミ",
D/OkHttp: "prefcode": "1",
D/OkHttp: "zipcode": "0790177"
D/OkHttp: },
D/OkHttp: {
D/OkHttp: "address1": "北海道",
D/OkHttp: "address2": "美唄市",
D/OkHttp: "address3": "上美唄町",
D/OkHttp: "kana1": "ホッカイドウ",
D/OkHttp: "kana2": "ビバイシ",
D/OkHttp: "kana3": "カミビバイチョウ",
D/OkHttp: "prefcode": "1",
D/OkHttp: "zipcode": "0790177"
D/OkHttp: }
D/OkHttp: ],
D/OkHttp: "status": 200
D/OkHttp: }
D/OkHttp: <-- END HTTP (854-byte body)
D/MainActivity: response=ZipResponse(message=null, status=200, results=[Address(address1=北海道, address2=美唄市, address3=上美唄町協和, kana1=ホッカイドウ, kana2=ビバイシ, kana3=カミビバイチョウキョウワ, prefcode=1, zipcode=0790177), Address(address1=北海道, address2=美唄市, address3=上美唄町南, kana1=ホッカイドウ, kana2=ビバイシ, kana3=カミビバイチョウミナミ, prefcode=1, zipcode=0790177), Address(address1=北海道, address2=美唄市, address3=上美唄町, kana1=ホッカイドウ, kana2=ビバイシ, kana3=カミビバイチョウ, prefcode=1, zipcode=0790177)])
最後に
CompositeSubscriptionが便利です。
AsyncTaskを使用した場合などに比べ、シンプルにコードが書けると思います。
ソースは
https://github.com/motomiya326/retrofit-example
にあるのでご自由にお使いください。