Android+RetrofitでHTTP通信するアプリの作り方（その１）

# Android+RetrofitでHTTP通信するアプリの作り方（その１）

通信しようと思った時、簡単なものならHTTP APIを自分で処理するのもアリだと思います。

ですが、少し複雑になるとバグが混入しがちだし、潜在的な不具合に怯えながら実装を進めるのは精神的に良くないです。

そこで、ライブラリの力を借りることにします。

今回はRetrofitの力を借りることにします。

## 目次
- [Android+RetrofitでHTTP通信するアプリの作り方（その１）](#androidretrofitでhttp通信するアプリの作り方その１)
  - [目次](#目次)
  - [記事を書く上で参考にした資料](#記事を書く上で参考にした資料)
  - [Retrofitとは](#retrofitとは)
  - [準備](#準備)
    - [dependency](#dependency)
    - [Manifest](#manifest)
    - [JsonPlaceHolder](#jsonplaceholder)
  - [実装](#実装)
    - [data classの定義](#data-classの定義)
    - [Retrofitのインスタンス](#retrofitのインスタンス)
    - [interfaceの定義](#interfaceの定義)
    - [interfaceのインスタンス化](#interfaceのインスタンス化)
  - [実行](#実行)
  - [リポジトリ](#リポジトリ)

## 記事を書く上で参考にした資料

- CodeLabs

  - <https://developer.android.com/codelabs/basic-android-kotlin-compose-getting-data-internet#0>
  
  Retrofitに限らず、androidで何かしようと思っらodeLabsを当たりましょう。大抵書いてあります

- Retrofitのサイト
  - <https://square.github.io/retrofit/>
  
  本家サイト。

## Retrofitとは

<https://square.github.io/retrofit/>

Retrofit turns your HTTP API into a Java interface.

- HTTP APIがinterfaceになる、ということ

## 準備

### dependency

`app/build.gradle.kts`のdependenciesにretrofitを追加します。

```kotlin
dependencies {
    ...
    //retrofit
    implementation("com.squareup.retrofit2:retrofit:2.9.0")
    ...
}
```

今回はRetrofitでGsonを使うので、それ関連の項目も記述します。

```kotlin
dependencies {
    ...
    //retrofit
    implementation("com.squareup.retrofit2:retrofit:2.9.0")

    // Gson converter
    implementation("com.squareup.retrofit2:converter-gson:2.9.0")
    
    // Gson
    implementation("com.google.code.gson:gson:2.10.1")
    ...
}
```

### Manifest

インターネットへ接続するための`permission`を`Manifest`に書かないと、ネットにつながりません。

```xml : AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    ...
    <!--internet connection -->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    ...
</manifest>
```

<https://developer.android.com/develop/connectivity/network-ops/connecting>

### JsonPlaceHolder

サンプルとして[JsonPlaceHolder](https://jsonplaceholder.typicode.com/)を利用します。

たとえば<https://jsonplaceholder.typicode.com/posts>にアクセスすると、以下のようなJSONを得ます。
```json
[
  {
    "userId": 1,
    "id": 1,
    "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
    "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
  },
  {
    "userId": 1,
    "id": 2,
    "title": "qui est esse",
    "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
  }
  ...
]
```

これを前提に実装を進めます。

## 実装

[CodeLabs](https://developer.android.com/codelabs/basic-android-kotlin-compose-getting-data-internet#0) と[Retrofitのサイト](https://square.github.io/retrofit/)を参考に実装しています。

### data classの定義

上記のJSONに対応するdata classを定義します。

観察すると、下記のような構造のオブジェクトがアレイになっていることがわかります。


|Key    |Type   |
|---    |---    |
|userId |Int    |
|id     |Int    |
|title  |String |
|body   |String |

このオブジェクトに対応する`data class`を定義します。

```kotlin
data class Post(
    val userId: Int,
    val id: Int,
    val title: String,
    @SerializedName("body")
    val text: String
)
```

各引数名と各Key名は一致させます。

それができない、あるいは避けたい場合には`@SerializedName`を使って読み替えます

今回`body`を`text`に読み替えているのは、HTMLの`body`と紛らわしいためです。

### Retrofitのインスタンス

`Retrofit.Builder()`を使います。

 ```kotlin 
private val retrofit = Retrofit.Builder()
    .baseUrl("https://jsonplaceholder.typicode.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .build()
 ```

この例では`converter`として`GsonConverterFactory`を使用しています。

### interfaceの定義

interfaceには`@GET`アノテーションをつけた関数が必要です。

```kotlin
interface JsonPlaceHolderAPI {

    /**
     * 全ての記事を取得する
     */
    @GET("posts")
    suspend fun getPosts(): List<Post>
}
```

この関数の定義からいくつか読み取れる事柄があります

1. エンドポイントとして`post`を指定していること
2. `suspend`関数であること
3. `Post`のリストを返すこと

### interfaceのインスタンス化

Retrofitのインスタンスを通じて取得します。

```kotlin
val jsonPlaceHolderAPI = retrofit.create<JsonPlaceHolderAPI>()
```

## 実行

```kotlin
override fun onResume() {
    super.onResume()

    lifecycleScope.launch {
        val postList = jsonPlaceHolderAPI.getPosts()
        val text = "postList size is ${postList.size}"
        Log.d(TAG, "onResume: $text")
    }
}
```

suspend関数なのでlifecycleScopeから実行しています。

- suspendを使いたくない、使えない場合は、`enqueue`を使えます。`enqueue`では`Callback`で結果を得ます

結果は`LogCat`に出力されます。

## リポジトリ

<https://github.com/aburagirio/retrofitExample.git>

ブランチ`part1`をcheck outしてください。