Android Kotlin基礎講座 08.1:インターネットからデータを取得する
目次
タスク:JSONをMoshiでパースする
現在、MarsウェブサービスからJSONの結果を取得しています。これは素晴らしいスタートではありますが、本当に必要なのはKotlinオブジェクトであり、膨大なJSON Stringではありません。そこで、Moshiというライブラリがあります。これはJSON StringをKotlinオブジェクトに変換してくれるAndroidのJSONパーサーです。RetrofitにはMoshiと連動して機能するコンバーターがあるので、Moshiは今回の目的においては素晴らしいライブラリといえます。
このタスクでは、ウェブサービスから得たJSON結果をKotlinオブジェクトに変換するためにMoshiライブラリをRetrofitと合わせて使っていきます。生のJSONを表示するのではんかう、火星の物件の数を表示できるようにアプリを修正していきます。
ステップ1:Moshiライブラリ依存関係を追加する
- build.gradle(Module: app)を開いてください。
- dependenciesセクションの中に、以下のコードを追加してMoshiの依存関係を追加してください。Retrofitの場合と同じく、$version_moshiはプロジェクトレベルのGradleファイルに切り離されて定義されています。この依存関係はKotlinサポートとMoshi JSONライブラリのサポートを追加します。
implementation "com.squareup.moshi:moshi-kotlin:$version_moshi"
- dependeciesセクションのRetrofitスカラーコンバーターの行に移動してください。
implementation "com.squareup.retrofit2:retrofit:$version_retrofit"
implementation "com.squareup.retrofit2:converter-scalars:$version_retrofit"
- これらの行を、converter-moshiを使うように変更してください。
implementation "com.squareup.retrofit2:converter-moshi:$version_retrofit"
- Sync Nowをクリックして、プロジェクトが新規依存関係とリビルドできるようにしてください。
Note: 削除されたRetrofitスカラーの依存関係が原因のコンパイルエラーが表示される場合があります。この問題については次のステップで解決します。
ステップ2:MarsPropertyデータクラスを実装する
現在ウェブサービスから取得したJSON結果は以下のように表示されています。
[{"price":450000,
"id":"424906",
"type":"rent",
"img_src":"http://mars.jpl.nasa.gov/msl-raw-images/msss/01000/mcam/1000ML0044631300305227E03_DXXX.jpg"},
...]
上で表示されているJSON結果は配列です。これは角括弧[ ]によって示されています。配列には、中括弧{ }で覆われたJSONオブジェクトが含まれています。それぞれのオブジェクトにはコロンで区切られた名前と値のペアが含まれています。さらに名前はダブルクォーテーションで囲まれており、値は数値かstringで、stringの場合はダブルクォーテーションで囲まれています。例として、この物件のpriceは$450,000で、img_srcはサーバー上の画像ファイルの位置を表すURLです。
上記の例で、それぞれの火星の物件がJSONのキーと値のペアを持っていることを確認してください。
price
: 火星の物件の値段。数値です。id
: 物件のID。stringです。type
: “rent”か”buy”です。img_src
: 画像のURL. stringです。
MoshiはこのJSONデータを解析し、Kotlinオブジェクトに変換してくれます。これをするためには、解析された結果を保存するための、Kotlinデータクラスが必要です。次のステップはこのクラスを作成することです。
- app/java/network/MarsProperty.ktを開いてください。
- 既存のMarsPropertyクラス定義を以下のコードで置き換えてください。
data class MarsProperty(
val id: String, val img_src: String,
val type: String,
val price: Double
)
MarsPropertyクラスのそれぞれの変数がJSONオブジェクトのキーの名前と対応していることを確認してください。JSON中の型と合わせるために、price以外の全ての値に対してStringを使います。priceはDoubleです。DoubleはいかなるJSON数値でも表すことができます。
MoshiがJSONを解析する際、キーを名前で照合し、データオブジェクトに適切な値を入れます。
- img_srcキーのコードを以下のコードで置き換えてください。
要求されたらcom.squareup.moshi.Jsonをインポートしてください。
@Json(name = "img_src") val imgSrcUrl: String,
JSON結果のキー名とKotlinのプロパティ名が混同してまったり、ご自身のコーディングスタイルと合わない場合があります。例えば、JSONファイル中のimg_srcキーは、アンダーラインを使っています。一方でKotlinのプロパティでは一般的にアッパーローワーケース(”キャメルケース”)が使われます。
JSON結果のキー名と異なる変数名をデータクラス中で使うためには、@Jsonアノテーションを使います。今回の例では、データクラス中の変数名はimgSrcUrlです。この変数は@Json(name ” “img_src”)を使うことで、JSON属性のimg_srcに当てられるようにあります。
ステップ3:MarsApiServiceとOverviewViewModelをアップデートする
MarsPropertyデータクラスを作ったことで、ネットワークAPIとViewModelにMoshiデータを含ませることができるようになりました。
- network/MarsApiService.ktを開いてください。ScalarsConverterFactoryが無くなったことに対するエラーが表示されていると思います。これはステップ1でRetrofitの依存関係の変更したことが原因です。
- ファイルのトップ、Retrofitビルダーの直前に、以下のコードを追加してMoshiインスタンスを作成してください。
要求されたら、com.squareup.moshi.Moshiとcom.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactoryをインポートしてください。
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
Retrofitで行ったことと似て、ここではMoshiビルダーを使ってmoshiオブジェクトを作成しています。Kotlinで適切に動作するためのMoshiのアノテーション用に、KotlinJsonAdapterFactoryを追加し、最後にbuild()を呼び出しています。
- Retrofitビルダーを変更して、ScalarConverterFactoryの代わりにMoshiConverterFactoryを使うようにしてください。またたった今作成したmoshiインスタンスを渡してください。
要求されたら、retrofit2.converter.moshi.MoshiConverterFactoryをインポートしてください。
private val retrofit = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.baseUrl(BASE_URL)
.build()
- ScalarConverterFactoryのインポート文を削除してください。
削除するコード:
import retrofit2.converter.scalars.ScalarsConverterFactory
- RetrofitがCall<String>の代わりに、MarsPropertyオブジェクトのリストを返すようにするためにMarsApiServiceインターフェースをアップデートしてください。
interface MarsApiService {
@GET("realestate")
fun getProperties():
Call<List<MarsProperty>>
}
- OverviewViewModel.ktを開いてください。getMarsRealEstateProperties()メソッド中のgetProperties().enqueue()の呼び出しまでスクロールしてください。
- enqueue()の引数をCallback<String>からCallback<List<MarsProperty>>に変更してください。
要求されたら、com.example.android.marsrealestate.network.MarsPropertyをインポートしてください。
MarsApi.retrofitService.getProperties().enqueue(
object: Callback<List<MarsProperty>> {
- onFailure()中の引数をCall<String>からCall<List<MarsProperty>>に変更してください。
override fun onFailure(call: Call<List<MarsProperty>>, t: Throwable) {
- 同様にonResponse()の引数も変更してください。
override fun onResponse(call: Call<List<MarsProperty>>,
response: Response<List<MarsProperty>>) {roperty>>) {
- onResponse()の中で、既存の_response.valueへの代入部分を以下のように変更してください。response.body()は現在MarsPropertyオブジェクトのリストなので、そのリストのサイズが解析された物件の数ということになります。このresponseメッセージは物件の数を表示します。
_response.value =
"Success: ${response.body()?.size} Mars properties retrieved"
- 機内モードがオフになっていることを確認してください。コンパイルしてアプリを起動してください。今回はメッセージはウェブサービスから返された物件の数を表示します。
Note: インターネット接続が正常にいかない場合は、実機、またはエミュレーターの機内モードがオフになっていることを確認してください。