Radiotalk Tech Blog

Radiotalk株式会社のエンジニアが知見や取りくみについてを共有するテックブログです。

DroidKaigi 2021にRadiotalkメンバーが登壇します

DroidKaigi 2021にRadiotalkからAndroidエンジニアの牧山 (@_rmakiyama) が登壇します!
1日目の10月19日 10:40から「Androidエンジニアが1人という不安と向き合う」というタイトルで行います。

f:id:radiotalk-tech:20211013155418j:plain

DroidKaigi 2021

開催日時 : 2021年10月19日(火)、20日(水)、21日(木)

開催場所 : オンライン開催

droidkaigi.jp

セッション内容

Androidエンジニア不足が叫ばれる昨今、1つのAndroidアプリに対して1人のAndroidエンジニアという状況が増えてきているように感じています。このような状況下では、スキル面・キャアリア面での不安を持つ方も多いのではないでしょうか。牧山のキャリアとしても、ほとんどがチームにAndroidエンジニアが1人という経歴です。
本セッションでは、その経験を踏まえ、牧山自身が抱えてきた不安を元に、その不安に向き合うためにやったこと・やっていることについて話します。

▼ アジェンダ

  • 技術の学び方を身につける
  • 優れたソースコードを読む
  • ソフトウェア開発を学ぶ
  • サービスへの理解を深める
  • エンジニアリングでサービスを前に進める

▼ 牧山からひとこと

抱える不安については人それぞれあります。ひとつの事例として聞いていただき、みなさん自身の抱える不安と向き合うきっかけになれれば幸いです!
当日はオフィスアワーもありますので、お時間ある方は気軽に参加いただき質問も気軽に投げてください!

宣伝

RadiotalkではAndroidアプリエンジニアを募集しております!
もしちょっとでも興味がある人は下記のページからご応募いただくか、Twitter等のSNSでお声かけいただければと思います!

www.wantedly.com

カジュアルな雑談も歓迎していますのでお気軽にぜひ! 🙌🏻

meety.net

【レポート】iOSエンジニア4社合同! LT会+パネルディスカッション(パラレル/ミラティブ/ヤプリ/Radiotalk)

この記事は2021年7月29日(木)に開催された、Radiotalk TechTalk vol.5「【耳だけ参加OK!覗き見OK!】iOSエンジニア 4社合同LT会」の後日レポートです。

f:id:radiotalk-tech:20210930123531p:plain
4社合同LT会 パラレル・ミラティブ・ヤプリ・RadiotalkのiOSエンジニアが語る!

登壇者

1.パラレル株式会社 Software Engineer 石川 直樹 氏
2.株式会社ミラティブ クライアントチーム マネージャー 千吉良 成紀 氏
3.株式会社ヤプリ iOSエンジニア 岸川 克己 氏
4.Radiotalk株式会社 iOSエンジニア 山岸 央青
(本文敬称略)

モデレーター

Radiotalk株式会社 Androidエンジニア 牧山 瞭

パネルディスカッションテーマ

1.Xcode 13 / iOS 15 / Swift 5で気になること
2.SwiftUIとの向き合い方
3.新しい技術をサービスにどういう判断で導入するか、また導入時の流れ
※進行順

LT

前半では登壇者が各自準備したLTを発表しました。記事ではスライドのみ展開します。

1.パラレル株式会社 Software Engineer 石川 直樹 氏

2.株式会社ミラティブ クライアントチーム マネージャー 千吉良 成紀 氏

3.株式会社ヤプリ iOSエンジニア 岸川 克己 氏

4.Radiotalk株式会社 iOSエンジニア 山岸 央青

パネルディスカッション

当日はそれぞれのLTについて視聴者からいただいたコメントや気になる点、興味深い点などを率直に語りあったのち、パネルディスカッションに移りました。あらかじめ登壇者から挙げてもらったテーマから、ピックアップされたものを中心にディスカッションしていきます。ここではトークの一部を抜粋してご紹介します。

1.Xcode 13 / iOS 15 / Swift 5で気になること

山岸:iOS15で気になるのはSharePlayですね。どこまでサービスとして提供するのか。動画だったら「友達と一緒に見る」といった使い方が想像できるんですが。

石川:Appleもついにこういうところを目指しているんだな、というのは共感できました。考えられるユースケースとしては、Apple(TV)のムービーだったりとか、Appleのアプリ上で提供されているコンテンツをFaceTime経由で共有してもらうといったところがメインなのかなと思っています。ただ、コンテンツを一緒に、いきなり見るのって、誘うのも含めてなかなかハードルが高いですよね。

山岸:FaceTime自体が(相手の)電話番号を必要としますよね。結構身近な人でないとやらないんじゃないかな、とは思うんですが。 Appleが毎回プレゼンしているほど、FaceTime、そんなふうに使われていないぞって(笑)いや、使われてるのかな? 身近な人では!

岸川:アメリカだと違うかもしれないですね。iMessageとかすごい使われているし。

山岸:あとは、通知まわりですかね。iOS15といえば。

岸川:通知については、基本的にメンション以外の通知はするなというか、してもOSに勝手に止められる、みたいな流れになっていますね。まぁ、使ってる分には、妥当だなっていうところはありますね。仕事の話でいうと、通知がこういう仕様になるんで、何か個別のユーザーにかかわるお知らせとかだったらまだしも、会社からの一斉お知らせを送るのは無駄だし、そもそも誰も見ていないよっていう感じですよね。

千吉良:いい流れだとは思います。ミラティブの場合はプッシュ通知を介して配信中にコミュニケーションを取っている部分があるので、ユーザーが知らないうちに通知をまとめる設定とかにしていたりすると通知がユーザーの目に触れないことになるので、ユーザーに対するコミュニケーションは必要なのかなと思いますね。

山岸:サービス側としても「こうしてね」と、ユーザーに案内する文言や工夫が必要ですね。どうしたらうまく(通知が)届くんだとか、実装においては考えることが多くなりますね。

岸川:@(アットマークメンション)的な、「この人に向けて(の通知)」っていうのが自動的に、機械的にわかるような補足を上手いこと付けるとか、いいんじゃないですかね。

石川:そこらへんのプロダクティビティみたいなこと、すごい重視してますね。フォーカスモードみたいなのが出たり。(こうした流れは、ユーザーファーストの観点から見ると)いいとは思います。

あとは、Xcode Cloudが気になりますね。

岸川:Xcode Cloud便利ですよ。全体的にクセは強いんだけど、少なくとも最初のセットアップさえ乗り切ることができれば、すごい楽だと思います。「TestFlight」の(β版アプリ)配信も自動的に対応してくれるし。

石川:ベータ版への対応も早いじゃないですか。マシンスペックとかは公開されてないですかね?

岸川:されていないですね。もしかしたら、カスタムスクリプトとかでuname(カーネル情報コマンド)とか実行すれば出てくるかもしれないけど。まぁ、どうせVM(仮想マシン)だろうしね、っていうところがあるのと、そんなに超絶技巧みたいなことはたぶんなさそう。

ちょっと(ドキュメント)読んでると、「クリーンする」チェックマークがあって、(スマホアプリの)GUIではあまりクリーンってそんなにしないよね、って思って。「必要ないよ」と思って眺めていると、なんかXcode CloudはDerived Data(デライブド・データ:自動補完データやログ、デバッグビルド、中間ファイルなど)を積極的にキャッシュしている、というようなことが書いてあって。

もしかしてこれ、状態継続するの? みたいなのをちょっと思って…… そこは気になりますね。

石川:キャッシュとかも全然ないんですかね?

岸川:ないけど、Derived Dataをずっと結構持っているから、クリーンするときはちゃんと(明示的に)クリーン(しなければいけない)みたいな感じで書いてあったから、謎だなと思って。環境は使い捨てで、(状態に関するデータは)キャッシュでやりくりするのがセオリーだと思うんですけど、どうなのかなって感じですね。

石川:(そのあたりの仕組みは)あまり公開されなさそうですよね。

岸川:他のCIサービスは、カスタム(ビルド)ステップとかも全部オープンソースだから。Bitrise(ビットライズ:CIツール)が「全然余裕ですよ」みたいなこと言ってますけど、あのへんはたぶんそういう感覚があるんだと思いますね。やっぱり一日の長はあるし、最後発にしては微妙にわからない(ノウハウがありそうな?)感じになってる部分があるから……。

主なオートメーションみたいなところはやはり、BitriseなりGitHub Actions(GitHubのCIツール)なりを使いつづけるかなっていうところはあるかなと。とくにGitHub Actionsに関してはすごい簡単ですしね。そのへんのアドバンテージは変わらなそうな気がします。

山岸:Swift5.5だと、async/awaitとかActorとかですかね。

岸川:そこだと思いますね。ただ、バックポート(新バージョンの機能が旧バージョンにもさかのぼって適用)されるのかどうかっていうところですね。

石川:(開発者コミュニティでは)議論中なんですかね?

岸川:そうですね、(開発者コミュニティの議論)スレッドがちょっと加熱しすぎて、いったんロック(凍結)にはなってしまいましたけど。作ってる人は、さすがにバックポートしたいとは思ってると思うんですよね。そうしないとそもそも使う機会が全然ないんですよね、普通に考えて。他のAPIといっしょに考えると、早くて1年後みたいな話になっちゃうんで。

async/awaitは他の言語ではポピュラーとはいえ、SwiftもOSSとしては先進的なところがあるから、どんどん使って慣れていかないというところがすごいあるんで。「(実装が)アナウンスされたけど、(実際に使えるのは)1年後だよね」みたいなスタンスだと厳しいと思うんですよね。これに関しては。

2.SwiftUIとの向き合い方

山岸:それでいうとSwiftUIとかも使いたいけど使えない企業とかも、結構あるじゃないですか。

岸川:SwiftUIは少しずつバックポートされてる部分があるっぽいんですよ。SwiftUI関連のニュースとか見ていると、「最新(バージョン)じゃなくても、ここから(ある程度前のバージョンから)使えるよ」というのが多くて。

だから、async/awaitも、そのあたりは期待できるんじゃないかという気がしています。

山岸:ミラティブは、(iOSの対応バージョンが)13.6以降じゃないですか。SwiftUI、使ってるんですか?

千吉良:いまはウィジェットだけですね。(プロダクト)本体にはまだちょっと先かなと思っていて。

設計はいま、ReactorKitっていうのを使っているんですけど、本来はServiceというレイヤーがあって、ユースケース的なものをキレイにまとめられるんです。ホントにキレイに分離されている状態だったら、SwiftUIでUIのガワだけ導入してみて、みたいなことがもしかしたら出来るかなという気もしているんですけど…。

いまはそこまで整理されていないし、アプリに関わる人数が意外に多いのもあって、中途半端に導入するとソースコードをいじる人たちが混乱しちゃったりするんで、下準備の期間は必要かなというステータスですね。

山岸:パラレルはどうですか?

石川:工数がないっていうのもありますが、まず着手するなら、Modelだったり、いまのPresentorから、Combine化みたいなのはできるかなと思っています。

いまXIBとかStoryboardで画面遷移の管理をやっているのを、全部SwiftUIに置き換えられるということは多分しないかな、と思います。やるなら、1から作りたいですね。そのほうがたぶん早いと思います。

岸川:SwiftUIは未来のUIフレームワークであることは間違いないと思います。正しい道、正しい未来を継ぐものだとは思っていて。

最近の完成度を見ていると、iOS15から新しく作るアプリで、iOS15以降のみ(対応)で行くのであれば、そろそろ行けるんじゃないのという気がしています。これまでは「SwiftUIへの対応は無理だから」と言っていたんですけど……。

どっちかというと、UIツールキットとしての補足部分といったところですよね。インタラクションのある部分とか、画面遷移のジェスチャーで右から左からスワイプするとか、画面の外から次の画面を引っ張ってくるみたいなやつを作るのは結構苦手というか。逆にすごいややこしくなるので。

あとは、トランジションとか、そういう操作に対応する機能がないので。プッシュ通知、モーダル表示、あとシート表示をSwiftUIで実装しようと思ったら行き詰まるっていうのがありますね。

山岸:遷移まわりがちょっとむずかしいですね。

岸川:凝った演出的なトランジション入れようと思ったら、本当に難しい。 まぁ、SPA(Single Page Application:単一ページのアプリケーション)向けですよね。画面にいったんすべてのパーツを置いておいて、すでにあるものが消えたり点いたりするっていう。

そもそもSPA向けの手法だと思って、最初に全部パーツを置いておいて表示/非表示を切り替えるっていう感じでやっていくのが正解なのかもしれないですね。

山岸:画面の一部分でも使われているものはありますよね。ヤフーとかクックパッドとか、結構エンジニアがテック記事を出していますが。

岸川:SwiftUIにアドバンテージを感じている部分があると思うんですよね。おそらく、Xcode Previewsですよね。Previewsとの連携がすごく強力っていうのはあるので。

PreviewsはUIKitでもだいたい問題なく動くから、Previewsのほうを上手く使っていければ、SwiftUIでは細かいボタンとかシェイプとかの記述は本当にラクだから。そのほうがトータルで書きやすそう、というところはSwiftUIを使っていく、というのは、いままでのバージョンのiOSでのやり方の延長線上ですよね。

iOS15からは、もしかしたらSwiftUIファーストでも行けるんじゃないかという気は若干しています。

石川:いずれはWidgetみたいにSwiftUIオンリーにならないといけないような感じになるかもしれないし……。

岸川:iOS15以降もUIKitによる実装を残すかは微妙なところですね。ただ、ツールキットとして考えると、UIKitはすごいので。もっとわかりやすく考えて、Macアプリを作る場合にはSwiftUIはオススメでいいかなと思いますね。AppKitはちょっと難しいのと、調べても情報が結構出てこないので。

Macアプリはややこしくって。Mac Catalystがあるんですよね。Mac CatalystはiOSアプリを作っている人にはいいけど、だいぶややこしいので。それならSwiftUIでいいんじゃない? とは思いますね。

山岸:あとは、iPadOS15からiPad上でもXcodeが動作するようになり、iPadだけでアプリを作れるようになるじゃないですか。誰か、iPadからアプリを出したりしないですかね。そういう勇者はいないですかね。(※Appleの発表では2021年秋対応予定)

岸川:素晴らしいですよね。何がいいかっていうと、やっぱりiPadから直接アプリをディストリビューションできるっていうことですね。Playgroundで作成するプログラムはもはやオモチャではないので。普通にAppStoreで動作するホンモノを、iPadだけで作れるっていう。

3.新しい技術をサービスにどういう判断で導入するか、また導入時の流れ

牧山:せっかくなんで、SwiftUIとか新しい技術が出ているなかで、サービスによっても取り入れようという判断基準とか意識って違うと思うんですけど、それぞれの会社でどう判断しているのか、導入の流れみたいなのをちょっと聞いてみたいです。

石川:会社によって結構違うかな、というのはおっしゃる通りで。前職では開発者にフレームワークを提供するかもしれないという局面になったことがあるので、なるべく依存するOSSが無いように開発したりはしていました。自作できるくらい人数も多かったもあります。

パラレルでは僕しかいないので、なるべく工数をかけないように、UIとかでもOSSを使ったりというのはありますね。環境によりけりって感じです。

千吉良:ミラティブの場合は、たとえばウィジェットみたいなケースだと、とりあえず適当なサンプルを作って「作ってみました」みたいな感じでPMやデザイナーに見てもらって、「あ、いいね」って仕様やデザイン決めて出しちゃう、という流れでリリースまでいきました。

SwiftUIとかを導入するか否かの判断は、普段のアプリの設計思想に結構関わってきて、実装する上で迷いが出ちゃったりする場面も出てくると思うので。こういう新技術に関しては、将来のことを考えて「いつにしようかな」みたいな感じの考えになることが多いですね。

岸川:基本的には「その人が使いたいのであれば、やればいい」と思います。だいたい、導入する技術を選べるっていう状況はそんなになくって、時間の制約とかがあるじゃないですか。

未知の技術を使ってなにかを作ると、当然予測しないことが起きますよね。でも既存の技術を使っても予測しないことというのは起こるので。そういうことがあると考えて、もろもろ困難をクリアして期日に間に合うのであれば全然大丈夫だと思いますし。

ただ、優先順位の大きなことが他にあるとか、まずは早く出してフィードバックを得ないとそもそもいけないという場合もあると思うので。そのへんを考えると、基本的なセオリーとしては「枯れている(使いこなされている)ものを使う」というのが普通の判断だと思います。

そのうえで、その人が本当にその技術が好きとか、興味関心があるとか、「予測不可能なことがあっても自分で調べて乗り越えられるし、それがモチベーションになります」とか。「もうUIKitとか正直ダルいとずっと思っていたんです」とか、そういうモチベーションは全然ありだと思います。

そういうエネルギーって、障害を乗り越える原動力になるので。そうやって、どんどん道を切り開いていく人は絶対必要なので。そうであるならば、どんどんやりましょうという感じでいいと思いますね。

牧山:探究心をもって、新しい技術にパッと飛び込んでくれる人も大事だったりしますもんね。

岸川:そうですね。逆にそういうのが無いなかで、よくわからないまま「とりあえず使ってたほうが人が集まるから、SwiftUIを使おう」みたいな考え方はどうかと思いますね。

山岸:千吉良さんが言ったように、「これいいね」みたいな感じで進めていく場合もありますし、岸川さんが言われたみたいに「これやりたいんだ」といって、先導してやっていくようなやり方とかがRadiotalkでは結構多いですけども。

やっぱりエンジニアでしか判断できなくて、「新しい技術だけど、これ入れたいな」という部分は、簡単なタスクを作って、若干工数を多めに申告して取り組んでいますね。

岸川:「ダメだったら、ダメだとあきらめる」というのはありますね。そのへんを柔軟にできるといいですね。

(了)


最後に視聴者の質問への回答もまじえながら、各社のプロダクトの印象や感想を伝えあい、会は和やかに終了しました。 登壇者の皆様、ご参加の皆様、学びの多い時間をありがとうございました! Radiotalk TechTalkでは、今後もエンジニア同士の勉強会、知見共有を通じて技術力の向上をめざしていきます。

Radiotalk Tech Talk Vol.1レポート「テーマ『Radiotalk』」

f:id:radiotalk-tech:20210518121213p:plain 毎回1つのテーマを定めて行う、Radiotalk主催のテックイベント「Radiotalk Tech Talk」。記念すべき第1回目では、「Radiotalk」をテーマに、Radiotalkが描くビジョン、そして実際にRadiotalkのサービスの基幹を形成するインフラ設計について、Radiotalk社員による発表を行いました。

(1)Radiotalkが実現するビジョン(Radiotalk 代表取締役 井上佳央里 @JD_KAORI

最初のセッションは、Radiotalk株式会社 代表取締役の井上佳央里より、「Radiotalkが実現するビジョン」。Radiotalkがサービスとして目指すビジョンをお話しました。(以降、各章ではスピーカーたちの発言要旨をまとめます) f:id:radiotalk-tech:20210518005129p:plain

トークをエンタメに 〜音声の力で「個の熱狂経済圏」を作る

Radiotalkが掲げるビジョンは「トークをエンタメにする」。人生を送るうえで欠かされることのない「会話」がエンタメになったら毎日がエンタメの宝庫になるのでは、という思いからサービスを運営しています。

f:id:radiotalk-tech:20210518005222p:plain メルカリやBASEなどを使って個人が自由に商品を売る「個人経済圏」が注目されているいま、Radiotalkは音声の強みである「熱狂度の高さ」に注目し、「熱狂経済圏」を作り上げようとしています。この「熱狂経済圏」を作り上げるビジネスモデルとして、Radiotalkが取り入れているのが、「ギフティング」。ライブ配信や収録配信においてリスナーから贈られる有料ギフトを通じ、「ラジオトーカー」と呼ばれる配信者たちが自らのトークを収益化しています。

f:id:radiotalk-tech:20210518005244p:plain

個人のストーリーや思想にファンが生まれ、経済が生まれていく 音声配信の魅力は「見た目とか年齢とか全然関係なく人気になれる」という点。これまで動画系のライブ配信では本筋とはされてこなった層の人々にも、脚光が当たってきているのです。 f:id:radiotalk-tech:20210518005302p:plain

その人が語るストーリーや言葉、思想にリスナーがどんどん引きつけられてファンになり、それに対して課金が発生していく──。こうした「人間的なボキャブラリーやパーソナリティが共感を呼び、応援される経済圏」をRadiotalkで作っていきたいと考えています。

(2)「インフラ環境が変更される可能性ってなんだかんだないよね?」「あるよ。」APIにおけるインフラ層の抽象化と実例(Radiotalk CTO 斉藤裕気 @gamu1012

続いては、Radiotalk株式会社 CTOの斉藤裕気による「APIにおけるインフラ層の抽象化と実例」。Radiotalkの基盤を支えるサーバサイドの設計における「抽象化」の取り組みについてお話しました。

f:id:radiotalk-tech:20210518005335p:plain

f:id:radiotalk-tech:20210518005344p:plain

サービスが変化し続ける時代に「抽象化」の概念は重要

サービス開発におけるエンジニアリングは、「完成品をリリースをして終わり」という時代から、ユーザーの反応や市場の反応を見ながら改善を継続し、ソフトウェアを進化させ続けていく、いわば「リリースがスタート」の時代へと突入しました。

いまやサービスはつねに変化し、それに応じて機能や実現方法も変化することが大前提となっています。そんななか、実装したサービスやソフトウェアを変化、改善し続けていくために強力な役割を果たす要素のひとつとして、今回は「抽象化」にフォーカスしてお話します。 f:id:radiotalk-tech:20210518005414p:plain

相次ぐインフラの変更に対応するための「抽象化」

Radiotalkのようなサービスにおいては、機能の変化に応じてユースケースや、使用するインフラの構造もめまぐるしく変化していきます。今回はこのインフラ部分に焦点を当ててお話をしたいと思います。

f:id:radiotalk-tech:20210518005438p:plain

サービス変化の過程では、必要に応じて裏側のインフラもまた変化していくことを想定しなければなりません。データベースがMySQLからPostgreSQLになる場合もありえるでしょうし、キャッシュがファイルベースからRedisになることもありえるでしょう。ファイルストレージがS3から別のクラウドストレージになることも今後十分にありえます。前職時代こそ「インフラの変更は本当に発生するのか?」と思いながら取り組んでいた抽象化ですが、Radiotalkに入社してからは、よりその必要性を実感するようになりました。

f:id:radiotalk-tech:20210518005454p:plain

Azure→OpenStack→CGP... 1年半で2度も基盤インフラを変えたRadiotalk

ここで、サービスとしてのRadiotalkがインフラをどのように変化させていったか、振り返ってみたいと思います。

Radiotalkはエキサイト株式会社の社内ベンチャーとしてスタートし、2017年8月にサービスを開始しました。当初はオフショアで開発を行っていたため、インフラとして先方の開発チームからも利用しやすいMicrosoft Azureを使っていましたが、その後社内開発チームの立ち上がりにあわせ、当時エキサイトがメインで利用していたOpenStackへと移行しました。

f:id:radiotalk-tech:20210518005515p:plain

f:id:radiotalk-tech:20210518005521p:plain

その後、エキサイトではプライベートクラウドからパブリッククラウドへの移行が検討されていたのですが、RadiotalkとしてはSpeech-To-Textなどをいち早く利用したいという思いから、いちはやくパブリッククラウドへと舵を切り、2019年2月にGCPへとインフラを移行しました。

わずか1年半のあいだに2回も基盤インフラを変えるというのは、さすがに異例のことでした。しかし、どう成長していくかわからないスタートアップという舞台においては、必要なところを抜き出しながらインフラを変化させていくというアプローチが非常に有効だったのです。

f:id:radiotalk-tech:20210518005534p:plain

「抽象化ありき」で考えてもいけない

f:id:radiotalk-tech:20210518005600p:plain

抽象化にあたって大事なことは要求にフォーカスすることです。今回の要求は「データの永続化」です。ここで間違えてはいけないポイントは、たんに「MySQLを抽象化する」のではなく、「データの永続化のためにデータストアを抽象化させ、MySQLで実現する」ということです。

f:id:radiotalk-tech:20210518005615p:plain

次に大事にしたのが「YAGNI(You Ain't Gonna Need It:実際に必要になるまで機能を追加しない)」という考え方です。具体的に存在する要求や課題をきっちり抽象化するのは大事であると同時に、それが難しければ抽象化をしないという方法もあります。「なんでもかんでもとにかく抽象化すればよい」という考えになってしまうと、Redisのようにもともと多様な使い方が出来る仕組みの前では、とくに痛い目を見ることになってしまいます。

もっとも、こうした考え方はあくまで原則論なので、そのまま現場に持ってこれるかというと、難しいでしょう。エンジニアやプロダクトマネージャーらと議論を重ね、「本当にユーザーが求めているものはどういうことか」というところにきちんと向き合わなければならないのは言うまでもありません。

f:id:radiotalk-tech:20210518005631p:plain

(3)Radiotalk Androidアプリにおけるモジュール分割の課題とこれから(Radiotalk Androidエンジニア 牧山瞭 @_rmakiyama

f:id:radiotalk-tech:20210518005643p:plain

続いてのLTは、Radiotalk Androidエンジニア・牧山瞭による「Radiotalk Androidアプリにおけるモジュール分割の課題とこれから」。RadiotalkのAndroid向けアプリの開発現場で実践しているモジュール分割の方法についてお話しました。

RadiotalkのAndorid版アプリは2018年10月にリリースしましたが、当時の設計からマルチモジュールを採用していたので、今回は「ひとつのAppモジュールをどう分割するか」といった話は出てきません。

また、開発体制についても、現在は私ひとりがフルタイムで行っており、これから複数人体制での開発へと取り組んでいこうとしている状況です。

サービスのフェーズや開発組織の大きさによっても事情は異なってくるので、すべてのプロジェクトでこれからお話する方法がオススメ、というわけではないことは、あらかじめ念頭においておいていただければと思います。

f:id:radiotalk-tech:20210518005706p:plain

現在のRadiotalk Android版のモジュール構成

現在のRadiotalk Android版アプリにおけるモジュール構成は、図の通りとなっています。

f:id:radiotalk-tech:20210518005731p:plain

「features」と表している部分は、検索やライブ配信など、機能(feature)単位で分割したモジュールがいくつもある形となっています。いわゆる垂直方向の分割です。

「clients」と表している部分は、TalkClientや、LiveClientなど、Radiotalk内でメインとなるロジックをまとめたモジュールがいくつかある形です。これによって、開発の当初から大きく変わらない主要な処理を共通化し、実装の高速化を図っています。

f:id:radiotalk-tech:20210518005744p:plain

「screen_navigation」モジュールでは画面遷移を抽象化させています。マルチモジュール環境では画面遷移で問題が起きやすい部分があるのですが、ここでは循環依存を回避しつつ、機能間での画面遷移を実現させています。

f:id:radiotalk-tech:20210518005758p:plain

さらにドメインモデル的なアプローチとして、収録やライブ配信といった領域ごとにそれぞれモジュールを切り出し、ライブラリ的な扱いができるように実装しています。これらのモジュールを使う際は基本的にインターフェイス経由で処理を呼び出すようにしており、使う側からは具体的な技術詳細を隠すよう実装しています。すなわち、機能を呼び出す側は、その裏側の仕組みを一切意識する必要がありません。

f:id:radiotalk-tech:20210518005811p:plain

機能ごとに独立して開発し、主要ロジックも共通化しているため、個々の部分で改善点はあるにせよ、全体としては特にストレスなく実装が行えています。

もっとも、これは現在私がひとりで開発を行っているという前提によるものなので、今後のプロダクトの成長や、開発チームの拡張も視野に入れていかなければいけません。

f:id:radiotalk-tech:20210518005827p:plain

現状の課題は「制約設計」「機能ごとのインフラの取り回し」「設計思想の明文化」

先ほどCTOの斉藤のLTにもあったとおり、いまやプロダクトを作って終わりの時代ではありません。機能追加や開発組織の変化も含め、プロダクトの成長に合わせて常に改善を続ける必要があります。これを踏まえ、現在の課題について考えてみたいと思います。

f:id:radiotalk-tech:20210518005852p:plain

Featureモジュールの中身はよくあるMVVMパターンを用いており、1モジュールの中でパッケージごとにレイヤー分けしています。

f:id:radiotalk-tech:20210518005907p:plain

AndroidではKotlinで開発しているのですが、この言語の制約を駆使しても、プレゼンテーション層からインフラ層を呼び出せてしまうなど、制約の設計が弱いかなと感じています。またFeatureモジュールごとにインフラ層を持っているので、別のFeatureで定義しているAPIを使いたい、メモリキャッシュにアクセスしたいというときなど、インフラの取り回しが効きづらい部分も出てきています。

f:id:radiotalk-tech:20210518005921p:plain

また、Feature間のモジュール依存についてもあまり整理できておらず、複数人のチームで開発する上ではあまり統一した設計思想が作れていないという点が目下の課題となっています。ひとりでだけで開発している状態では自身が気をつければよいですが、チームが大きくなるとコードレビューで設計の崩壊を防ぐのことはどうしても困難です。

そんななか私が注目したのは、最近読んだ『Design It! プログラマーのためのアーキテクティング入門』(Michael Keeling著/オライリー・ジャパン)にあった次の一文です。

f:id:radiotalk-tech:20210518010008p:plain

改善にあたり、設計によって「何を」促進させたいのかを明確にし、そしてその設計思想が見えるよう意識することで、チームでの開発体験を向上させたいと考えました。

今後の改善策:「モジュール間依存の強制」「開発メンバーの判断コストを下げるモジュール設計」「巨大なCommonモジュールの分割」

これらの話を踏まえ、これからのモジュール構成について現時点で考えていることをお話ししたいと思います。

まず、これからの開発チームが達成したいことについて考えてみました。

これらの話を踏まえ、これからのモジュール構成について現時点で考えていることをお話ししたいと思います。

まず、これからの開発チームが達成したいことについて考えてみました。

f:id:radiotalk-tech:20210518010048p:plain

これを踏まえ、中期的には、次の図のようなモジュール分割に変えようと考えています。

具体的には、Featureモジュールとして1モジュールに実装されていたレイヤー表現をモジュールとして分割すること、ひとつの大きいCommonモジュールとして共通化した処理を持っていた部分を分割したいというところ。また細かい部分では、次の改善への準備を目指す作り方を進めていきたいと思っています。

f:id:radiotalk-tech:20210518010102p:plain

それぞれについて、もう少しだけ説明していきます。

まず、今までパッケージレベルで分けていたレイヤーをモジュール分割します。これにより、依存していないモジュールは参照できないため、たとえばプレゼンテーション層からインフラ層を呼び出せないといった制約を強めることができます。また、Kotlinの場合は、internal修飾子を使うことによって依存していないモジュール内でのみクラスを呼べるよう制約も設けられるため、インタフェースのみを公開し技術的関心事を隠蔽することができるようになります。これにより、ユースケースには「何をするのか」ということを記述し、インフラ層には「どうやって実現するのか」によりフォーカスして実装を進められるメリットもあります。

また今の段階では、ユースケースとインフラを担うモジュールをコンテキストごとに切らず、1モジュールにまとめることで、開発時の判断コストを下げることも考えています。ここには、これから新たな開発メンバーが入ってくることを考えたときに、コンテキストを簡単に理解してもらうことは難しいためであることと、変化の激しいスタートアップだとそのコンテキスト自体も今後変わっていくことが十分に考えられるためという意図があります。

f:id:radiotalk-tech:20210518010119p:plain

また、これまではCommonモジュールとして「便利モジュール」を設けていたのですが、すべてのモジュールから依存されているがゆえ、このモジュールのクラスを変更すると、不必要な個所までまとめて再ビルドが起きるという問題を抱えていました。そのため、こちらは関心の分離をするため、レイヤーごとに分割していきます。

f:id:radiotalk-tech:20210518010134p:plain

長期的にはしっかりのドメイン層を設け、ドメインを中心としたアーキテクチャに寄せていきたいなという思いがあります。まずは依存の逆転を見据え、インターフェースをしっかり切っていく実装をしていきたいと考えています。

f:id:radiotalk-tech:20210518010152p:plain

成長し続けるプロダクトのため、設計も改善を続けていく

「ソフトウェアは作って終わりではない」というのが、Radiotalkの開発組織全体としての哲学です。プロダクトや開発組織の成長とともに、常に設計も改善を続けていくという気持ちを大事にしています。また、設計判断にはバズワードに飛びつくだけではなく、その導入に意味をもたせることを意識しています。

このような話を一緒にワイワイしながら成長させてくれるメンバーも募集しています! >>音声エンターテインメントを創るVoiceTechベンチャーのテックリード
>>急成長中VoiceTechベンチャー"Radiotalk"のエンジニア募集【サーバサイドエンジニア】

f:id:radiotalk-tech:20210518010357p:plain

(4)Radiotalk iOSの音声配信技術について(Radiotalk iOSエンジニア 竹内彰吾)

f:id:radiotalk-tech:20210518010413p:plain

最後のLTは、Radiotalk iOSエンジニア 竹内彰吾による「Radiotalk iOSの音声配信技術について」。Radiotalkの根幹をなす音声配信技術についてお話しました。

f:id:radiotalk-tech:20210518010429p:plain

Radiotalkの「再生まわり」

iOSアプリにおけるオーディオの再生・録音・ミックス・生成まわりをサポートするのが「Core Audio」。以下のように複数のフレームワークで構成されています。

f:id:radiotalk-tech:20210518010529p:plain f:id:radiotalk-tech:20210518010536p:plain Radiotalkでもっとも多く使っているのが、「AVFoundation」と呼ばれるフレームワークです。ストリーミング再生の場合、サーバーから音声のURLを取得し、AVFoundationに含まれる「AVPlayer」モジュールに渡すことで機能を実現しています。

f:id:radiotalk-tech:20210518010555p:plain ダウンロード再生、すなわちiPhoneの端末上にある音声ファイルを再生する場合には、同じくAVFoundationの中にある機能で「AVAudioPlayer」モジュールを使っています。この仕組みではストリーミング再生はできませんが、ローカルファイルの再生に適しているため、採用しています。

iPhoneのコントロールセンターやロック画面で、現在再生しているメディアの情報が表示されるのをよく目にしますが、こうした挙動もその都度実装しなければいけません。Radiotalkでは「MediaPlayer」クラスをimportし、その中の「MPNowPlayingInfoCenter」「MPRemoteCommandCenter」モジュールを組み合わせて実現しています。

f:id:radiotalk-tech:20210518010621p:plain

「MPNowPlayingInfoCenter」にはメディアの再生時間や再生中のタイトル、配信ユーザー名を渡しています。コントロールセンターで再生ボタンや一時停止ボタン押されたときには「MPRemoteCommandCenter」でイベントをキャッチし、アプリの挙動と連携するようにしています。

Radiotalkの「録音まわり」(単体収録の場合)

続いては、音声の録音まわりを説明します。

ユーザーが1人でトークを録音し、配信する場合には、先程と同じく「AVFoundation」をimportし、「AVAudioRecorder」モジュールを使用しています。

f:id:radiotalk-tech:20210518010642p:plain

「AVAudioRecorder」にまずローカルの音声データを渡し、さらに録音の形式を設定ファイルで渡しています。その後AVAudioRecorderのrecordメソッドを叩くと、録音がスタートします。

Radiotalkの「録音まわり」(リモート収録の場合)

Radiotalkには、リモート上でゲストを呼んで一緒にトークをし、それを一つの音声データにまとめて配信する機能があります。この機能は「Agora」という配信プラットフォームを利用しています。

f:id:radiotalk-tech:20210518010659p:plain

この「Agora」は、複数の音声インプットをミックスし、一つの音声データとして出力してくれます。具体的な使い方としてはローカルに音声ファイルのパスを作成し、AgoraSDKに渡すことで、Agora上でトークを録音し、ローカル上に音声ファイルを完成させるというものです。Radiotalk側ではとくに実装はしておらず、Agoraのサービスを純粋に利用しているという形です。

Radiotalkの「音声編集まわり」

Radiotalkでは、収録した音声をあとから編集・エフェクトをほどこしたり、スピードや音量を変えることができます。この機能はAVFoundationをimportし、その中の「AVAudioEngine」を使用して実現しています。

f:id:radiotalk-tech:20210518010738p:plain 「AVAudioEngine」は音声の生成、処理、入出力をトータルに扱うモジュールです。使い方としては、このAVAudioEngine に、音量を変更する「AVAudioPlayerNode」、音の高さや速度を変更する「AVAudioUnitTimePitch」を装着することでエフェクト機能を実装しています。

続いて、波形編集の部分です。

Radiotalkでは収録した音声ファイルの一部分を切り抜いたりできる編集機能を提供していますが、この実装もAVFoundationを使用しています。具体的には「AVAudioRecorder」で波形を取得し、編集した後の波形を用いて「AVConposition」で音声ファイルを再生成しています。

f:id:radiotalk-tech:20210518010755p:plain

Radiotalkの「ライブ配信まわり」(単独ライブ配信)

Radiotalkにはライブ配信の機能が備わっており、配信者が配信をしている音声を大勢の人がリスナーとして聞くことができます。このライブ配信のアーキテクチャについて説明していきます。

f:id:radiotalk-tech:20210518011000p:plain

まずは「単独ライブ配信」。これは、1人のホストと複数のリスナーの場合で構成される場合を指します。配信者がiPhoneに対して喋っている音声データを取得し、それを音声サーバーのRTMP(Real Time Messaging Protocol:リアルタイム通信プロトコル)として配信することで、リスナーがそれを生配信として聞くことができます。

加えてRadiotalk側のサーバーでは、リスナーからギフトやコメントが送信された際にAPIリクエストを行います。リスナー側は、これをWebSocket(双方向通信プロトコル)経由で受け取ることで、自分を含めリスナーからのギフトやコメントがリアルタイムに表示される仕組みとなっています。

Radiotalkの「ライブ配信まわり」(コラボライブ配信)

もうひとつが「コラボライブ配信」。これは、配信者がリスナーの中から何人かをピックアップし、一緒に配信できる機能です。ピックアップしたリスナーのことを「ゲスト」と呼んでいます。

配信者とゲストとの会話には、先程のリモート配信でも出てきた「Agora」を使用しています。

配信者側のアプリでは、まずAgora上にルームを作成し、ゲストに対してRadiotalkのサーバー経由で招待を送ります。ゲストはWebSocketでこれを受け取ってAgoraのルームに参加し、複数人が同時に会話できる状況ができあがります。さらに、このAgoraからミックスされた音声をRadiotalkのサーバーで取得し、リスナーにHLSで送信するという仕組みになっています。

Apple製品間の「サンプリングレート違い」を埋めるための工夫

iPhoneから音声データを取得し、音声サーバーに流すまでのあいだに、低レベル(ハードウェア制御に近い)APIを用いた実装が必要になります。実はこの部分こそが、結構頭を悩ませるポイントでした。iPhoneやiPad、AirPodsなどのApple製品は、それぞれ製品ごとに機器の音声サンプリングレートが異なっています。Radiotalkでは、これらのサンプリングレートをひとつに統一するため、アプリ側で変換を行っています。

f:id:radiotalk-tech:20210518011058p:plain f:id:radiotalk-tech:20210518011107p:plain まず、サンプリングレートを取得する方法ですが、AVFoundationをimportし、「AVAudioEngine」と「AVAudioNode」を組み合わせて実装しています。具体的にはマイクから受け取る音声と、あらかじめ設定したサンプリングレートに基づくアウトプット定義を比較し、44.1KHzで入力されたものはそのまま変換しないで通しています。

f:id:radiotalk-tech:20210518011122p:plain 一方、16KHzで入力された場合、過去にうまくいかなかった事情から、いちど48KHzに変換したうえで44.1KHzに再度変換する、というトリッキーな操作をしています。アップサンプリング(サンプリング周波数を上げる)操作はよい感じに進むのですが、48KHzを44.1KHzに下げるといったダウンサンプリング(サンプリング周波数を下げる)はなかなか難しく、(バッドノウハウ的に)このような処理を行っているのが現状です。

最後に「音声開発あるある」

音声周りの開発をしているうえでの「あるある」なのですが、コワーキングスペースなど、周りに人がいる中で開発をしていてハウリングを起こしてしまったり、音楽を聞きながら作業をしていて、ホストとゲスト側それぞれの疎通を確認するために端末のイヤホンを差し替えていたら、その拍子に爆音の音楽が流れてしまったり…… ということがよくあります。

あとは、Android側をホストにして、iOSをクライアント側にしていた際、環境が異なるAndroid側から音声が送られたのを聞いて、うっかりiOS側の問題が解決したと思い込んでしまったり……。このように恥ずかしい気持ちになることも多いですが、概ね楽しく開発を行っています。
>>音声エンターテインメントを創るVoiceTechベンチャーのテックリード
>>前年比5倍!急成長中の音声配信サービスiOSエンジニア募集!
>>音声配信プラットフォーム"Radiotalk"のAndroidエンジニア!

Radiotalk株式会社のCTOに就任しました

こんにちは、サイトウ です。

この度、Radiotalkの取締役CTOに就任しました。

就任にあたり、この記事では現在の具体的なRadiotalkの開発組織と次に目指す開発組織についてご紹介します。

こちらのインタビューもあわせて見ていただけると、幸いです!

www.wantedly.com

www.wantedly.com

Radiotalkの開発組織

2021/05/13現在の開発メンバー

PM
1名

デザイナー
1名

サーバーサイド
4名

iOS
3名

Android
2名

QA
1名

他にも副業やインターンのメンバーがおり、チームでRadiotalkを開発しています。

自分を含む社員のメンバーは全員Radiotalkで配信をしていたり、

今まで100人近くのユーザーと会ってきたり、

サービスに対する理解が深いメンバーが多いのが一番の特徴です。

また社員全員がテックカンファレンスや勉強会などで登壇しており、アウトプットを通じて、社内や技術コミュニティに対して活動を行っているのも大きな特徴です。

なにを大事にしているのか

下記の2つを開発組織として大事にしています。

  • エンジニアリングを通して、事業拡大に貢献しつづけること
  • すばやく価値を提供し、仮説検証を行えるようにすること

"つづける" という点がポイントで、瞬間的な解決ではなく、線で捉えて、開発の意思決定をしています。

音声配信サービスはまだ勝ちパターンが定まっていない市場であり、試して初めてわかることがとても多いです。

そのために悩むより、スコープを絞ってでも、素早くユーザーに届け、反応をもらいというのを開発組織として大事にしています。

これからの開発組織とCTOとしてやっていくこと

上記2点を強化するために、CTOとしてこれらのことをやっていきます。

  1. エンジニアリングを通じて、事業貢献を続ける
  2. 事業貢献を続ける開発組織をつくる
  3. 1と2を支える開発文化をつくる

技術と事業の両輪でエンジニアリングすることを楽しいと思えるような組織にしていきます。

具体的な課題

事業貢献を続ける開発を目指すとはいえ、具体的な開発課題もあります。

各領域であげると、

Android

  • 音声編集機能の実現
  • テロップ動画機能の実現

iOS

  • 音声編集、テロップ動画の改善
  • Swift UIを見越したアーキテクチャの改善

サーバーサイド

  • より安定したライブ、リアルタイム対話の実現
  • 音声コンテンツのレコメンドの改善
  • 音声コンテンツの検索の改善
  • WEBでのログイン、収録やライブ配信の実現

もちろん他にまだまだあり、やりたいこと、解決したいことはたくさんあります!

ぜひ詳細はカジュアル面談等でお話しましょう!

最後に

もっと多くの人が気軽に音声配信をはじめらるように、

もっと多くの人が満足のいくトークができるように、

配信者とリスナーとのコミュニティがどんどん生まれていくように、

トークを新しいエンタメにできるようにこれからも精進していきます!


トークや音声配信、ライブ配信が面白そうと感じている方

ユーザーとの距離がものすごい近いサービスをつくりたい方

エンジニアリングが大好きな方

一緒にRadiotalkを開発しませんか!?開発メンバーを積極的に募集しています!

www.wantedly.com

www.wantedly.com

www.wantedly.com

www.wantedly.com

カジュアル面談ご希望の方は上記Wantedly URLの「話を聞きに行きたい」を押して、「まずは話を聞いてみたい」とご連絡ください!

RadiotalkのAndroid/iOSのサポートバージョンの基準について

こんにちは、サイトウ です。

今回はRadiotalkのAndroid/iOSのサポートバージョンの基準についてご紹介します。

なぜ基準を定めたのか、その基準を使い、どういう心構えで判断するのかなどをまとめました。

なぜ基準を設けるのか

開発の効率化のためです。

以前は、なにかしらの理由によりサポートバージョンを引き上げないといけなくなってからあわてて、議論、判断、告知、実際の引き上げ作業をやっていたため、議論や判断もメンバーそれぞれになってしまい、無駄が多くなってしまっていました。

そこで基準を設けることで、それをベースに議論、判断が行えるので、チームでのコミュニケーション効率があがり、対応もスムーズになるだろうという狙いです。

Radiotalkにおけるそもそもの心構え

Radiotalkは積極的にサポートバージョンを引き上げたいというわけではありません。

我々は下記のような心構えで運用しております(※社内向けドキュメントの引用)

基準を設けたからといって、機会的に引き上げるということはせず、毎回チーム内で合意をとって、行います。

この基準に達したから、すぐにサポートを打ち切るというわけではありません。

広くサポートし、多くのユーザーに楽しんでもらうことを念頭においてください。

サポートを切ることでユーザーに不便を強いる面と下記のようなメリットを踏まえて、総合的に判断してください。

- OSレベルの新機能を利用することでサービスの成長につながる
- 下位のOSの対応をやめることで開発効率が向上する
- 下位のOSのバグやセキュリティリスクを回避できる

Android/iOSのサポートバージョンの基準について

直近30日のアクティブユーザーのうち、対象OSのユーザーが3%未満である

これを大前提の判断基準とし、上記のようなメリットを踏まえ、総合的に判断します。

また打ち切ることを決めたら、2ヶ月以上のバッファを持って、ユーザーに告知することも1つの推奨事項としました。

このときにアプリが使えなくなるのか、最新バージョンへのアップデートができなくなるのか、動作保証はどうなるのかなどをできる限り具体的に伝えるようにしたいと思います。


各社で公開されている基準を参考にし、草案を作り、各アプリのエンジニア、プロダクト開発メンバーと協議し、決定しました。

参考にさせていただいた各社様

実はまだ作成したばかりでこの基準を使ってのサポートバージョンを引き上げてはおりません。

運用していくなかでアクティブな比率や告知の期日などは改善していきます!

もし読んでいただいている皆さまの企業で、サポート基準がありましたら、ぜひ教えて下さい!

最後に

Radiotalkではサービスとユーザーのことを中心に考え、開発効率とのバランスをとりながら、エンジニアリングしてくれるメンバーを募集しています!

www.wantedly.com

www.wantedly.com

2020年のRadiotalk Androidアプリの開発現場

こんにちは、Androidアプリエンジニアの牧山(@_rmakiyama)です!

RadiotalkのAndroidアプリは、2018年のリリースから約2年が経ちました。この間に、Kotlin Coroutinesが正式版になったりJetpackがどんどん充実してきたりと、Android開発の事情も大きく変化していきました。それに合わせてRadiotalkのAndroidアプリ(以下Radiotalkアプリ)も少しずつ変化をしていっています。今回はそんなRadiotalkアプリの2020年時点での開発現場についてまとめます。
スタートアップ企業でどのような開発が行われているかのイメージが少しでも持っていただけたら嬉しいです。

アーキテクチャ

Radiotalkアプリではマルチモジュールを採用しています。 機能ごとでのモジュール分割を採用しています。別途特別なモジュールとして、Radiotalkのトークや番組など、主要なドメイン操作に関する機能をクライアントとして切り出し、処理を共通化させています。

f:id:radiotalk-tech:20201222224246p:plain

それぞれのモジュールは、MVVM + レイヤードアーキテクチャを採用して実装しています。

f:id:radiotalk-tech:20201222224418p:plain

少し変わっているところでいうと、UseCaseと名前のついているクラスがアプリケーションロジックとリポジトリパターンの責務を持つ実装にしている部分です。
ひとつのUseCaseにはひとつのパブリックメソッドを定義し、ユースケースに合わせてローカルとリモートのデータのやり取りをし、必要に応じてViewModelが求めるデータを返却します。

詳細はまた別の機会にということで割愛しますが、開発当初はRxJavaを全面採用していたところをKotlin CoroutineやLiveData、最近だとFlowを採用したりと、会社としてのフェーズとAndroid開発環境の進化とともにアーキテクチャも少しずつ変えていっています。まだまだチームも小さいので、開発者が責任を持ちながらも楽しく開発できることを第一に考え、積極的に新しい技術も取り入れています。

これからは、チームが大きくなったときのことを想定し、さらにスピードアップできるようなりアーキテクチャも考えています。

開発体制

Androidエンジニアはリリース当初からフルタイムだと私のみで、業務委託だとiOS/Androidを両方できる方にジョインしてもらっているため、1.5人くらいのリソースで開発しています。

タスクについては、ライブ配信などの大型施策、KPIを伸ばすためのグロース施策、UI/UXのカイゼンや不具合修正などの改善施策の大きく3つに分かれています。
大型施策は基本的にフルタイムの社員が取り組みますが、それ以外を放置してしまうとサービスとしての質が落ちてしまうので、グロース施策や改善施策についても業務委託の方と協力しながらオーナーシップを持って優先度をつけてバンバン取り組んでいます。

開発環境

MAD scorecardはこちらになります!

f:id:radiotalk-tech:20201222224429p:plain

Android Studio

現在は安定版の最新を使っていますが、betaやalphaも場合によっては使っていた経緯もあります。
これに関しては、リリースされる機能によって開発者体験が向上することをチームで合意が取れれば使っていこうという方針です。

minSdkVersion

21です。これについてはminSdkVersionを上げる基準はまだ明確化できていません。
コンテンツをストックできるサービスでもあるため、慎重に検討しつつ、23に上げるタイミングをはかっています。

Android App Bundle

対応済みです。NDKを扱うSDKもあるため早めに対応をし、かなりアプリサイズの削減ができています。

Jetpack Libraries

積極的に採用しています。主要なものだと、ViewModel/LiveData/WorkManager/Room/Dagger Hilt/Material Componentsなどを使っています。採用できていなものとしてはNavigationがあります。

CI/CD

CIはGitHub Actionsを使っています。プルリクエストに対するktlintの実行と、特定ブランチへのマージをフックにFirebase App DistributionへのAPKのアップを行っています。また、UI崩れがないかというヘルチェックの一環で1日1回Firebase Test Labを走らせています。

QAやリリースのフローは手動で行っているので、折を見て自動化していきたいです。

ドキュメント管理とプロジェクト管理

ドキュメント管理にはKibelaを使っています。施策の素案や詳しい仕様のまとめから、自己紹介や運用ガイドライン、アイデアなど様々なドキュメントを残す文化になっています。

小さいチームなので、タスク管理はスプレッドシートと口頭ベースで行っていました。コロナでのリモート勤務も長引き、少し課題感があったためアイデアベースで提案したところ、Clubhouseというプロジェクト管理ツールを使っています。

f:id:radiotalk-tech:20201222224546p:plain

その他のライブラリ

その他、プロジェクト内でよく使っているライブラリを一部紹介します。

  • ExoPlayer
    • 音声の再生周りを任せています。
  • Groupie
    • RecyclerViewを扱いやすくするライブラリです。好んで使っています。
  • Flipper
    • debugツールです。今年から導入しました。
  • Retrofit/OkHttp/Moshi
    • 通信はsuspend関数で呼び出しています。
  • Glide
    • Coilが気になっていますが今はGlideを全面的に利用しています。
  • MockK
    • テスト時のモックに使っています。
    • 恥ずかしながら最近徐々にテストを書き始めています。
  • RxJava
    • 非同期処理はCoroutines、値の監視はLiveData/Flowに置き換えています。
    • 今のところ、新規の部分で使う予定はなく消す予定です。
  • Kotpref
    • SharedPreferencesを扱いやすくするライブラリです。好んで使っています。
    • DataStoreの動向は横目で見ていますがまだ導入していません。

まとめ

今回は、RadiotalkのAndroidアプリの開発現場についてざっくりと紹介しました! スタートアップでの開発現場が少しでもイメージしていただけたら幸いです。プロダクトを成長させ、最速でユーザーに価値を提供させるためにも、開発現場の改善も日々行っていきます!

今回詳しく説明できなかった部分でもっと聞きたい部分がありましたら、ぜひカジュアルにお話しましょう!気軽に私にDMをするでもOKです!

meety.net

また、RadiotalkではAndroidエンジニアの採用も進めています!
書ききれない部分でカオスな面もあります。一緒にオーナーシップを持ってプロダクトの成長と開発現場のアップデートを推し進めてくれるエンジニアを絶賛募集中です! もしちょっとでも興味がある人は、Wantedlyからまずはカジュアル面談でお話しましょう!

www.wantedly.com

「GCPとPHP」というタイトルでPHP Conference Japan 2020に登壇しました

こんにちは、サーバーサイドエンジニアのサイトウ (@gamu1012) です。

今回はPHP Conference Japan 2020に登壇したことについての振り返りレポートになります。

PHP Conference Japan 2020

https://phpcon.php.gr.jp/2020/

PHPカンファレンスは1999年からPHP関連の技術を主とした技術者カンファレンスです。

登壇内容

「GCPとPHP」というタイトルで発表し、下記のような内容についてお話しました。

  • Google Cloud PlatformでPHP動かす4つの選択肢について
  • オススメの環境について
  • Radiotalkでの事例
  • そこからの学び

全体を通して、

GCPでPHPを動かすときの選択肢を整理するのに使ってもらえれば幸いです。

実際に発表を聞いていただいた方、TwitterやDiscordでコメントや質問をくれた方、ありがとうございました!

GCPについての補足

今回はPHPを動かす環境についてフォーカスして発表しましたが、

GCPは他にもWebアプリケーションを開発/運用するために強力なツールがそろっています。

データベースのCloud SQLやMemorystore、

Cloud Logging, Cloud Monitoringなどのロギングや監視などの運用ツールも素晴らしいです!

皆さんの事例や運用方法もぜひ色々と教えて下さい!

一緒に開発するメンバーを募集しております!

Webと管理ツールの分離やRSS生成をCloud Runに移行するなど、これからもRadiotalkは改善しつづけていきます!

その改善、進化を一緒に楽しんでくれるメンバーを募集しております!

youtrust.jp

カジュアルな知見の情報交換もぜひ!

meety.net

Androidでオーディオアプリを作るということ

こんにちは、Androidアプリエンジニアの牧山(@_rmakiyama)です!
XTechグループ Advent Calendar 2020の4日目を任されました!

先日行われたZli × エキサイト 合同LTに、グループ会社枠としてLTをさせていただきました。
「テーマは技術系ならなんでもOK!」な会だったので、RadiotalkからはAndroidでオーディオアプリを作るにはなにを考えどのように作るかを簡単に説明しました。

10分のLTでは駆け足になったり省略した部分もあるので、発表内容をもとに、よりAndroidエンジニア向けに概要をまとめます。

オーディオアプリの概要

Androidにおけるオーディオアプリを作る際は、UI用のメディアコントローラと実際のプレイヤーを操作するメディアセッションに分割する、クライアント/サーバー型設計が推奨されています。

f:id:radiotalk-tech:20201202185619p:plain

図にもあるように、Androidのmediaライブラリではクライアント/サーバー型設計のアプローチを助けるクラスが準備されています。

メディアセッションとメディアコントローラ

メディアセッションは、サーバーの役割を担うクラスになっています。 プレイヤーの操作は、すべてメディアセッションにより処理するよう実装します。そうすることで、アプリのUIからだけではなくWear OSやAndroid Autoからの操作も一貫した処理を行うことができます。

メディアコントローラは、クライアントの役割を担うクラスになっています。 このクラスを介することにより、UIからはプレイヤーがMediaPlayerなのかExoPlayerなのかといったことを意識することなく操作できます。

オーディオアプリの作成

前述の概要をもとに、ここからは簡単なオーディオアプリの基礎部分の実装方法を紹介します。

f:id:radiotalk-tech:20201202185652p:plain

全体像は上記のようになります。 概要で説明したもの以外にMediaBrowserServiceMediaBrowserが登場しています。

MediaBrowserServiceは、AndroidのServiceを継承しており、MediaSessionとのやりとりを簡略化させてくれます。Serviceを利用することにより、オーディオをバックグラウンドで再生することを可能にしています。

MediaBrowserは、MediaBrowserServiceを介したSessionTokenの取得とMediaSessionが公開するオーディオコンテンツへのアクセスを提供します。

※ 実装にはmedia-compatサポートライブラリを使います。

Serviceの実装

前述の通り、MediaBrowserServiceを用いて実装をしていきます。 onCreate()でメディアセッションを初期化します。

class MyMediaBrowserService : MediaBrowserServiceCompat() {

    private var mediaSession: MediaSessionCompat? = null

    override fun onCreate() {
        super.onCreate()
        mediaSession = MediaSessionCompat(baseContext, TAG).apply {
            // メディアコントローラからの操作をハンドリングする。
            setCallback(callback)
            // 初期化したMediaSessionのTokenをセットする。
            // 接続したクライアントがこのトークンを用いて通信できる。
            setSessionToken(sessionToken)
        }
    }

    // MediaSessionが提供するコールバックをそれぞれ処理する。
    private val callback = object : MediaSessionCompat.Callback() {
        override fun onPlay() {...}
        override fun onStop() {...}
        override fun onPause() {...}
        ...
    }
    ...

MediaSessionCompat.Callbackでは、それぞれ必要に応じてプレイヤーの操作やオーディオフォーカスの制御が必要になります。非常に重要ではありますが、ここでは割愛します。 詳細の実装については公式ドキュメントの実装ガイドを参考にしてください。 また、後述するExoPlayerのExtensionを利用すると、こちらの処理の多くが簡略化されるのでそちらも検討すると良いでしょう。

さて、MediaBrowserServiceでは、クライアントの空の接続を処理するために実装すべきメソッドが2つあります。それが、onGetRoot()onLoadChildren()です。

onGetRoot()では、サービスのアクセス制御を行います。こちらはアプリケーションの要件に応じて処理していきます。

    ...

    override fun onGetRoot(
        clientPackageName: String,
        clientUid: Int,
        rootHints: Bundle?
    ): BrowserRoot? {
        return if (allowBrowsing(clientPackageName, clientUid)) {
            // ブラウジングを許可する場合はコンテンツ階層を表すルートIDを返す
            BrowserRoot("root", null)
        } else {
            // 接続を拒否する場合はnullを返す
            null
        }
    }

    ...

クライアントからは、MediaBrowserCompat#subscribeによりコンテンツを走査できます。このメソッドからonLoadChildren()コールバックが呼ばれ、適切なコンテンツを返すことでクライアントはUIを構築できます。
ここで返したコンテンツのリストは、MediaBrowserCompat#subscribeを呼び出すときに渡せるSubscriptionCallbackでコールバックとして流れてきます。

    ...

    override fun onLoadChildren(
        parentId: String,
        result: Result<MutableList<MediaItem>>
    ) {
        // parentIdに応じたMediaItemのリストを取得
        val mediaItems: MutableList<MediaItem> = getMediaItems(parentId)
        result.sendResult(mediaItems)
    }

    ...

MediaBrowserの実装

MediaBrowserは、MediaBrowserServiceとの接続とMediaControllerの生成の役割を担っています。

まずはMediaBrowserServiceとの接続を実装していきます。 MediaBrowserは生成時に、接続するServiceの名前を指定します。 connect()を呼ぶことでMediaBrowserServiceに接続できます。

class MediaPlayerActivity : AppCompatActivity() {

    private lateinit var mediaBrowser: MediaBrowserCompat
    private lateinit var mediaController: MediaControllerCompat

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        mediaBrowser = MediaBrowserCompat(
            this,
            ComponentName(this, MyMediaBrowserService::class.java),
            connectionCallbacks,
            null
        )
        mediaBrowser.connect()
    }
    ...

接続が完了すると、MediaBrowserの生成時に渡していたコールバックが呼ばれます。 接続後にはMediaBrowserを通してセッショントークが取得できるため、それを用いてMediaControllerを生成します。

    ...

    private lateinit var mediaController: MediaControllerCompat
    ...

    private val connectionCallbacks = object : MediaBrowserCompat.ConnectionCallback() {
        override fun onConnected() {
            // 接続したMediaSessionのトークンを取得することができる
            mediaBrowser.sessionToken.also { token ->
                // トークンを使ってMediaControllerを生成
                mediaController = MediaControllerCompat(
                    this@MediaPlayerActivity,
                    token
                )
            }
            // コンテンツの情報と再生の状態をコールバックで受け取る
            mediaController.registerCallback(controllerCallback)
        }

        // 必要に応じて実装
        override fun onConnectionSuspended() {}
        override fun onConnectionFailed() {}
    }
    ...

あとは生成されたMediaControllerCompatTransportControlsを通してオーディオを再生するだけです。ここで大事なのは、このMediaPlayerActivityではMediaPlayerやExoPlayerといったプレイヤーに依存していないということです。どのように再生するかをMediaSessionが一貫して担うことで、Wear端末などからの操作でも同じように振る舞うことができます。

※ 今回例示した実装ではActivityにそのまま実装をしていますが、実際のプロダクトではアーキテクチャに応じてViewModelやドメインサービス等に実装をすると良いでしょう。

オーディオアプリとしての振る舞い

ここまでの説明に含まれていませんが、必ず考慮すべき点がまだあります。

  • オーディオフォーカスの管理
  • 音声出力の変更の処理
  • 再生中の通知の表示

オーディオフォーカスの管理は、再生中に他のアプリで音声を再生した場合の振る舞いを制御するために必要です。ここを無視すると、自分のアプリが別のアプリの音声とかぶってしまい、ユーザーにとって非常に悪い体験になってしまいます。
音声出力の変更の処理は、再生中にイヤホンが抜けた場合などの振る舞いを制御するために必要です。イヤホンが抜けて聴いていたものが大音量で流れてしまわないためにも必要です。 再生中の通知の表示は、バックグラウンドで動作するアプリには必須の要件です。

これらを愚直に実装するのはなかなか骨が折れます。これらをExoPlayerを用いて簡略化する方法を次に紹介します。

ExoPlayer用いた実装

ExoPlayerはGoogle製のライブラリで、MediaPlayerが提供していないDASHなどの再生APIもサポートしています。
2020/12/04時点での最新バージョンは2.12.2です。

ExoPlayerでは2.9.0からオーディオフォーカスのハンドリングを、2.11.0から音声出力の変更の処理のハンドリングをサポートしています。
そのため、以下のように初期化するのみで対応できるようになりました。

private val exoPlayer: ExoPlayer by lazy {
    SimpleExoPlayer.Builder(context).build().apply {
        val attr = AudioAttributes.Builder()
            .setUsage(C.USAGE_MEDIA)
            .setContentType(C.CONTENT_TYPE_MUSIC)
            .build()
        setAudioAttributes(attr, true)
        setHandleAudioBecomingNoisy(true)
    }
}

さらに、ExoPlayerライブラリの中のexoplayer-uiにはPlayerNotificationManagerというクラスが含まれています。詳細を省きますが、これを利用することで通知チャンネルの生成や再生中の音声の情報を用いた通知の構築などを切り出せるので、実装の見通しがかなり良くなります。

ExoPlayer Extension

ExoPlayerはさまざまなExtensionを提供しています。その中にあるMediaSession extensionを利用することで、MediaSessionとExoPlayerのやりとりをかなり簡略化できます。

前述の通り、実際に音声を再生するプレイヤーの制御はMediaSessionCompat.Callback()を実装してそれぞれ操作する必要がありました。MediaSession extensionではMediaSessionConnectorMediaSessionConnector.PlaybackPreparerというクラスを提供しており、MediaSessionとの統合がかなり簡単になります。
これらの実際の実装は、公式の紹介ブログGoogleによるオーディオアプリのサンプルを参考にしてください。

最後に

今回は、Androidでオーディオアプリを作成する基礎部分について紹介しました。最初はとっつきにくいですが、一度実装してみると理にかなった設計だと感じます。またExoPlayerとそのExtensionを使うことでさらに便利になりました。
音声や動画を扱うアプリに携わらないと知れない領域でもあるので、これを機に興味を持っていただけたら幸いです!

RadiotalkではAndroidエンジニアも絶賛募集しております!
もしちょっとでも興味がある人は、Wantedlyからまずはカジュアル面談でお話しましょう! 気軽に私にDMをするでもOKです!

www.wantedly.com

参考

iOSDC Japan 2020にRadiotalkメンバーが登壇します

iOSDC Japan 2020

開催日時 : 2020年9月19日(土)〜9月21日(月・祝)

開催場所 : オンライン開催

公式サイト : https://iosdc.jp/2020/index.html

公式Twitter : https://twitter.com/iosdcjp

登壇メンバー

Radiotalkから2名が登壇します!

両名とも音声に関する内容になっておりますので、

iOSでの音声関連の実装に興味あるかた、音声配信サービスを運営している方はぜひご覧ください!

2020/09/19 18:30〜 今日から分かるAVAudioEngineの全

fortee.jp

2020/09/21 15:40〜 実装したくなる音声編集

fortee.jp

2020/09/21 17:25〜 Apple Low-Latency HLSを使った超低遅延配信について

fortee.jp

最後に

RadiotalkではiOSアプリエンジニアを募集しており、

もしちょっとでも興味がある人は下記のページからご応募いただくか、

Twitter等のSNSでお声かけいただければと思います!

herp.careers


グループ会社のエキサイトはシルバースポンサーとして協賛をしておりますので、そちらもご確認ください。

exdev.exblog.jp

インターン体験談 #Androidチーム編

f:id:radiotalk-tech:20200712113532p:plain

こんにちは!

2020年2月中旬から4ヶ月ほど、AndroidチームでインターンさせていただいたGo(@TwisedGoap)です!

本記事では、RadiotalkのAndroidチームで体験したこと・成長できたことを紹介します。

最初のタスクはデザイン修正

最初のタスクは「タグ一覧のリストデザイン修正」でした。

具体的には、次のことをしました。

  • アイコンを角丸にする
  • 「#」マークをアイコンの右上に設置する

以下の画像が、実際に修正した様子です。

f:id:radiotalk-tech:20200711142335p:plain
タグ一覧のリストデザイン修正

Kotlinのコードを書くよりも、XMLのコードを修正する方が比較的簡単だろうと思って着手しましたが、全然そんなことはありませんでした😅

その理由は、次のような細かいルールに則る必要があったからです。

@deminの値を使って、レイアウト崩れを防止する

XMLで4dp上にマージンが欲しいときは、次のように書くと思います。

android:layout_marginTop="4dp"

しかし、Radiotalkでは次のように書きます。

android:layout_marginTop="@dimen/space_4dp"

// dimens.xml
<dimen name="space_4dp">4dp</dimen>

どちらも4dp上にマージンを指定しているのは変わりませんが、Radiotalkではdimens.xmlに定義した値を使っています。

一見すると回りくどく見えますが、「アプリで使えるスペース幅は決まっている」という意識づけがされます。

デザインスペックに合わせる

デザインスペックとは、デザイン上の幅や色を細かくみるためもので、RadiotalkではFigmaで共有されています。

エンジニアはデザインスペックにできるだけ従って実装していきます。

全然難しいことはなさそうですが、僕にとっては結構大変でした。

それは、XMLとデザインスペックを照らし合わせて確認作業をしていたからです。

そのため確認に時間がかかりすぎたり、見落としが多くなってしまいました。

これを解消してくれたのは、Window VQAというアプリでした。

Window VQAはスマホ画面上であらゆるViewのサイズを測ることができます。

f:id:radiotalk-tech:20200711160715p:plain
Window VQAの動作イメージ

Window VQAを知ってからは、デザインの確認がスムーズになりました。

アーキテクチャを理解する

インターン開始から1ヶ月ほど経ち、一通りの開発フローをこなせるようになったところで、アプリ全体に関わるタスクを着手するようになりました。

そして、このインターンでの最大の壁に直面します。

それは「アプリのアーキテクチャの理解」です。

Hello, マルチモジュール!

RadiotalkのAndroidアプリはマルチモジュールのプロジェクトになっていて、以下のようなモジュール分けがされています(※大部分が省略されています)

また、モジュール内・モジュール間の具体的な実装は、Layered Architectureに従っています。

f:id:radiotalk-tech:20200704121703p:plain
モジュール分けの概要図

私自身、MVVM, Layered Architectureなど用語は知っているつもりでしたが、実践したことはなかったので、なかなか慣れるまでに時間がかかりました。

「Androidの会」でキャッチアップ力をつける

RadiotalkのAndroidチームでは、週1で「Androidの会」を実施して、Androidに関する情報共有をしていました。

私がインターンをしていた時期は、Android11の新情報がたくさん出ていた時期であり、Androidの会でもよくトピックになりました。

f:id:radiotalk-tech:20200711175552p:plain
Androidの会のissueにコメントする

Androidの会で共有したいことは予めGitHubのissueにコメントしておく運用でした。

そのため、前にコメントしている人とトピックが被らないようにしたくなります(別に被ってもよかったけど😅)

自分はいつも後からコメントしていたので、いつも違うトピックを見つけてくるのに躍起になっていました。

そのおかげか、

  • Androidの良い情報が集まる場所はどこなのか
  • Androidの最新情報の出どころはどこなのか

などがわかるようになり、自ずとキャッチアップ力が向上しました。

さいごに

Radiotalkでのインターンを経て、自分は「Android好きな学生」から「Androidエンジニア」にレベルアップさせてもらったと感じでいます。

また施策に取り組む中で、自分の目指したいエンジニア像が変わり、

明確に「グロースエンジニア」を目指していきたいと思うようになりました。

また、新しい開発現場でもすぐに施策にアサインできているのはRadiotalkでの経験のおかげに他なりません。

改めて、Radiotalkの皆さんに感謝申し上げます。

本当にありがとうございました!