LoginSignup
25
15

More than 3 years have passed since last update.

3分で理解する coroutine + retrofit2

Last updated at Posted at 2020-06-09

なぜcorutineを使用するのか?

非同期処理のコールバック地獄を防ぐ為です。
corutineを使用することにより、非同期処理をより高速に、少ない行数で書くことができます。
もちろんcorutineを使用しなくても非同期処理をかけますが、corutineを使った方が遥かに簡単に実装できます。

この記事では3分でcorutine + retorofit2 を理解することを目指します。

セットアップ

Android StudieでEmptyActivityを選択し、kotolinでHelloWorldというアプリを作成してください。
スクリーンショット 2020-06-09 22.04.57.png

4つのライブラリを追加

retrofit = http通信ライブラリ 2020年のhttpリクエストのデファクトスタンダード
converter-gson = retorofitで使用する jsonのパーサー
coroutines-core = coroutineのコア。 coroutineは、kotlinとは別々に提供される。
lifecycle-runtime = 後述するlifecycleScopeを使用する為のもの。 kotlin公式

// 以下二行は、retrofit
implementation 'com.squareup.retrofit2:retrofit:2.6.0'
implementation 'com.squareup.retrofit2:converter-gson:2.6.0'
// 以下二行はcoroutine
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0'

http通信を許可する

Androidでは通信を行うにはAndroidManifestに以下の一行を加え、通信を許可する必要がある。

 <uses-permission android:name="android.permission.INTERNET"/>

をAndroidiManifestに加えて、http通信を許可する。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.helloworld">

+    <uses-permission android:name="android.permission.INTERNET"/>


    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

実装

これから出てくるコードは全てMainActivityの中に記述する。厳密には正しくないが、シンプルさの為にいらないコードは削除している。

初期MainActivityは以下

package com.example.helloworld

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {

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

モデルを作成する。
今回取得するjsonは以下

{
  "name": "kabaya",
  "job": "engineer"
}

そこでこのjsonに対応するモデルを作成し、retorofitがAPIレスポンスの結果を代入する。

// MainActivity.kt
data class ApiResponse(
        val name: String,
        val job: String
)

retrofitのセットアップ

retrofit自体は、jsonのパースをする機能はないため、ここでは有名なGsonというjsonをパースするライブラリを利用する。
GsonConverterFactory.create()が、retrofit用のGsonだ。
よくあるBuilderパターンでビルドした後createメソッドに後述するretorofitのinterfaceを渡す。

val api = Retrofit.Builder()
        .addConverterFactory(GsonConverterFactory.create())
        .baseUrl("https://gist.githubusercontent.com/t-kabaya/")
        .build().create(ApiInterface::class.java)

retorofitのセットアップは一行で完成した。

interfaceを定義する

interfaceを定義する。
このinterface内に関数とその戻り値を定義しておく。

interface ApiInterface {
    @GET("0616752fd86043f79f8a61e93f25d022/raw/a129469a6895c7e95fa0511efcf4c4f012ef0e0e/helloRetrofit")
    suspend fun getUser(): ApiResponse
}

coroutineを作成する。

onCreateの中に以下の2行を書くことで、非同期処理を行うためのcoroutineを作成出来る。
lifecycleScopeは、Activity, Fragmentなどlifecycleがあるクラスで使用可能なcorutineのためのスコープだ。
lifecycleが破棄される時に、lifecycleScopeも同時に破棄される。
他にもいろいろな種類のScopeがあるので、適切に選択する必要がある。
今回はonCreate内部でcoroutineを作成するのでlifecycleScopeを使用する。


lifecycleScope.launch {
}

suspend関数

suspend関数は、coroutineまたは他のsuspend関数内でのみ使用可能な関数で、suspend関数内部で発生する非同期な処理が終了するのを待ってから、次の処理へ移る。
つまり、suspend関数内部でhttpリクエストを行えば、リクエストの結果が帰ってくるまで待ち、その後処理を進めることが可能だ。

先ほどインターフェースで定義したgetUserをここで使用する。

suspend fun getRequest(): ApiResponse? {
    try {
        val response: ApiResponse = api.getUser()
        return response
    } catch (e: Exception) {
        return null
    }
}

corutineの中でsuspend関数を実行

suspend関数は、corutineの中か、他のsuspend関数の中でしか使用できないことを思い出してほしい。
先ほど作成したcorutineの中でgetRequsetを呼ぶ。

非同期処理を、同期的に書くことに成功した。


lifecycleScope.launch {
    val response = getRequest()
    Log.d("response ", response.toString())
}

完成したコード

package com.example.helloworld

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.GET

val api = Retrofit.Builder()
        .addConverterFactory(GsonConverterFactory.create())
        .baseUrl("https://gist.githubusercontent.com/t-kabaya/")
        .build().create(ApiInterface::class.java)

data class ApiResponse(
        val name: String,
        val job: String
)

interface ApiInterface {
    @GET("0616752fd86043f79f8a61e93f25d022/raw/a129469a6895c7e95fa0511efcf4c4f012ef0e0e/helloRetrofit")
    suspend fun getUser(): ApiResponse
}

suspend fun getRequest(): ApiResponse? {
    try {
        val response: ApiResponse = api.getUser()
        return response
    } catch (e: Exception) {
        return null
    }
}

class MainActivity : AppCompatActivity() {

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

        lifecycleScope.launch {
            val response = getRequest()
            Log.d("response ", response.toString()) // ApiResponse(name=kabaya, job=engineer)
        }
    }
}

お願い

いいねください。

25
15
1

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
25
15