https://droidkaigi.jp/2022/timetable/365129
コルーチンを使って処理の見通しをよくするリアルタイム放送の品質を向上し、保つためにhoritamon • 2022 年 10月 5 日 @DroidKaigi2022
View Slide
自己紹介堀 多聞 - horitamonAndroidエンジニア5年目2020/9〜 Voicy品質改善チームTwitter: @horitamon
音声プラットフォーム - Voicyさまざまなパーソナリティのトークを楽しめるサービスを提供
Voicyのアプリ聴くアプリ「Voicy」と収録するアプリ「Voicy Recorder」ふたつをAndroid/iOSともに展開
こんなことで困ったこと、ありませんか?
ぱっと見何してるのかわからん😇(追えばわかるけども)
コールバック内コールバック
帰らぬ人となったメソッド分割呼び元のメソッドの役割が不明瞭(行数増えると分けがちだけど)
LiveDataでUIへ処理完了を通知これすなわち呼び元のメソッドが分断されているということ
ぱっと見startLiveの結果がisSucceededStartから来るとはわからない🤔
コードの見通しが悪くて不具合の原因がわからない…!● エラーログが無いので読むしかない…● コールバックやメソッド分割でどの処理がどの順番で進むのかわかりづらい…● 原因が見つからないし、直すにしてもどこを直せば…→不具合の解決に時間がかかる
そんなときに、コルーチン
コルーチン=スレッド操作だけじゃない!● コルーチンってスレッド操作をやりやすくするんじゃないの?→それだけじゃない!● コールバックを減らせるという可読性の面での利点がある
今日伝えること1. コルーチンの「中断」2. コルーチンでコードの見通しを良くする3. リアルタイム放送の品質を向上し保つためにしたこと
コルーチンとは
コルーチンとはざっくり言うと並行実行のデザインパターンスレッド切り替え、バックグラウンド処理といった非同期実行するコードを簡略化できる全体をつかむには過去のDroidKaigiでとてもわかりやすい発表があります Understanding Kotlin Coroutines:コルーチンで進化するアプリケーション開発 @mhidaka
https://developer.android.com/kotlin/coroutines/coroutines-adv?hl=jaloginをメインスレッドから呼ぶとmakeLoginRequestが完了するまでスレッドをブロックしてしまう🤔
https://developer.android.com/kotlin/coroutines/coroutines-adv?hl=ja💡処理を簡単にI/Oスレッドへ移せる
コルーチンの中断
https://developer.android.com/kotlin/coroutines/coroutines-adv?hl=jaそもそもスレッドをブロックしないでほしい😠
https://developer.android.com/kotlin/coroutines/coroutines-adv?hl=ja💡makeLoginRequestを suspend関数にする 呼び元のコルーチンを中断し 結果が出たら再開する
💡止めるのではなく中断する→結果が返ってくるまではメインスレッドが使えるMainI/OSystem CoroutinelaunchCoroutinewithContextCoroutinereturnsuspend
https://developer.android.com/kotlin/coroutines/coroutines-adv?hl=ja💡makeLoginRequestはsuspend関数 コルーチンを中断し 結果が出たら再開する
https://developer.android.com/kotlin/coroutines/coroutines-adv?hl=ja💡実行スレッドをブロックせず 中断するだけ メインセーフティ✨今回このコルーチンの「中断」が活躍します
コルーチンでコードの見通しを良くする
例:生放送Voicyが提供するリアルタイム放送機能リスナーからのお便りに答えたりリスナーにゲストとしてトークに参加してもらったりリアルタイムな交流が可能
問題のコードをコルーチンで書き換える※もちろん擬似
コールバックで結果を受け取っているLiveRepository#getByLiveIdをコルーチンを使って表してみる
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines/suspend-coroutine.html💡コルーチンを「中断」しつつ 「継続」するためのオブジェクトを提供する
suspendCoroutineもsuspend関数→呼び元のコルーチンを中断
結果を返してコルーチンを再開
💡同期処理っぽい書き方に→コールバックが減らせる
💡やってることが一目でわかる● 生放送情報の取得● 生放送データを開始状態にする● 画像URL取得
開始処理の結果がまだちょっと追いづらい…
成功/失敗をBooleanで返すメソッドに
💡生放送の開始処理の成功/失敗を 返すメソッドになった
💡見通しUP!・生放送の開始処理の成功/失敗を 返すメソッドであることがわかる・処理の順番がすぐわかる
💡見通しUP! startLiveの結果を受け取る箇所が すぐわかる
生放送が開始しない不具合が発生!↓エラーハンドリング追加までやってみる見やすくなったコードから問題点を解決していく
・setLiveStartedが完了しないと次へは進まない・liveがnullの場合もsetLiveStartedへ進んでしまう→止まるポイントがすぐわかる
liveがnullのときは失敗を返す
見通しが良くなると品質を落としているポイントがコードからわかるようになるさらに問題点を探してみる
この処理をしなくてもsetLiveStartedへ進むことは可能→余計な待ち時間がある
💡並列実行で 処理スピードUp
リアルタイム放送の品質を向上し保つためにしたこと
VoicyのQualityチーム既存機能の品質向上を担う● 2022年から開発チーム編成を変更● 変更前のチームでリリースした機能の保守責任が曖昧に● 2021年リリースの生放送で障害発生● 対応の流れでQualityチームが発足● リリース済み機能の不具合対応やユーザーからの問い合わせに対応
Voicy server音声通信・放送データ・お便りイベント通信・放送開始・データ更新3つの通信経路を同時性を保って管理生放送の構成
見通しが悪くてどこが問題なのかなかなかわからない…!
生放送を開始する流れ1. 放送データを「予約中」から「放送中」に更新2. 生放送開始イベントを通知3. 音声を接続する4. 放送データの取得、更新○ 放送データを作成○ 自身のユーザーデータ取得○ アイコン画像取得○ お便りブロックユーザー一覧取得etc…順番は…?失敗したときは…? よ〜〜〜く読まないと把握できない😇
そもそもやってることが複雑● 3つの双方向通信● 通信の同時性を保つ● たくさんのセットアップ処理開発していくうちにさらにどんどん複雑に● 技術調査、実装、仕様調整を繰り返す● 複数人で同時並行で開発するなぜ見通しが悪いコードに…?
すぐ直すのではなく一度立ち止まってでもコードの見通しを良くする● このままだと調査も修正も時間がかかる● よくわからないまま修正してさらに不具合を生むリスク→問題を解決し 問題を生まないために まず見通しを良くする
リファクタで価値をストレートに届けるリファクタは直接的な解決策ではないしかし不具合が減らしやすく増えにくくなるユーザーが感じる品質課題が減る→価値ある機能が ストレートに届くようになる
コルーチンで非同期処理を同期的に書き直すとコードの見通しが良くなる● 処理の順番が追いやすくなり、全体の見通しが良くなる● 不具合原因を見つけやすくなりコードからさらなる課題を見つけやすくなるリファクタはユーザーにとっても価値がある● 不具合解決のスピードが上がり、リスクも減る● 価値ある機能がストレートに届くまとめ
参考● Understanding Kotlin Coroutines:コルーチンで進化するアプリケーション開発 @mhidaka ● Using Kotlin Coroutines in your Android App ● Android での Kotlin コルーチン | Android Developers ● Coroutines guide | Kotlin
Voicy のエンジニアがテックや開発組織についていろいろ語っていますDroidKaigi登壇メンバーのアフタートークも予定!voi-chord配信中!
Voicyで音声の未来を切り開く仲間を募集中!対面でも、Twitter DMでも詳しくお話しします @horitamon
Thank you!