【初心者向け】Kotlin&AndroidでRetrofitを使ってAPIから取得したデータをviewに表示させる

# はじめに
Retrofitを使ってAndroidアプリからAPIを叩いて、viewに表示させるところまでをやろうかと思います。他にもRetrofitの記事は多いので、細かい説明は省きます。
いくつか勉強する中で、なんとなくはわかったものの、実際にアプリに組み込む時にどのような構成にしようか迷ったため、その構成も含めて書きたいと思います。

# 全体の構成
下記の構成で、[GitHubのAPI](https://developer.github.com/v3/repos/#list-user-repositories)から取得したリポジトリのデータをviewに表示させます。

```
└── retrofit_exercise
    ├── api
    │   └── APIClient.kt
    ├── response
    │   └── Response.kt
    ├── services
    │   ├── GitHubRepositoryService.kt
    │   └── GitHubUserService.kt(追加をする場合、説明では省略)
    │
    └── MainActivity.kt
```


# [Retrofit](http://square.github.io/retrofit/)を使ってみる
作成したコードは[GitHub](https://github.com/buntafujikawa/retrofit-exercise)にあげたので、説明いらないって人はよかったら見てみてください。

## まずは準備
permissionの設定と、使うライブラリを指定します。

```AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
```

```app/build.gradle
ext {
    retrofit = '2.3.0'
}

implementation "com.squareup.retrofit2:retrofit:$retrofit"
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.9.1'
```

## 使うAPIのドキュメントを確認して、どんなデータを受けとるか決める
今回は[GitHubのlist-user-repositories](https://developer.github.com/v3/repos/#list-user-repositories)からデータをとってきます。
こちらのレスポンスの形式を確認して、それ用のdataクラスを作成します。

今回はリポジトリ名、詳細、言語、urlのみ取得することにします

```Response.kt
data class Repos(val name: String, val description: String, val language: String, val url: String)

// 他のAPIから取得する場合にはここに追加をしていく
```

## ドキュメント通りのリクエスト形式になるようにretrofitを使って記述する
同じく先ほどのドキュメントからリクエストの形式を確認します。
こんなのを作る想定です。
`curl "https://api.github.com/users/{user}/repos?users=username(自分のgithubアカウント名を引数で渡す)&sort=desc(これも引数で渡す)"`

interfaceの呼び出し元で`BASE_URL = "https://api.github.com/"` と指定をするため、`@GET`のパスはusers~となっています。
あとリポジトリ名とsortも呼び出し元で引数に渡すようにします。

細かいところは[公式のドキュメント](http://square.github.io/retrofit/)を確認してください。

```GitHubRepositoryService.kt
// https://developer.github.com/v3/repos/#list-user-repositories
interface GitHubService {
    @GET("users/{user}/repos")
    fun fetchReposList(@Path("user") user: String, @Query("sort") sort: String): Call<List<Repos>>
}
```

## 作成したinterfaceの呼び出しを行う
`ACCOUNT_NAME`を自分のアカウント名に変更してください。
サーバで作成したAPIを取得して、処理をすることが多いと思うので、base urlを指定したretrofitのインスタンスを返すメソッドを作成しました。

```APIClient.kt

object APIClient {
    private const val BASE_URL = "https://api.github.com/"
    private const val ACCOUNT_NAME = "自分のgithubアカウント名"

    private fun restClient() : Retrofit {
        return Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }

    fun fetchReposList() :Response<List<Repos>> {
        val service = restClient().create(GitHubService::class.java)
        return service.fetchReposList(ACCOUNT_NAME, "desc").execute()
    }

    // 他のAPIから取得する場合にはここに追加をしていく
}
```

## viewに表示をさせる
ただとってきたデータを表示させるだけなので、手抜きですが取得したデータをそれぞれ表示させるtext viewを作成します。

```activity_main.xml
(省略)
    <TextView
        android:id="@+id/name_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!" />

    <TextView
        android:id="@+id/description_text"
        android:layout_width="wrap_content"
        android:layout_height="15dp"
        android:layout_marginTop="50dp"
        android:text="Hello World!"
        app:layout_constraintBottom_toTopOf="@+id/name_text"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/language_text"
        android:layout_width="wrap_content"
        android:layout_height="15dp"
        android:layout_marginTop="65dp"
        android:text="Hello World!"
        app:layout_constraintBottom_toTopOf="@+id/description_text"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/url_text"
        android:layout_width="wrap_content"
        android:layout_height="15dp"
        android:layout_marginTop="80dp"
        android:text="Hello World!"
        app:layout_constraintBottom_toTopOf="@+id/language_text"
        app:layout_constraintTop_toTopOf="parent" />
(省略)
```

今回はこちらから呼び出しを行いますが、fragmentに表示させる場合にはそちらに追加をしたほうが良いと思います。

```MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val handler = Handler()

        // メインスレッドでネットワーク通信を行うことができない
        thread {
            try {
                val response = APIClient.fetchReposList()
                // とりあえず1つだけ表示させてみる
                val firstRepos = response.body()!![0]

                // 別スレッドからUI操作ができないのでhandlerを使用する
                handler.post(Runnable {
                    name_text.text = firstRepos.name
                    description_text.text = firstRepos.description
                    language_text.text = firstRepos.language
                    url_text.text = firstRepos.url
                })

                Log.d("retrofit", "リポジトリのID" + response.body())
            } catch (e: Exception) {
                Log.w("retrofit", "fetchReposList :" + e)
            }
        }
    }
```

これでひとまず動くと思うので、APIを変更したりPOSTの場合を試してみたりとしてもらえれば良いなと思います。
APIから受け取ったデータをRxJavaを使って処理をすることが多いようなので、検証したら追記したいと思います。