Radiotalk Tech Blog

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

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の皆さんに感謝申し上げます。

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

Flipperを導入してデバッグを快適に

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

Radiotalkでは、特にAPI通信周りの確認にokhttp3logging-interceptorを使っていました。
どのエンドポイントを呼び、結果がどうだったかなどを確認する上では十分だったのですが、細かくレスポンスの中身を見たい、APIの呼ばれた順序をひと目で確認したいというニーズが高まりました。

タイミングよく、DroidKaigi 2020 - 1から学ぶAndroidアプリデバッグ - アプリの動作を追いかけよう / Yoshihiro Wada [JA]の発表を見て、デバッグツールの便利さを改めて知り、Flipperを採用したのでご紹介します。

Flipperとは

公式から引用すると

Flipper is a platform for debugging iOS, Android and React Native apps. Visualize, inspect, and control your apps from a simple desktop interface. Use Flipper as is or extend it using the plugin API.

つまり、モバイル用のデバッグツールです。Android専用ではないところが驚きでした。

ちなみにiOSでは物理デバイスのサポートはされていないようなので、RadiotalkではiOSでの導入は見送りました。

他の選択肢

他にもstethoHyperion-Androidなどがあります。

今回は主にNetwork周りのデバッグを楽にしたいというニーズと、比較的導入が簡単なFlipperを選びました。

Flipperを導入して良かったところ

まずは導入方法の前に良かったところを。

ネットワーク通信のログをLogcatを探らず見れる

どのエンドポイントにリクエストしてレスポンスが何かをGUIで簡単に見れて素敵でした。 一覧性があるのでどの順番で呼んだかなども見やすくなりました。

API呼び出し一覧

|f:id:radiotalk-tech:20200626155506p:plain

APIレスポンス

f:id:radiotalk-tech:20200626155522p:plain|

Roomデーターベースの中身が覗ける

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

デバッグのときはかなり便利。クエリも書けちゃいます。

SharedPreferenceの中身が覗ける

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

Android Studio上からも見れますが意外と面倒ですよね…これも便利です。値の変更もできます。

Flipper導入方法

公式のSet up your Android appに沿って説明していきます。

今回は紹介したNetworkプラグインを含めた導入方法になります。

Dependencies

複数のビルドバリアントが存在する場合は各バリアントに合わせた依存を定義します。

debugImplementation "com.facebook.flipper:flipper:${versions.flipper}"
debugImplementation "com.facebook.soloader:soloader:${versions.soloader}"
debugImplementation "com.facebook.flipper:flipper-network-plugin:${versions.flipper_network}"
stagingImplementation "com.facebook.flipper:flipper:${versions.flipper}"
stagingImplementation "com.facebook.soloader:soloader:${versions.soloader}"
stagingImplementation "com.facebook.flipper:flipper-network-plugin:${versions.flipper_network}"

releaseImplementation "com.facebook.flipper:flipper-noop:${versions.flipper}"

Application Setup

releaseImplementation'com.facebook.flipper:flipper-noop'を入れましたが、プラグインに関してはnoopを提供しない予定なようなので、リリース版とその他版でApplicationをわけるのが良さそうです。

本番のApplicationをopenクラスにしつつ

open class App : DaggerApplication() {

    override fun onCreate() {
        super.onCreate()
        // setups...
    }

他のバリアントでは、元のApplicationを継承しつつFlipperをセットアップしましょう。

class DebugApp : App() {

    override fun onCreate() {
        super.onCreate()
        initializedFlipper()
    }

    private fun initializedFlipper() {
        SoLoader.init(this, false)
        if (BuildConfig.DEBUG && FlipperUtils.shouldEnableFlipper(this)) {
            val client = AndroidFlipperClient.getInstance(this).apply {
                addPlugin(NetworkFlipperPlugin())
                addPlugin(SharedPreferencesFlipperPlugin(this@DebugApp))
                addPlugin(DatabasesFlipperPlugin(this@DebugApp))
            }
            client.start()
        }
    }
}

Diagnostics

公式のとおりこちらも追加しておきます。

<activity android:name="com.facebook.flipper.android.diagnostics.FlipperDiagnosticActivity"
        android:exported="true"/>

Enabling plugins

公式サイトからデスクトップアプリケーションをダウンロードして、追加したプラグインを有効にすれば完了です!

Links

輪読会のすゝめ

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

今回はエキサイト社とRadiotalk社の合同で行っている輪読会について紹介します。

輪読会とは

輪読会

人々が集まって、同じ教科書などの本を読み、その内容について意見を交わすことを意味する語。事前に決められた担当者が、本の内容を訳したりまとめたりしてから、他の参加者が理解できるように発表する形式がとられることも多い。

Weblio辞書より引用

我々が行っている輪読会は下記の特徴があります。

  • 全員が本を購入
  • 章ごとに行う
  • 事前に該当章を全員が読んでくる
  • 議論中心
  • 週に1回1時間程度

誰とやっているか

エキサイト社とRadiotalk社のエンジニア合同でやっており、現在は8人で進めています。

年次は新卒入社1年目から中堅まで、専門領域や担当サービスなどもバラバラです。

本を選ぶ

上述のとおり、メンバーは領域、経験がバラバラなため、下記の2点を意識しています。

  • 極端に簡単であったり、極端に専門的な内容は選ばない
  • 偏った領域の本は選ばない

1冊が完了したときに、みんなで次はこれがいいと思う!というのをリストアップし、合議で決めるという流れで行っています。

2点を意識すると設計の話になりがちで、もし輪読会をやっているよ!という方がいらっしゃいましたら、オススメの本を教えて下さい!

進め方

事前準備

  • 全員が購入済み
  • 章ごとに担当を割りふる
  • 担当は概要と気になった点をまとめる

当日のフロー

担当から概要の共有

それぞれが"気になった点"をあげる

議論

以下、くりかえしで1時間ほど、盛り上がったときは1時間30分ほどの議論します。

"気になった点"について

  • 私はここが気になった
  • ここがいまいち理解できなかったので、どなたかもう少し解説してほしい
  • ここが"エモい"

議論の種は幅広くあげ、それぞれの意見を交換することを心がけています。

輪読会のなにがいいか

本の内容と実体験を交えた話ができるため、学びが深くなるというのが一番素晴らしい点だと感じています。

経験があるメンバーは実体験を踏まえた、意見をルーキーのメンバーに共有しながら、

設計パターンやテクニックついて、メリットデメリットを議論できたり、

ルーキーのメンバーから出た質問に全員がハッとすることも多々あり、いいサイクルがまわります。

まとめ

輪読会は議論を通すことで、学びが深くなり、素晴らしいです。

XTechグループは輪読会をはじめ、AWSGCPなどのクラウド勉強会もやっております。

その話はまた別の機会にさせていただきます。

参考

主催メンバーからみた輪読会はこちら。

ohshige.hatenablog.com

ohshige.hatenablog.com

いつもありがとうございます!

輪読した本

GCPで組織間のプロジェクト移行

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

この記事ではGCPで組織間でのプロジェクト移行について、それの手順と気をつけることについて紹介します。

概要

GCPのリソースはプロジェクトという単位で基本的に管理します。

そのプロジェクトを束ねる、組織というリソースがあります。

f:id:radiotalk-tech:20200616101246p:plain
GCP_リソース階層 リソース階層 より引用

上記のように、組織は会社単位になることが多いので、

GCPにおいて、別の組織へプロジェクトを移行するということはめったに起こらないと思います。

そういう背景もあり、調べても事例や体験談が全く見つからなかったので、この記事を通して、誰かの助けになれば幸いです。

基本的な手順については公式ドキュメントに記載がありますので、あわせてご確認ください。

https://cloud.google.com/resource-manager/docs/project-migration?hl=ja

ポイント

  • 組織移行は自分たちでは行うことはできない
  • Googleがやってくれるることは 該当プロジェクトを組織から外す という部分のみ

手順 と 気をつけること

1.準備

上記のドキュメントにもあるこれらを行う

以下のリストを使用して、プロジェクト リソースを移行する準備をします。

f:id:radiotalk-tech:20200616101417p:plain
移行への準備 別の組織へのプロジェクトの移行 より引用

特に権限周りは注意して確認することが大事です。

組織やディレクトリから継承している権限が該当プロジェクトにないか、上位のリソースから確認しましょう。

特にプロジェクト担当者と組織管理の担当者が違う場合は密に確認することをオススメします。

2.チケットを発行する

我々は有償のサポートに入っていなかったため、サポートケース経由で連絡をしました。

またサポートケースは英語のみなのでご注意ください。

有償サポートに入っている場合は日本語で送ることができますが、その場合はサポートではなくテクニカル担当者に連絡するのが一番いいと思います。

3. 現組織から外す

外すと書きましたが、実際に外すのはGCPの担当者です。

サポートケースを送り、メールアドレス等の確認が済むと、受付完了連絡がサポート担当者から送られてきます。

そこから我々のケースでは1日後に実際に現行の組織からプロジェクトが外されました。

最初のサポートケースから5日間で完了しました。

ちなみに受付完了後にいつ行う予定等の連絡はありません。

次に連絡をもらえるのは、組織から外れたことの完了連絡です。

組織から外されることで影響がでる権限周り等の調整は必ず事前に終わらせておきましょう。

4. 組織なしプロジェクトを新組織に紐付ける

新しい組織に紐付けを行うのは

移行前に新組織でのディレクトリ構成とそれに紐づく権限構成を決めておくとスムーズでオススメです。

5. 請求先アカウントを変更する

ここは必須ではないですが、組織が変わると、多くの場合は請求先アカウントを変わるはずなのでお忘れなく。

まとめ

  • 組織移行は自分たちでは行うことはできない
  • Googleがやってくれることは 該当プロジェクトを組織から外す という部分のみ
  • サポートケースを開いてから、5日程度で現行の組織から外れる

参考

Google Cloud 別の組織へのプロジェクトの移行

Google Cloud リソース階層

Google Cloud エンタープライズ オンボーディング チェックリスト

Google Cloud エンタープライズ企業のベスト プラクティス