2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Android + Kotlin での HTTP リクエストの方法

Last updated at Posted at 2021-09-14

はじめに

Firebase Authentication の ID トークンを HTTP ヘッダに付与したり、独自データと JSON 文字列間の変換を行ったりすることはよくあるのですが、頻繁に使う割に忘れるので記事として残します。

環境

build.gradle は次のような内容になっています。

plugins {
    ...
    id 'org.jetbrains.kotlin.plugin.serialization' version '1.5.21'
    id 'com.google.gms.google-services'
}

dependencies {
    ...
    implementation "androidx.navigation:navigation-compose:2.4.0-alpha06"
    implementation platform('com.google.firebase:firebase-bom:28.4.0')
    implementation 'com.google.firebase:firebase-auth-ktx'
    implementation 'com.squareup.okhttp3:okhttp:4.9.0'
}

実装

  • POST で送信
  • Firebase Authentication の ID トークンを HTTP ヘッダに付与
  • Firebase Authentication の認証が終えていない場合は例外を投げる
  • Serializable なクラスのインスタンスを JSON 文字列にエンコード
  • API サーバーから受け取った JSON 文字列をクラスのインスタンスにデコード

上記の仕様を持つ request 関数は次のようになります。

val API_PREFIX = "http://192.168.1.100:3000/api"
val json = Json { ignoreUnknownKeys = true }

@OptIn(ExperimentalSerializationApi::class)
inline fun <reified T, reified S> request(path: String, post: T): S {
  val user = FirebaseAuth.getInstance().currentUser
  require(user != null)
  val idToken = Tasks.await(user.getIdToken(true)).token
  require(idToken != null)

  val client = OkHttpClient()
  val req = Request.Builder().apply {
    addHeader("Authorization", "Bearer $idToken")
    url("${API_PREFIX}${path}")
    post(json.encodeToString(post).toRequestBody("application/json".toMediaType()))
  }.build()

  client.newCall(req).execute().use {
    val str = it.body?.string()
    require(str != null)
    return json.decodeFromString(str)
  }
}

正直なところ、inlinereified を使う理由がよくわかりません。コンパイルエラーを直していくと上記のコードになりました。

使用例

次のように使用します。Kotlin の型推論により、戻り値の型を request 関数で指定する必要がなくて素晴らしいです。

@Serializable
data class ListUsersRequest(
  val offset: Int,
  val pageSize: Int,
)

@Serializable
data class User(val id: Int, val name: String)

fun listUsers(req: ListUsersRequest): List<User> {
  return request("/users/list", req)
}

この関数は Jetpack Compose の Composable から使うことを想定しています。ボタンがクリックされたときなどです。その箇所のコードは次のようになります。

var users by remember { mutableStateOf<List<User>>(emptyList()) }
var ajax by remember { mutableStateOf(false) }
val scope = rememberCoroutineScope()
val handleClick = {
  scope.launch(Dispatchers.IO) { 
    try {
      ajax = true
      val req = ListUsersRequest(0, 20)
      val res = listUsers(req)
      users = res
    } catch (e: Exception) {
      // TODO: ここで Snackbar などを使ってエラー内容をユーザーに伝える
      Log.e("ERROR", e.toString())
      return@launch
    } finally {
      ajax = false
    }
  }
  Unit
}
Button(onClick = handleClick, enabled = !ajax) {
  Text("click me!")
}

おわりに

HTTP リクエストを行うライブラリを公式が用意してくれると嬉しいのですが、ないですかね?

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?