Androidアプリ の minSdkVersion を21にした話

技術部モバイル基盤グループの こやまカニ大好き( id:nein37 ) です。今回はクックパッドにおける Android アプリの minSdkVersion を 21 にした話を紹介します。

クックパッドのモバイルアプリではユーザーが5%存在するプラットフォームではサービスを維持するというルールが存在していて、ここ数年はこのルールに従って minSdkVersion を決めてきました。 最後に更新されたのは2016年7月のことで、このときは Android 4.0.x (API level 14-15) のシェアが 5% を下回ったため minSdkVersion を 16 に更新しました。 その後、 Android 4.1 (API level 16) のシェアが5%を下回った際に minSdkVersion を見直す機会はありましたが、同じく Jelly Bean である 4.2 のシェアが高く 4.1 だけサポート外にしてもあまり効果が見込めないことから minSdkVersion の更新は行いませんでした。

そのような状況が1年近く続いていたのですが、最近クックパッドアプリだけでなく国内向けアプリ全体の minSdkVersion ポリシーを見直す機会があったため、その内容を書いていこうと思います。

minSdkVersion の定期的な更新が必要な理由

Android には Support Library という古いバージョンのOSに新しい機能をバックポートするためのライブラリがあります。(Google I/O 2018 ではさらに新機能も追加され Jetpack という枠組みが生まれました) また、 Google Play サービスや Firebase といったライブラリも独立したライブラリとして提供されているため、Android 4.0 (API level 14) 以上であればほとんどの機能を利用することができます。 Android 開発ではこれらのライブラリによって古いOSでもあってもある程度不自由なく開発や運用ができるようになっていますが、やはり限界は存在しています。

新機能のバックポートが遅い、または不十分である

最近では新しいOS(API level)の Developer Preview 提供とほぼ同時に新しい Support Library の alpha が提供されるようになりました。 しかし、その中でも古いOS向けにバックポートされていない新機能があり、どのOSバージョンでも最新OSと同じ機能を提供できるというわけではありません。 たとえば、 ImageView#setImageTintList() というメソッドは Android 5.0 (API level 21) から提供されていますが、 Support Library の AppCompatImageViewsetImageTintMode() が追加されたのは2017年7月リリースの v26 からで、 v25 で入った background tint のサポートからは7ヶ月遅れています。

また、同じくAndroid 5.0 (API level 21) で導入された JobScheduler は Android 5.0 以上でしか利用できず、過去のOS向けのバックポートである GcmNetworkManagerFirebase JobDispatcher でその機能をすべて置き換えることはできません。 その一方で JobScheduler 以前に利用されていた AlarmManagerWakefulBroadcastReceiver などの制限はOSバージョンアップのたびに厳しくなっており、ひとつの実装で全てのOSに同じ機能を提供することが難しくなっています。

このように古いOSが存在することでアプリの構成自体が複雑化していってしまうため、アプリの健全な開発効率を維持するためにも minSdkVersion の定期的な見直しは必要です。

バックポート不可能な機能の差異が存在する

例えば、以下のようなOSバージョンごとの差異は Support Library では埋めることができません。

  • WebView の挙動
    • Android 4.4 (API level 19)より前のバージョンではOSに組み込まれたWebViewコンポーネントが利用される
    • Android 4.4 (API level 19) では Chromium ベースになったがバージョンは固定で更新されない
    • Android 5.0 (API level 21) 以降では Chromium ベースの最新のコンポーネントが提供される
  • メディアサポート
    • 動画や静止画のサポート状況はOSバージョンによって異なる(ただし、 ExoPlayer など独自のメディアサポートを提供するライブラリは存在する)
    • MediaSessionなどの再生関連UIは 5.0 から追加された
  • TLS 1.1, 1.2サポート
    • TLS 1.1, 1.2 の実装は Android 4.1 から含まれているが、デフォルトで有効になったのは 5.0 から

これらの機能に強く依存したサービスの場合、 minSdkVersion を上げる以外の選択肢はなくなります。

スマートフォン・タブレット以外のプラットフォームサポート

Android Auto や Android TV といったプラットフォームは Android 5.0 (API level 5.0) から追加されました。これらの機能はより minSdkVersion の低いスマートフォン向けのアプリに同梱することもできますが、それぞれの機能は古いOSの端末から呼び出されることを想定していません。

これは極端な例ですが、Android TV で実際に発生した問題について説明してみましょう。Android TVではTV端末の判定のために UI_MODE_TYPE_TELEVISION を参照するように公式ドキュメントに書いてあります。 ところが、このフラグ自体はAPI level 1から存在するものであり、一部のSTB型端末はこのフラグが有効になっているため、 Android 4.0(API level 14) の端末であるにも関わらずTV端末として判定されます。 通常、TV はホームアプリが参照する category がスマートフォン・タブレットと異なるため画面が分離されていますが、上記のフラグだけに頼って TV 判定を行って leanback ライブラリの機能を呼び出したため、端末のAPIレベルに存在しないメソッドを呼び出してクラッシュすることがありました。(leanback ライブラリの minSdkVersion は 17 に設定されており、これより古いOSから呼び出した場合の動作は保証されません)

このような事故を防ぐために、 新しいプラットフォームをサポートする場合は minSdkVersion を見直したほうが良い場合もあります。

サポート外となったOSはどうなるのか?

これは新しいアプリのリリース後、以前のAPKをどうするかによって変わってきます。

何もしなかった場合、 minSdkVersion や uses-feature が異なるAPKが配信されると過去のAPKと新しいAPKは同時に配信され続けます。 この状態では最新のAPKでサポート外となった端末でも以前のAPKが新規にインストールできます。 この状態ではユーザーからは普通に自分の端末でアプリのインストールや利用ができるため、自身の端末がサポート外となったことはわかりません。

一方、Playコンソールから古いAPKを無効にすることもできます。 その場合、最新のAPKでサポートされなくなった端末ではアプリのインストールができなくなり、アプリのサポート外であることがわかるようになります。

OSのサポートバージョンを変更する方法として、 minSdkVersion の切り上げを行いつつ古いAPKは有効にしておき、古いOSのシェアが低くなった時点で過去のAPKも無効にする、とう方法も取ることができます。

minSdkVersion をどの値にするべきか?

minSdkVersion の設定値を決めるための基準は2つあります。

OSバージョンが一定のシェアを下回っているものをサポート外とする

minSdkVersion を上げる理由は主に開発・運用の効率化のためですが、当然サポート外となったOSバージョンにはアップデートにより最新のサービスを届けることができなくなってしまいます。 また、サポート外となったOS向けに配信されていたアプリに致命的なバグがあった場合、アップデートによる解決を行うこともできなくなります。

これらの問題によるユーザーへの悪影響を最小限にするため、クックパッドでは対象のOSバージョンが 5% を下回った場合に minSdkVersion を更新してもよい、というルールを設けています。 直近ではクックパッドアプリのOSバージョンごとのシェアは大まかに以下のようになっていました。

OSバージョン API level シェア
5.0.x 21 13.60%
4.4.x 19 7.9%
4.3.x 18 0.16%
4.2.x 17 3.67%
4.1.x 16 0.87%

Android 4.1-4.3 は一般に Jelly Bean と呼ばれているバージョンで、以前検討した際には「サポート対象外にするときはなるべく一緒のタイミングでやりたい」という判断にしていました。 以前は Andoroid 4.2(API level 17) のシェアが 5% を上回っていたため見送りましたが、今回は Jelly Bean 全体で合計しても 5% を下回っており、サポート外とすることができそう、という判断になります。

機能面・開発効率で比較して大きなメリットがありそうなものを閾値とする

前述の通り、通常の 5% ルールでは Jelly Bean をサポート外にできそうということがわかりました。 しかし、Android 4.4 (API level 19) もよく見るとシェア 7.9% という低めの値で、しかもひと月ごとに 0.5% を上回るペースで減少し続けていました。このままだと半年以内に 5% を切りそうです。 そこで、 minSdkVersion を Android 4.4 (API level 19) とした場合と Android 5.0 (API level 21) とした場合で簡単に比較してみることにしました。

  • Android 4.4 (API level 19)
    • AlarmManager の挙動変更やストレージ関連の変更など、挙動変更の閾値となる部分は多い。
      • ただし Android 5.0 では JobScheduler が導入され AlarmManager の用途が狭まっている
    • WebView が Chromium ベースになっており、 ウェブページ側の改修が楽
      • ただし WebView コンポーネントのアップデートは Android 5.0 から
  • Android 5.0 (API level 21)
    • JobScheduler 、Camera2 API など過去のOSでは利用できない大きな変更が多数含まれている。
    • Material Design にネイティブ対応しており、レイアウトXMLでの属性指定などにSDK側のものを利用できる。
    • 現状 JobScheduler を利用している料理きろくなどでOSバージョンごとの機能差が存在しているが、このバージョンまで minSdkVersion を引き上げることで内部の分岐がなくなる

上記を踏まえチーム内で議論した結果、今回の見直しで minSdkVersion を Android 5.0 (API level 21) とした場合、もっとも開発効率を引き上げることができるという結論になりました。ちょうど新規のアプリの開発・リリースがいくつか控えていたこともあり、半年後に再度 minSdkVersion を見直すよりも半年前倒しにして社内の国内向け全アプリに minSdkVersion 21 を適用することで大きなメリットがあると判断したためです。

社内でどのようにバージョンシェアの変更議論を進めたか

前述の通り「開発効率・運用工数の改善」という観点でみた場合、最も効果がありそうな閾値は Android 5.0 (API level 21)でしたが、Android 4.4(API level 19) の 7.9%のユーザーというシェアはかなり大きいものです。 すでに多数のユーザーを抱えるクックパッドアプリではサービス面での責任をもっている部署と何度も相談を重ねて慎重に進めていくことになりました。

一方これからリリースする新規のアプリでは既存ユーザーへの影響を考えなくて良いため、まずはそちらのチームにminSdkVersion を 21 から始めることのメリットについて「Android アプリの minSdkVersion(最小サポートOSバージョン) は Android 5.0 以降 にすべき」というブログを書いたり開発チームに直接説明したりして共有しました。 これらの取り組みの結果、cookpadTVアプリクックパッドMYキッチンアプリのいずれも minSdkVersion 21 からのスタートとなりました。今後リリースされる新規のアプリに関しても全て minSdkVersion 21 以上となる見込みです。

クックパッドアプリにおける適用は当初 Android 4.4 (API level 19) のユーザーシェア 7.9% という割合の多さから見送られそうになりましたが、アプリ全体のリファクタリングのための期間が始まるためその期間前に適用することがベストなタイミングであることを説明したり、ユーザーシェアの減少率の傾向やWebページの差し替えによる改善は引き続き可能であることを説明したり、経営層との「クックパッドアプリの開発を高速化するためにはどうすればよいか一旦数字を度外視して考えてみる」という場で取り上げたりした結果、近日中に minSdkVersion 21 に引き上げることになりました。 現在細かいリリース日時を調整中で、開発環境にももうすぐ反映見込みです。 minSdkVersion の更新後、代替リソースの整理やレイアウトファイルの見直しなどやりたいことがいっぱいで今からとても楽しみです。

おまけ

今回の取り組みの最中に minSdkVersion という謎のアカウントが値を 21 に更新していました。 世界的に minSdkVersion 21 の流れが来ているのだと思います。

最後に

今回はモバイル基盤の取り組みとしてAndroidアプリの minSdkVersion を 21 にした話を紹介しました。 モバイル基盤では今後も引き続きユーザーサポートとのバランスを取りながら開発効率を高める取り組みを行っていく予定です。 クックパッドではモバイル基盤と一緒に minSdkVersion 21 でアプリ開発を行いたい仲間、開発を効率化する仕組みづくりに興味がある仲間を募集しています

/* */ @import "/css/theme/report/report.css"; /* */ /* */ body{ background-image: url('https://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527163350.png'); background-repeat: repeat-x; background-color:transparent; background-attachment: scroll; background-position: left top;} /* */ body{ border-top: 3px solid orange; color: #3c3c3c; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; line-height: 1.8; font-size: 16px; } a { text-decoration: underline; color: #693e1c; } a:hover { color: #80400e; text-decoration: underline; } .entry-title a{ color: rgb(176, 108, 28); cursor: auto; display: inline; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; font-size: 30px; font-weight: bold; height: auto; line-height: 40.5px; text-decoration: underline solid rgb(176, 108, 28); width: auto; line-height: 1.35; } .date a { color: #9b8b6c; font-size: 14px; text-decoration: none; font-weight: normal; } .urllist-title-link { font-size: 14px; } /* Recent Entries */ .recent-entries a{ color: #693e1c; } .recent-entries a:visited { color: #4d2200; text-decoration: none; } .hatena-module-recent-entries li { padding-bottom: 8px; border-bottom-width: 0px; } /*Widget*/ .hatena-module-body li { list-style-type: circle; } .hatena-module-body a{ text-decoration: none; } .hatena-module-body a:hover{ text-decoration: underline; } /* Widget name */ .hatena-module-title, .hatena-module-title a{ color: #b06c1c; margin-top: 20px; margin-bottom: 7px; } /* work frame*/ #container { width: 970px; text-align: center; margin: 0 auto; background: transparent; padding: 0 30px; } #wrapper { float: left; overflow: hidden; width: 660px; } #box2 { width: 240px; float: right; font-size: 14px; word-wrap: break-word; } /*#blog-title-inner{*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-position: left 0px;*/ /*}*/ /*.header-image-only #blog-title-inner {*/ /*background-repeat: no-repeat;*/ /*position: relative;*/ /*height: 200px;*/ /*display: none;*/ /*}*/ /*#blog-title {*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-image: url('https://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527172848.png');*/ /*background-repeat: no-repeat;*/ /*background-position: left 0px;*/ /*}*/