16
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

Android Retrofit2とMoshi

はじめに

Androidアプリの開発でAPI通信を行う際、Retrofitを使うことが多いと思います。
取得したjsonをパースする際にGsonを利用してもいいのですが、Kotlinと相性が悪いです。
そこで、Kotlinのclassへのサポートを提供しているSquareのMoshiを使ってjsonをパースしました。

  • Kotlin 1.3.21
  • Retrofit 2.5.0
  • Moshi 1.5.0
  • Okhttp 3.13.1
app/build.gradle
// ...

dependencies {

    // ...

    // Retrofit
    def retrofit_version = '2.5.0'
    implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
    implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version"

    // Moshi
    def moshi_version = '1.5.0'
    implementation "com.squareup.moshi:moshi:$moshi_version"
    implementation "com.squareup.moshi:moshi-kotlin:$moshi_version"

    // Okhttp
    def okhttp_version = '3.13.1'
    implementation "com.squareup.okhttp3:okhttp:$okhttp_version"
    implementation "com.squareup.okhttp3:logging-interceptor:$okhttp_version"
}

受け取るJson

APIを叩いた時に受け取るJsonはこちらを想定しています。

companies.json
{
    "companies": [
        {
            "id": "001",
            "name": "会社1",
            "img": "https://kaisya1"},
        {
            "id": "002",
            "name": "会社2",
            "img": "https://kaisya2"
        }
    ]
}

data class

パースしてオブジェクト化するためのclassがこちらです。

Company.kt
data class Company (val id: String, val name: String, val img: String)
Companies.kt
data class Companies (val companies: List<Company>)

Interface

Retrofit2のinterfaceはこちらです。

CompanyApiInterface.kt
interface CompanyApiInterface {
    @GET("companyList")
    fun getCompanyList(): Call<Companies>
}

実装例

API通信を行なうclassをこんな感じで実装してみました。

CompanyRepository.kt
class CompanyRepository(url: String) {

    private var retrofit: Retrofit

    init {
        val moshi = Moshi.Builder()
                .add(KotlinJsonAdapterFactory())
                .build()

        this.retrofit = Retrofit.Builder()
                .baseUrl(url)
                .addConverterFactory(MoshiConverterFactory.create(moshi))
                .client(getClient())
                .build()
    }

    fun getCompanies(): Response<Companies> {
        val service = this.retrofit.create(ArticleApiInterface::class.java)
        return service.getCompanyList().execute()
    }

    private fun getClient(): OkHttpClient {
        return OkHttpClient
                .Builder()
                .connectTimeout(120, TimeUnit.SECONDS)
                .readTimeout(120, TimeUnit.SECONDS)
                .addInterceptor(HttpLoggingInterceptor().apply {
                    level = HttpLoggingInterceptor.Level.BODY
                })
                .build()
    }
}

利用する際はこの様に利用します。

private fun load(){
    try{
        val companyRepository = CompanyRepository("https://hoge")
        val response = companyRepository.getCompanies()
            if(response.isSuccessful){
                // 成功
            }else{

            }
    }catch (t: Throwable){

    }
}

つまったとこ

Moshiと全く関係のない箇所でつまりました。
Retrofitのinterfaceをこの様に作成していました。

@GET("companyList")
fun getCompanyList(): Call<List<Company>>

こう書いてしまうとAPI通信は成功しますが、
Jsonのcompaniesのキーに紐づく変数がなくパースに失敗します↓。
retrofit expected begin_array but was begin_object at line 1 column 2 path $
よってこのdata classを作成することで解決しました。

data class Companies (val companies: List<Company>)

よりよい解決方法をご存知の方は教えていただけると幸いです!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
16
Help us understand the problem. What are the problem?