Studyplus Engineering Blog

スタディプラスの開発者が発信するブログ

透明Activityの罠

初めまして、Studyplus開発部でAndroidアプリの開発をしております中島です。

今回のブログでは、我々 Studyplus Androidチームが踏んでしまった、深い深い落とし穴について少しお話しさせていただければと思います。

透明Activityとは?

ここで 透明Activity と言っているのは、以下の指定を持ったThemeを用いることで背景を透過させたActivityのことです。

(Android SDK内、theme.xmlより抜粋)

<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowIsTranslucent">true</item>

弊社アプリでは、例えば以下のような箇所で使用しています。

  • 初めて使う機能の際に、チュートリアル画像を出して簡単に説明する
  • ホーム画面を開いた際、ユーザーが設定したイベント(模試などが多いでしょうか)までの日にちをカウントダウン表示する

透明Activity の罠とは?

では、この透明Activityにどんな罠があったかということについて、順を追って説明いたします。

Overture<発端>

弊社アプリは、今年の10/10のアップデートをもってTargetSDKを 26 から 27 に更新いたしました。 これ自体は特に問題ありません、むしろ素晴らしいことであると思います。(Androidチームとしては28まで上げたかったのは確かですが、まぁ一歩ずつ)

手元のテストでも特に問題はなくリリースしたのですが、その日の夕方から悪夢が始まりました…

Stampede<殺到>

Fatal Exception: java.lang.RuntimeException: Unable to start activity ComponentInfo{~~Activity}: java.lang.IllegalStateException:
Only fullscreen opaque activities can request orientation

複数のActivityで上記のクラッシュレポートが殺到しました。一晩で 10k を超えるクラッシュが報告され、はっきり言って異常事態でした。

Investigate<調査>

正直この時点で精神を落ち着かせることなど不可能ですが、頭だけでも落ち着かせて調査を進めなくてはいけません… Crashlyticsの確認と、ソースコードの確認で以下のようなことがわかりました。

  • クラッシュは Android 8.0 のみで起こっていること
  • クラッシュしているActivityは全て 透明Activity であること

また、エラーメッセージで検索したところ StackOverFlowの投稿 が見つかりました。 これに加えAOSPのActivity.java内部を8.08.1で比較してみましたところ、8.0で以下の処理が追加され、8.1で削除されていることがわかりました。

Activity.java(8.0)

protected void onCreate(@Nullable Bundle savedInstanceState) {

    ~~~

    if (getApplicationInfo().targetSdkVersion > O && mActivityInfo.isFixedOrientation()) {
        final TypedArray ta = obtainStyledAttributes(com.android.internal.R.styleable.Window);
        final boolean isTranslucentOrFloating = ActivityInfo.isTranslucentOrFloating(ta);
        ta.recycle();

        if (isTranslucentOrFloating) {
            throw new IllegalStateException(
                    "Only fullscreen opaque activities can request orientation");
        }
    }

    ~~~

以下は、この変更がAOSPに追加された時(66fa94d42f30771c3e7b249756aed656d38aed08)のコミットメッセージです。

This changelist enforces that activities targeting O and beyond can only specify an orientation if they are fullscreen. The change ignores the orientation on the server side and throws an exception when the client has an orientation set in onCreate or invokes Activity#setRequestedOrientation.

O以上において、透明Activity などのfullscreenではないActivityにはOrientationの指定を行えないようにすることが目的として明言されています。 こうすることで、直前のActivityのOrientationに依存させることが目的だと思われます。

また、その後に Oには指定を許す といった旨のコミットメッセージと共に コミット(d1ac18c7c9eca1b07120be598dc6859b188baeb3)が追加され、上記のコードの形になったようです。

Allow for SDK 26 Activities to specify orientation when not fullscreen.

Conclusion<結果>

調査の結果、以下の条件を全て満たした場合に、 Activity#onCreateで必ず例外をスローしてクラッシュする ようになっていることが発見されました。

  • アプリの指定する TargetSDKが 27(Android 8.1) 以上
  • 動作端末 が Android 8.0 (SDK 26)
  • ActivityのThemeに <item name="android:windowIsTranslucent">true</item> または <item name="android:windowIsFloating">true</item> と指定している
  • Activityに android:screenOrientationportraitlandscape を指定している

なお弊社アプリは縦画面固定アプリのため、アプリ内の全Activityに対し android:screenOrientation="portrait" が指定がされています。 そのため、TargetSDKを27に上げたことが、罠の最後のトリガーだったのでした…

Fix<対応>

対応としては、Orientationの指定を消せば解決する問題ではあります。 しかし、メッセージでは8.0 以上に限定しているため、ただOrientation指定を消すのみでは 8.0 未満の端末に影響する可能性があります。 以上の理由から、該当Activityにおいて以下のような修正を行ないました。

  • AndroidManifestからOrientation指定を削除
  • 8.0 未満の場合のみ、 Activity#onCreate でOrientation指定を改めて追加
super.onCreate(savedInstanceState);

// super#onCreate の後に改めて Orientation 指定
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}

この対応により事態は収束しましたが、次に私を待っていたのは障害報告書の作成任務であったことは想像にかたくありません。

まとめ

今回は、TargetSDKを上げたことにより顕在化した不具合についてお話しさせていただきました。

ただ、今回の一件も、元はと言えばTargetSDKを最新に近づけようという意志の下に生まれたものと言えます。 StudyplusにはFail Forward(失敗から学ぶことに重点を置き、失敗そのものを批判することはしない)という標語があります。 我々Studyplus Androidチームはこの一件から学んだことを糧にして、今後もモダンなAndroid開発を目指していければと思っております。(Kotlin Coroutinesの導入も始まっています!) もちろん同じ轍は踏まぬよう、十二分に注意いたします。

Studyplusでの開発に興味がおありの方はぜひ ご応募ください

AAC Pagingライブラリ導入の話

お久しぶりです。Androidエンジニアの若宮(id:D_R_1009)です。 今日はAndroid Architecture Components Pagingライブラリ(AAC Paging)の導入例を紹介したいと思います。

アプリを利用してくださっている方はお気づきになられていると思いますが、11月19日よりアプリのデザインを一新しました。 デザインの変更に伴い、アプリ起動時の通信処理や各Fragmentの生成処理の見直しを行いました。安定性が増すことを目標に、見直しに取り組みました。

デザインのアップデートとほぼ同時ではあるのですが、タイムラインのパフォーマンス改善にも取り組みました。 今回は、パフォーマンス改善でAAC Pagingライブラリを導入した際のアレコレを書きたいと思います。

AAC Pagingライブラリとは

https://developer.android.com/topic/libraries/architecture/paging/

AACライブラリの一つとして2018年6月に正式リリースされた、ローカルDBやリモートサーバーから逐次データを読み込む処理をサポートしてくれるライブラリです。 データ読み込みの処理と表示するデータの保持を分けることで、RecyclerViewのスクロール処理がデータの読み込み処理によって遅くならない等の効果が期待できます。

個人的には recyclerview.extensions.ListAdapter がsupportライブラリに入った頃から気になっている存在です。 そのため「ようやく正式版になってくれたか」という気分ですね。

導入に当たって検討した事柄

旧タイムライン処理にはいくつかの課題がありました。一部を抜粋すると下記の通りです。

  • 追加読み込み処理が「最後から5番目」を表示したタイミングになるため、読み込み処理がUI処理に強く依存している
  • リストのデータがRecyclerView.Adapterで保持されているため、Fragmentの破棄時にキャッシュが削除されてしまう
  • ObservableListを利用しているため、通信取得処理のスレッドとUI更新時のスレッドをFragment内で明示的に切り替える必要がある

Pagingライブラリの導入によって次のように解決すると考えました。

  • 追加読み込みのタイミングがPagingライブラリ側でハンドリングされるため、読み込みの通信処理に専念できる
  • リストのデータをメモリやディスク上で保持できるため、Fragmentの破棄時に(不要な)キャッシュ削除が発生しない
  • LiveDataでリストの取得ができるため、UIスレッドとそのほかのスレッドをほぼ意識しないで済む(ViewModel + LiveDataの処理にまとめられる)

導入方法

前述の通り、Pagingライブラリを導入していた時期はアプリのデザインリニューアルの時期とかぶっています。 また導入事例や導入LTなどをあまり見つけることもできなかったため、いくつか安全策をとることとしました

旧タイムライン処理との平行運用

通信時のパースクラスから表示用のFragmentまで、旧タイムラインの処理と切り離して全て新規作成することとしました。 他の新規開発機能とのバッティングを抑え、ライブラリの導入に失敗した場合や想定以上の工数を使ってしまった場合に備えるためです。 結果としてタイムラインを置き換えることはできましたが、タイムラインの一部のみ置き換えることを想定しました。

良かった点

  • 旧タイムラインを置き換え可能な箇所から書き換えることができたため、レビューのコストが想定より低くなった
  • 一部のみの置き換えを念頭に置いて設計した結果、依存度の低いコード記述となった
  • 旧タイムラインは主にJavaで記述されていたが、新タイムラインはKotlinで記述することができ、data classを活用できた

悪かった点

  • JSONのパースクラスから作り直したところ、RecyclerViewのItemレイアウトが使い回せなくなってしまった
  • 旧タイムラインに追加された修正を新タイムラインに反映させていたが、反映漏れが発生した

その他に新しくなった箇所

旧タイムラインのItemをConstlaintLayoutに置き換えたところ、View間の関係性が漏れていた箇所が多数ありました。 Viewの縦横のサイズ計算に関わる問題となるため、ネストが浅くなるようリファクタリングを行なっています。

また、タイムラインのFragment間でRecyclerViewのViewPoolを共有し、Inflate回数を減らす対応を行いました。 こちらは2018年のDroidKaigiより広く使われている手法なので、弊社アプリもようやく追いついた感覚があります。

感想

導入のためサンプルコードをPCに落としてから、機能としてリリースするまで3週間ほどかかりました。 ほとんどPagingライブラリの導入にかかりきりだったので、長い時間をかけたような気がしています。

導入時に想定していたほど、Pagingライブラリの導入にはコストがかかりませんでした。 これは公式で非常にわかりやすいサンプルプロジェクトが用意されていたことが大きいです。まず公式サンプルに従ってAPIとDBの接続をするのが良さそうです。

逆に想定しきれていなかったのが、レイアウト周りの修正の多さです。 もともとButterKnifeをDataBindingに置き換えている途中だったこともあり、Kotlinとxmlの両方で書き換え処理が多くなりました。 変更に強いデータ構造を作らなければならない、という強い反省になったと感じています。

ひとこと

バリバリAACを使っていきたい! という気持ち溢れるエンジニアがいましたらこちらよりご連絡ください。

Kubernetesハンズオンを行いました

はじめまして、Studyplus開発部でサーバーサイドを担当している栗山です。

背景

Studyplusではコンテナ化を進めたいと思っていますが、まだAWS Elastic BeanstalkEC2上でアプリケーションが動いています。 今後コンテナ化するにあたり、

  • コンテナ化を進めていきたいが、コンテナのデプロイや管理はどうすればいいかわからない。
  • コンテナ管理するためにKubernetesというのが良いらしいが覚えることが多くて難しそう。
  • そもそもコンテナよくわからないし、敷居が高そう

といった不安や疑問が部内にあったのでそれらを解消すべく、Kubernetesのハンズオンを実施しました。

目的

ハンズオンの目的としては以下です。

  • Kubernetesが何を解決するかを知る
  • Kubernetesの機能を知る
  • Kubernetesの各概念を知る
  • GCP上のKubernetesに簡単なアプリケーションをデプロイして、実際にKubernetesのスケーリング機能や耐障害性等々を体験してもらう。

ハンズオンの進め方

ハンズオンの資料は以下にあります。

まず最初にKubernetesの特徴や何ができるか、そして各概念の説明をしました。

  • Kubernetesの各概念の理解 (※説明が間違っている可能性があるので間違いを見つけた場合はPRを送ってもらえると助かりますm( )m)

Kubernetesには様々な機能、リソースがありますが、まずは基本であるPod, ReplicaSet, Deployment, Service, Ingressを理解してもらうことに重点をおきました。

その次に、実際にGCP上でアプリケーションを動かしてもらいました。

GCPを選んだのは

  • GCPは2014年からKubernetesをマネージドサービスとして提供しており、Kubernetesとの親和性が高い
  • Studyplusでは主にAWSを使っているので、違ったクラウドを体験することは見識を広める意味でもプラスになる

といった理由からです。 実際触ってみると、GCPは非常にシンプルでKubernetesがシームレスに組み込まれており、Kubernetesを動かすには最適な環境だと感じました。

このハンズオンは、説明と実際に手を動かすのに全部で1時間半〜2時間くらい必要となります。 そのためスムーズに進める必要があり、以下のことを注意して行いました。

  • ハンズオン資料のコードをコピペすれば動くようにしておく
    • しかしコピペを間違う人もでてくるので、必要なファイルは全て予め用意しておきダウンロードすれば済むようにした
  • 事前に時間がかかる作業(例えばGCPアカウント作成や、SDKインストール作業等々)はハンズオンの前に各自で行ってもらう
  • 出来るだけ早く理解してもらうために、説明文はできるだけ丁寧かつ正確に書いておく。また喋る予定の内容も全て書いておいて読めば分かるようにしておく。
  • 予期せぬエラーがでたり躓いて先に進めない人が出ないように、問題なく進めているかこまめに確認を取る。人数が多いと躓く人が多くなるので、一回のハンズオンの参加者は4人くらいにした。

まとめ

Kubernetes自体の素晴らしさもあり、ハンズオン後はKubernetesを使っていく機運が高まりました。 実際に運用していないのでまだ見えていないつらみやデメリット等があると思いますが、それを上回るメリットがあるように感じているので、今後はKubernetesの導入を進めていけたらと思っています。

スタディプラスではKubernetes導入を一緒に進めていく仲間を募集しております! ご応募お待ちしております!!

補足

ハンズオンの内容は、 WEB+DB PRESS Vol.99のKubernetes特集の内容を参考にしました。ありがとうございました。 また、Kubernetesを知るためにはKubernetes完全ガイドという本が2018/9に発売されており、これを読めば完全に理解できると思います。本当に最高の本です。

インフラエンジニアがオフィス移転で考えたこと[前編]

初めまして、インフラエンジニアの菅原です。

弊社では事業拡大に伴うオフィス移転を予定しております。
インフラエンジニアとして社内ネットワーク移設について検討したことを移転前と移転後の2回に分けてまとめたいと思います。

f:id:ksugahara08:20181110143139j:plain:w300

まだ何も物が入っていないまっさらなオフィスの写真です。結構広い!
移転は年末になり、現在は移転準備をしている最中となります。

移転前に検討するべき項目

1.移転後の要件

まず、移転後の社内ネットワークデザインを決める基礎となる情報を収集しました。

  • 社内ネットワークの利用ユーザー数
    弊社では事業拡大も視野に入れて50人~200人規模の利用ユーザー数を想定しました。
    それだけでなく、イベント時はゲストも繋げるため最大200人の同時接続を許容する必要がありました。

  • ネットワーク接続する端末の台数
    アプリの検証端末などもあるため利用ユーザー数+αの接続が見込まれます。接続数には余裕を持たせたほうが良さそうでした。

  • オフィスのレイアウト
    今回は内装業者さんに座席の位置やパーティション等の図面を早い段階で頂いたため、そちらを基にルーターの設置場所、無線アクセスポイントの配置について検討することにしました。

  • オフィスで行う業務
    一般的な事務、カスタマーサポート、WEBアプリ開発がオフィス業務の中心になります。
    AWSとGCPを利用しているため、サーバーの移設については今回検討不要でした。
    オフィス向けプリンター・複合機は現在利用しているものを利用します。

2.現状の確認

現状どのような業者と契約しているか、どのような機器を利用しているかを把握し、移転後も使い続けるのかを確認する必要があります。今回は以下の現状について確認しました。

  • ISP(インターネットサービスプロバイダ)と光回線業者
    過去の経緯からIP電話とインターネット回線を別々の業者と契約している状況でした。こちらを一つにまとめられないか検討しました。しかし、変更してもランニングコストは変わらなかったため、現状のプランのまま移転することにしました。

  • ネットワーク機器とその設定
    ルーター、無線アクセスポイントについて設定内容を確認しました。現状では接続先アクセスポイントによって回線速度が遅いと感じていました。こちらも移転に伴って変更を検討しました。

  • 現状の回線速度
    引越し後の回線速度とも比較を行いたいため、測定しておきます。ツールは「Speedtest by Ookla」を 利用しました。

  • 固定IPが使われている箇所
    社内ツールなどは固定IPでアクセス制限を行なっている箇所があったため、設定を洗い出しました。
    移転に伴って設定を書き換える必要があります。

3.障害・災害時の検討

SLOによって変わってくるとは思いますが、機器の故障に寄る障害にも強い構成を検討するべきでしょう。
今回の移転ではSPOF(単一障害点)になりうる部分をできるだけ排除し、ネットワークの可用性を向上させることにしました。
具体的には現状ルーターが1つしかないので、機器の故障に備えてもう一つ購入し、多重化することにしました。 業者の保険に入ることで故障時の対策とすることもできましたが、ルーターを2つ購入して設置しておくことで、素早く復旧できると考えたからです。

4.スケジュールの策定

ネットワークの動作確認を考えると、移転の1週間前までには移転先でのネットワーク設定作業を終えていたいと考えました。オフィス移転の前日や前々日がネットワーク移転の作業日になるケースは多いと思います。そうするとスケジュールに余裕がなく、もしもの時に対応できません。
こうした要件を満たしたい場合に問題となってくるのが光回線業者の作業です。 「移転」で依頼した場合は工事作業日が前日や前々日になってしまします。しかし、「新規」で契約し直すことで現オフィスと平行契約期間を設けることができます。これで1週間前に動作確認ができるようにしました。

5.移設後のネットワークの構成と機器を検討

  • ルーター
    現状使っているルーターが故障した時のために同じものをもう1台購入することにしました。
    移転後のオフィスでは利用者が増えるためルータのNAPTテーブルエントリ数(IPマスカレードエントリ数)は購入前に確認しました。 PC1台に対して50〜200程度あれば十分だと思います。
    スループットも重要な確認ポイントになります。 こちらが低いと回線速度を遅く感じられてしまいます。 大雑把ですが、実測値で1Gbps以上出るものを選ぶと良いと思われます。

  • 無線アクセスポイント
    移転前のオフィスでは無線アクセスポイントが2つしかなく、接続先によっては遅く感じられました。
    そのため、移転先のオフィスでは無線アクセスポイントを5つに増やすことにしました。 天井に設置し、電波の強い範囲が被るようにする予定です。
    これでインターネット回線が遅いと言われないようになって欲しいです(願望)。

まとめ

今回はオフィス移転前に社内ネットワークについて検討したことを中心にまとめました。
社内要件を行い、業者さんとコミュニケーションを取りながら移転に向けて準備を進めております。
次回は移転後の話を記事にしたいと思います。

弊社では新しいオフィスで一緒に働いてくれる仲間を募集しております!
ご応募お待ちしております!!

RubyWorld Conference 2018に行ってきた

こんにちはスタディプラスCTOの島田です。

はじめに

スタディプラスは、RubyWorld Conference 2018Platinumスポンサーとして協賛をさせて頂きました。 それに伴って、ブースも出展いたしました。

2018.rubyworld-conf.org

島根到着からブース出展までの内容と、同行した各エンジニアの印象に残ったセッションの感想を紹介させて頂きます。

出雲縁結び空港と会場

出雲縁結び空港には、早速RubyWorld Conferenceのポスターが。

カンファレンス当日は晴天に恵まれました。(大橋川にてしじみ漁をしている模様)

企業ブース出展をする大展示場の様子。

ブース出展

スタディプラスのブースでは、Studyplusのサービスにちなんで、「好きなRubyの技術書を投票!!」という企画を実施しました。

投票してもらうRuby技術書を独断と偏見で5冊ピックアップ。

多くの方に投票を頂きました。

ブースにはMatzさんにも来て頂き、著書にサインと記念撮影をしていだきました。

セッションの感想

島田

基調講演 The Power of the Community(まつもとゆきひろ氏)

Rubyコミュニティのこれまでのヒストリーと、コミュニティの力。 個人的には「Rubyは不景気が生んだ言語」というのが刺さった。

花井

RubyによるDBスケーラビリティ

Leonard Chinさんによる、クックパッドの1000万ユーザーを支えるDB周りについての発表でした。 クックパッドさんほどではないですが、弊社もRails 3の時代からRuby on Railsによる開発を続けており、すぐに業務に役立てたいと思うTipsがある発表でした。 特に

  • ユーザーが多いので遅くなっている人を特定するのが難しい
  • データが多いので再現するのも大変

という点は大変共感できました。

発表では、実際にあったトラブルを例に

  • NewRelicのAverageResponstimeでは問題ないようにみえるレスポンスの陰にあるユーザーの体験を95パーセンタイル、99パーセンタイルも見てボトルネックの発見に至った
  • activerecord のexplainメソッドで計測した
  • indexだけで解決できない問題をRubyの積演算で解決した

など具体的な解決の手順と、そこに至る道筋を追体験できるもので、とても参考になりました。

mruby/cを用いたプログラミング教育向けデバイスの開発

牧 俊男さんによる、mruby/cでArduinoの制御をする事例の発表でした。 今回のカンファレンスには現地の高校生も参加しており、最近のプログラミング教育で具体的にどのような取り組みがされているのかを知る機会に恵まれておりました。 そんな中での本セッションは、プログラミング未経験の中高生に8時間で体験してもらうための工夫や、発展途上のmruby/cでの苦労などが紹介されていて個人的に興味深い内容でした。

Cのコードへ変換するか、直接Cのコードを書くというところにハードルを感じていたのですが、irbの感覚でデバイスの制御ができるという点に関心を持ちました。 業務ではまず使うことのないデバイスですが、この発表を見て早速Arduinoを購入しました。

石上

1日目、2日目の中から気になった講演・発表の感想などを書きます。

基調講演 The Power of the Community(まつもとゆきひろ氏)

タイトル通り、Rubyコミュニティの力についての講演でした。Rubyをデザインしたのはまつもとさんですが、Rubyがここまで大きくなるにはコミュニティの力が不可欠だったことを知ることができました。

スモウルビー3.0の開発とRubyを用いたプログラミング学習への活用

ScratchのRuby版、smalruby(スモウルビー)の開発についての発表。私はこの発表を聴くまでsmalrubyについて知りませんでしたが、発表者である島根大学の武本さんの、プログラミングを楽しむことへの情熱が伝わってきてとてもよい発表でした。Githubに公開されているので、実際に手元へcloneしてきて yarn; yarn run start したら動きました。スモウルビーはscratch-guiをforkしていて、Rubyコードの生成以外は本家と同じようです。

Scratchのようなツールは、存在自体は前々から知っていたものの、普段触ろうとする機会がないので新鮮でした。プログラミング教育の盛り上がりと同時に、こういったプログラミング体験ツールも今後いろいろ出てくると面白いなと思いました。

基調講演 Don't Stop Moving(Chad Fowler氏)

エンジニアがモチベーションを保つためにどうするかという話でした。エンジニアリングに限らず、自己啓発系の書籍や、考え方なども紹介されていました。自分が投資している技術カテゴリを意識することなど、今後のキャリアを考える上で参考になる話が多かったです。

CookpadがRubyと歩んできた10年

Cookpadで実際に存在した、過去のおもしろPull Requestが紹介されていて楽しかったです。 終わったあと、一緒に聴いていた自社のエンジニアと、うちもいろいろありそうですねという話をしました。

RubyによるIoTデバイス制御

mruby/c で下記のIoTシステムを作ったことについての発表。

組み込み系のシステムをRubyで書いた実例として面白かったです。mrubyに対して興味がわきました。

田口

一日目に印象に残った2つの発表についての感想を書きます。

Railsチュートリアル×反転授業: 解説動画を用いた能動的な学びによる驚きの効果

https://speakerdeck.com/yasslab/more-interactive-way-of-learning-rails

Railsチュートリアルを公開しているYassLab株式会社の安川さんの発表です。
個人的にRailsチュートリアルは大変お世話になったので、今回の発表は非常に楽しみでした。
発表を聞く前は「反転授業」とはどういうことかわからなかったのですが、「一斉授業」「反転授業」について以下のスライドでわかりやすく説明されています。

https://speakerdeck.com/yasslab/more-interactive-way-of-learning-rails?slide=30 https://speakerdeck.com/yasslab/more-interactive-way-of-learning-rails?slide=31

「一斉授業」は、現在の学校での授業のような体系です。講義外の課題として、宿題を解いたりします。
「反転授業」はまさに一斉授業の反転で、学校の授業でやっているような「知識のインプット」を講義外で行い、講義中は実戦形式で開発していくといった体系です。
反転授業の体系の話を聞いたとき、なるほどと感じました。自分がRailsチュートリアルを実際にやってみたり、エンジニアとして働いてみて感じたことですが、手を動かして作りながら学んでいったほうが、インプットだけするよりも効果的であると思っています。それに通ずる感覚を、講義を通じて養えると考えると非常に有意義だと思います。また、スライドにもありますが、難しくて挫折しがちな初学者が挫折しないようにすることにおいてもかなり効果的なのは素晴らしいことだと思います。
この発表を見て、学生だけでなく、新しいことを学ぶ社会人にも反転授業が有効であると感じました。料金的にお得な法人向けの動画視聴サービスを公開されているようなので、法人での導入も大いにアリなのではないかなと思います。

プログラミング入門をプロジェクトでやってみた -Rubyで取り組むプログラミング実践-

フェリス女学院大学でのRubyを用いたプログラミング授業の実例の発表でした。上記のRailsチュートリアルの次の発表だったのですが、実践的な教育の事例として関連が深い発表だと感じました。
授業の講師の方と、その授業を受けていた生徒2名の合同発表でした。授業の実際の内容の話を聞くと、4〜5人でチームを組み、チームメンバーの得意分野に応じて作業を分担したり、講師の方との頻繁なやり取りで疑問を解決したりしていて、非常に実践的だなと感じました。個人の実際の作業とチームでの作業を同時に体験できるので、かなり良い経験になるのではと思います。
生徒の方々は文系学部で、IT関連の業界や職務には詳しくなかったと聞きました。プログラミングは遠い世界にあるものという感覚を持っていたそうです。そのような人が、授業を通じてプログラミングを実際にやってみて、さらにそれをチーム全体のプロジェクトとして進める経験を積んだというのは、非常に素晴らしいことだと思います。
成果物として、Rubyで図形を描いて作成したというステッカーを配布していたので、ありがたくいただきました。素敵な授業だと思うので、今後もこの授業を受けて少しでもプログラミングに興味を持ってくれる生徒が増えるといいなと思います。

スポンサーLT

コーヒーブレイクで発表した協賛企業のショートプレゼンテーションを紹介します。

speakerdeck.com

最後に

スタディプラスとしてテック系カンファレンスでブースを出すのは初めてのことだったので、不慣れな点や反省する点がいくつかありました。 しかし色々な方に立ち寄って頂き、ユーザーの方とも触れ合う機会も得たりと実りあるものだったと思います。

今後もメンバーの技術への知見を広げる事とOSSコミュニティへの貢献のため、カンファレンスへの協力をしていきたいと考えています。

Kotlin Coroutinesを(SDKに)導入しました!

初めまして。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を使って開発をしたい方はこちらからぜひご応募ください! 開発部一同、お会いできる機会を心よりお待ちしております。

Sidekiq Enterpriseを使う

お久しぶりです。サーバーサイドエンジニアの花井です。

みなさんは非同期処理に何をお使いですか? ActiveJobでしょうか? Resqueでしょうか? Sidekiqでしょうか?

弊社では歴史的経緯から、上記全てのgemをプロダクトで使っていますが、 この度Sidekiq Enterpriseを導入して非同期処理の統一に着手しました。 Sidekiq Enterpriseの日本語記事があまりなかったので、利用の検討や実際に使う際の注意点などを紹介できればと思います。

What is Sidekiq

SidekiqはRuby製の非同期処理フレームワークです。 resqueを使うと、ジョブのリトライとユニーク性の担保のために自前で実装が必要でつらいので、sidekiqの採用に至りました。

また、非同期処理の流量を制限したい(特に外部サービスへの問い合わせが発生する所)という意図があり、Enterpriseを採用するに至りました。

今回は以下の2機能を使ったので、その設定方法を紹介します。

  • RATE LIMITING
  • PERIODIC JOBS

How to use

install

ここから申し込みをすると利用に必要なキーの情報が送られてきます。 Gemfileに指定された内容を追記して、$ bundle install します。

Enterpriseを入れるとき、同時にProのインストールもします。 Enterpriseを申し込むとProの機能も使えるようになるというのは、こういうカラクリのようです。

Implementation

Wikiが充実しているので、そちらを参考にすればほとんど迷うことなく設定できます。

ActicveJobなどと併用している場合、Sidekiq単体(include Sidekiq::WorkerしたWorker)に変更する必要があるようです(Wikiに例がなかったのと、resqueueからの置き換えだったこともあり、Sidekiq単体で使うように書き換えていますがもしかしたらできるかもしれません)。

RATE LIMITING

大量の子ワーカーを作るような非同期処理1つ1つの中で、APIを叩くようなケースでは、許容量を超えたリクエストをしてしまうことがあると思います。そんな時に指定した量を超えないように制限できる機能がRate Limitingです。 ※Redis 2.8以上が必要です。

弊社ではサブシステムから大量のアプリ内メッセージを送信する際に、APIサーバーへ負荷をかけないようにすることを目的として使っています。

特別な設定は必要なく、流量を制限したいWorkerに、以下のような設定を記述します。

class HogeWorker
  include Sidekiq::Worker
  DELIVER_LIMIT = Sidekiq::Limiter.concurrent('limitter_name', 50, wait_timeout: 5, lock_timeout: 30)

  def perform
    DELIVER_LIMIT.within_limit do
          # 必要な処理
    end
  end
end

Sidekiq::Limiter.concurrent には、リミッター名、同時実行数、ロック空き待ち時間(秒)、ロックを手放す時間(秒)を指定できます。 Web UIにタブが追加され、そこからどのくらいロック待ちが起きたかなどのメトリクスを確認することができますので、チューニングも簡単にできます。

PERIODIC JOBS

非同期処理の定期実行をcron形式で指定できます。Sidekiqを使う場合、sidekiq-cronを使う方も多いと思いますが、Enterpriseにすると標準装備されています。 弊社では集計処理など、ピークタイムを避けて深夜に行いたい処理の実行に活用しています。

/config/initializer/sidekiq.rb の中に以下のように実行を開始する時間と、対象のWorkerを指定します。 cronに設定する時間は内部的にTimeクラスで操作されるので、システムのタイムゾーンに合わせて記述する必要があります。resque-schedulerを使っているとタイムゾーンも指定できるので、システムのタイムゾーンと異なるタイムゾーンを扱っている場合は注意が必要です。

Sidekiq.configure_server do |config|
  config.periodic do |mgr|
    mgr.register('0 20 * * *', Hoge::HardWorker)
    mgr.register('0 21 * * *', FugaWorker)
    mgr.register('0 23 * * *', Piyo::PiyoWorker)
  end

まとめ

プッシュ通知の送信が重複しないように、次はUnique Jobsを利用予定です。 他にも色々な機能があるので、必要に応じて活用していきたいです。

一緒にプロダクトの改善をやってくれる仲間を募集しています!

おまけ

Enterpriseの効果かわかりませんが、設定中に相談のIssueを立ててたところ早めに返事がもらえました。 また、確認の過程で翻訳のIssueが見つかったらしく、コントリビュートもできました。