はじめに
Kotlin 1.3がリリースされ、coroutinesが正式版になりました。
Kotlin 1.3にバージョンを上げるとKotlin 1.2系で動いていたcoroutinesの処理が動かなくなってしまったため、少しコードの修正が必要になりました。
2020年8月17日にKotlin 1.4がリリースされました
過去に投稿した
AndroidでKotlinのcoroutine(Async, Await)を使ってサクッとHTTP通信(非同期処理)を行う
をKotlin 1.3 1.4でも動くように修正しました。
(前回の記事と内容はそこまで変わらないです。このスニペットの背景などは上記ページを参照ください)
2019年2月16日更新
ライブラリのバージョンを更新しました。
サンプルコードを公開しました。
https://github.com/jonghyo/android-http-async-await
2019年3月27日更新
Okhttp3のバージョンが3.13以降の場合
Javaの compileOptionsでJava 8機能を有効にしないとエラーが発生するので修正しました。
@kk2170さん、ありがとうございます!
2019年4月18日更新
Kotlin1.3.30がリリースされたので、Kotlinのバージョンを修正しました。
2019年6月2日更新
GlobalScope.launch(Dispatchers.Main)
をむやみに利用することはアンチパターンとされています。Kotlin Coroutineを使う際のスコープに関して触れたセクションを追加しました。
@tetsu0831 さん、ありがとうござます!
2019年7月14日更新
Kotlin1.3.40がリリースされたので、Kotlinのバージョンを修正しました。
また、kotlinx-coroutinesのバージョンを上げました。
2019年8月24日更新
Kotlin1.3.50がリリースされたので、Kotlinのバージョンを修正しました。
また、kotlinx-coroutines,OKHttp3のバージョンを上げました。
Kotlin 1.3.50の内容をまとめたのでもしよろしければ読んでください!
Kotlin 1.3.50がリリースされました!!
2020年3月7日更新
Kotlin1.3.70がリリースされたので、Kotlinのバージョンを修正しました。
また、kotlinx-coroutines,OKHttp3のバージョンを上げました。
2020年8月7日更新
livedoor Weather APIが2020年7月31日をもって提供終了となったため本記事のLGTM!を表示するサンプルに変更しました。GitHubのサンプルコードも修正しました。
@yuki-kamikita さん、ご指摘ありがとうございます!
2020年8月17日更新
Kotlin1.4がリリースされたので、Kotlinのバージョンを修正しました。
また、kotlinx-coroutinesのバージョンを上げました。
事前準備
Gradle設定
使用するライブラリの依存解決のため、以下の記述をappのbuild.gradleに追加します。
Kotlin coroutine用ライブラリは2020年8月17日時点で最新の1.3.9を利用します。
dependencies {
implementation 'com.squareup.okhttp3:okhttp:4.4.0' //http通信ライブラリ
implementation 'com.eclipsesource.minimal-json:minimal-json:0.9.5' //jsonパースライブラリ
def coroutines_version = '1.3.9' //Kotlin coroutines用ライブラリ(async, await)のバージョン
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version" //Kotlin coroutines用ライブラリ(async, await)
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" //Kotlin coroutines用ライブラリ(async, await)
}
Okhttp3のバージョンが3.13以降の場合、 compileOptionsでJava 8の機能を有効化する必要があります。
詳細は、 @kk2170 さんの記事にて解説されています。
ありがとうございます!
OkHttp3をAndroidで使おうとしたときにjava.lang.ClassCastException: Bootstrap methaod returned nullが発生して初期化に失敗する
android {
//compileOptions以前の記述は省略
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
使用するKotlinのバージョン変更
プロジェクトのbuild.gradleで、Kotlin coroutineを使うためにKotlin1.4にバージョンを変更します。
ext.kotlin_version = '1.4.0'
HTTP通信処理を書く
今回はHTTP GETを行うメソッドを定義しました。
関数の呼び出し元でasyncなどを使うことが推奨されているようなので少し修正しました。
object HttpClient {
//OKHttp3はシングルトンで使う
val instance = OkHttpClient()
}
参考
KotlinでRetrofit2/OkHttp3を使ってXMLを取得する
class HttpUtil {
fun httpGet(url : String): String? {
val request = Request.Builder()
.url(url)
.build()
val response = HttpClient.instance.newCall(request).execute()
val body = response.body?.string()
return body
}
}
OkHttp 4.0.0以降はresponse.bodyの書き方が変わったみたいです。
Getting an error Using 'body(): ResponseBody?' is an error. moved to val with okhttp
ライブラリのimport文の修正
coroutines関連がKotlin1.3よりexperimentalではなくなったので修正します。
※こちらはKotlin 1.2系から1.3系以降にバージョンアップする場合に必要です。
import kotlin.coroutines.experimental,Dispatchers
import kkotlin.coroutines.experimental.GlobalScope
import kotlin.coroutines.experimental.async
import kotlin.coroutines.experimental.launch
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
HTTP GETの結果を用いてUIを更新する
今回はボタンをクリックすると、HTTP GETの処理が走り、結果をTextViewに反映する処理を書きました。
class MainActivity : AppCompatActivity() {
val URL = "https://qiita.com/api/v2/items/bf3e4e06022eebe8e3eb" //サンプルとしてQiitaのAPIサービスを利用します
var result = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val getButton = findViewById(R.id.button) as Button
getButton.setOnClickListener(object : View.OnClickListener {
override
fun onClick(view: View) {
onParallelGetButtonClick()
}
})
}
//非同期処理でHTTP GETを実行します。
fun onParallelGetButtonClick() = GlobalScope.launch(Dispatchers.Main) {
val http = HttpUtil()
//Mainスレッドでネットワーク関連処理を実行するとエラーになるためBackgroundで実行
async(Dispatchers.Default) { http.httpGet(URL) }.await().let {
//minimal-jsonを使って jsonをパース
val result = Json.parse(it).asObject()
val textView = findViewById(R.id.text) as TextView
textView.setText(result.get("likes_count").asInt().toString() + "LGTM!")
}
}
Kotlin Coroutineスコープについて(2019年6月2日追記)
KotlinでCoroutineを利用する場合、Coroutineのスコープについてケアする必要があります。
GlobalScope.launch(Dispatchers.Main)
のようにGlobalScopeを目的もなく使うのはKotlin Coroutinesのアンチパターンとされています。
Kotlin Coroutines patterns & anti-patterns
上記を和訳、解説した @ikemura23さんの記事
Kotlin Coroutinesパターン&アンチパターン
GlobalScopeはアプリケーションの有効期間全体にわたって動作するため
GlobalScopeを同一アプリ内で乱用すると不都合が発生したり
Androidのアプリケーションライフサイクルとの間で問題が発生する可能性もあります。
単に、REST APIを叩く検証するだけであればrunBlocking
などの利用も検討すると良いでしょう。
また、プロダクションコードではアプリケーション定義の
CoroutineScope
を利用することがベターだとされています。
@tetsu0831 さん、ご指摘ありがとうござます!
結果
HTTP GETを非同期処理で実行し、取得したjsonをTextViewに反映できました。
所感
Kotlin1.2系のcoroutines処理のままでは、Kotlin1.3系では動きませんでしたがそこまで大幅な修正は必要なさそうでした。
Kotlin 1.3系で正式採用されたcoroutinesもこなれてきた感がありますね。
Kotlin 1.4ではCoroutine Debuggerも実装されより便利になったみたいです!
Kotlin 1.4についての記事も書きたいな。
最後までお読み頂きありがとうございました!
Twitterでも技術ネタやデザインについてなどもツイートしているので、よかったらフォローしてもらえると嬉しいです
→ Twitter@jonghyo_