初めまして。Studyplus開発部の若宮(id:D_R_1009) です。9月よりAndroidアプリの開発を行っています。
Kotlin、いいですよね。Kotlin Coroutines、シビれますよね。
ということで、Kotlin1.3がリリースされたことを記念してStudyplus-Android-SDKにKotlin Coroutinesを早速導入した話をします。
Studyplus-Android-SDKとは
Studyplus APIの詳細はこちらをご確認ください。
Studyplus-Android-SDKはStudyplus APIを簡単にご利用いただくためのSDKです。社内のAndroidチームメンバーにより開発・メンテナンスされています。
2018年の8月にフルスクラッチを行い1系から2系にバージョンが大きく上がりました。なお、2系からKotlinを利用しています。
Githubリポジトリ : https://github.com/studyplus/Studyplus-Android-SDK-V2
SDKは次の2つの機能を提供しています。
- Studyplus APIによる学習記録の投稿
- Studyplus AndroidアプリによるStudyplusとの連携
この学習記録の投稿処理をRxJava/RxAndroidからKotlin Coroutinesへ切り替えたv2.1.0
系のリリースを、2018年10月31日に行いました。今回はこのRxJava/RxAndroidをKotlin Corutinesへ置き換えた経緯、置き換えの所感について書いていきたいと思います。
なぜRxJava/RxAndroidをやめてKotlin Coroutinesへ置き換えたのか
stable版がリリースされて嬉しかった
開発チーム内で上がっていた「RxJava/RxAndroidの利用は色々と過剰なのでは」という疑念を解消するためになります。
疑念1 : 利用シーンに対して
SDK内で行う通信処理は、主にサーバーのStudyplus APIを叩き学習記録を投稿する、というシンプルなものになります。そのため、本来Streamを扱うためのRxJavaであるのにOne-Shotな処理を行うに止まっていました。
SDKの基本機能としては現在のSDKでほぼ要求は満たされていると考えており、これ以上Streamを必要とする処理が追加される見込みもありません。
疑念2: SDKが持つ依存関係に対して
SDKを使っていただくデベロッパーの視点に立った時、RxJava/RxAndroidへの依存をSDK側で作成してしまうことに対してセンシティブになる必要があると思っています。
もちろんRxJava/RxAndroidは多くのプロダクトで利用されており、近年では当たり前な選択肢になっています。しかし、Androidの標準ライブラリに組み込まれるには至っていません。
Kotlin Coroutinesでは
Coroutinsのasync/awaitは1回の通信処理に対して1つのCoroutinesとなるため、処理と実装が必要十分な関係性になります。その意味で、SDK内の処理を整理し非同期処理をasync/awaitに統一することは、非常に理にかなった行為であると判断しました。
依存関係については少しチーム内で議論するところなり、はっきりとは解決しませんでした。
ご存知の通りKotlin Coroutinesの導入にはGradle上で依存関係を追加する必要があります。その点において、RxJava/RxAndroidを利用している問題は解決していません。
現時点での結論としては、SDK v2ではKotlinを記述言語として採用していることを重視しました。
RxJva/RxAndroid(+RxKotlin)を利用するよりもKotlin Coroutinesを利用する方が、はるかに依存関係がシンプルであると判断しています。
RxJava/RxAndroidとRxKotlinの比較
RxJava
// API通信 internal interface ApiService { @Headers(value = [ "Accept: application/json", "Content-type: application/json" ]) @POST("/v1/study_records") fun postStudyRecords( @Header("Authorization") oauth: String, @Body studyRecord: StudyRecord) : Observable<PostStudyRecordsResponse> } ~~~ // 投稿処理 fun postRecord(context: Context, studyRecord: StudyRecord, listener: OnPostRecordListener?) { if (!isAuthenticated(context)) { throw IllegalStateException("Please check your application's authentication before this method call.") } ApiClient.apiClient.postStudyRecords(context, studyRecord) .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( { listener?.onResult(success = true, recordId = it.recordId) }, { listener?.onResult(success = false, throwable = it) } ) }
Kotlin Coroutines
// API通信 internal interface ApiService { @Headers(value = [ "Accept: application/json", "Content-type: application/json" ]) @POST("/v1/study_records") fun postStudyRecords( @Header("Authorization") oauth: String, @Body studyRecord: StudyRecord ): Deferred<PostStudyRecordsResponse> } ~~~ // 投稿処理 fun postRecord(context: Context, studyRecord: StudyRecord, listener: OnPostRecordListener?) { if (!isAuthenticated(context)) { throw IllegalStateException("Please check your application's authentication before this method call.") } runBlocking { try { val deferred = ApiClient.postStudyRecords(context, studyRecord) val result = deferred.await() listener?.onResult(success = true, recordId = result.recordId) } catch (t: Throwable) { listener?.onResult(success = false, throwable = t) } } }
返りの型が変わるだけなので簡単ですね。Singleを使う程度の通信なら、書き換えのコストは非常に低く済みそうです。
まとめ
Coroutinesの導入
通信処理への導入は低コストであると実感しました。 そのためStudyplus Androidアプリへの導入を進めたいなと感じています。 特に新規に追加する機能はCoroutinesを積極的に導入をしようと話をしています。
ひとまず足がかりとして kotlinx-coroutines-rx2
の導入を着々と進めています。こちら、成果が出たら本ブログで公開いたします。
SDKの今後
より依存関係を減らし、軽量なライブラリを目指していきたいと思っています。
GsonやRetrofit、Retrofit-Kotlin-Coroutines-Adapterの依存関係は早期に解消していきます。
今回の置き換えにおいてはRetrofitによる簡潔なインタフェースがあったから迅速に対応できました。 しかしこれらの利便性と何を引き換えにしているか、常に検討しながら開発をしていくべきだとも感じる機会になりました。 今後とも、Studyplus-Android-SDKをよろしくお願いいたします。
最後に
一緒にKotlin Coroutinesを使って開発をしたい方はこちらからぜひご応募ください! 開発部一同、お会いできる機会を心よりお待ちしております。