【Android】CompileSdkVersion, TargetSdkVersionを26(Oreo)→28(Pie)に上げる対応をした話

はじめに

こんにちは、テクノロジー本部の三堀です。 AndroidにはCompileSdkVersion, TargetSdkVersionというものがありますが、それぞれ定期的にバージョンアップしないとGooglePlayストアでアプリを上げれなくなったり、新しいAPIを使えなかったりします。今回は、私が担当しているプロダクトでCompileSdkVersion, TargetSdkVersionを28にバージョンアップした際にやらなければならなかったことに関して記載していきます。

ちなみに28はAndroidPieのSDKバージョンとなっているので、Android Pieにて新たに加わったOSレベルの制約などに対応していった形になります。

また、対象となるアプリはまだSupportLibraryを使っているので、そちらにバージョンアップする上で必要だったことに関しても記載していきます。

今回対応した内容

  • SupportLibraryを28にアップデート
  • ClearTextによる通信の制限対応
  • Apache Http Clientがデフォルトで使用できなくなる
  • 透過してるscreenOrientaionが指定されてるActivityを起動すると落ちる

公式に出ている移行ガイドはこちらにあるので、基本的にこちらを参考にするとよいのかなと思います。

https://developer.android.com/about/versions/pie/android-9.0-changes-28?hl=ja

また、今回紹介する内容は、SdkVersionを28にする上で対応すべき内容を網羅的に解説したものではありません。実際に対応した内容のみを記載しています。アプリによっては本記事に記載していない対応が必要な場合もあるので、網羅的に知りたい方は上記ドキュメントを参考にしていただければ幸いです。

Support Libraryを28にアップデート

まず、SupportLibraryを使っている場合、TargetSdkVersionと追従する形でSupportLibraryのバージョンも上げなくてはなりません。 そこで、今回はSupportLibraryを28にアップデートしました。こちらを28にバージョンアップする上で必要になったことを記載します。

また、余談ですが、SupportLibraryは28を最後にサポートが終了になります。そのため今後新しい機能を使用したい場合は、AndroidXという新しいライブラリを使用する必要があるとのことです。こちらの対応も私のチームでは進めています。

getContext()ではなく、requireContext()を使う

これはKotlinから使用する前提の話ですが、Fragmentからcontextを使用する際のメソッドを変更する必要があります。

SupportLibrary28からgetContext()は@Nullableアノテーションがつき、Kotlinから呼び出すとContext?を返すようになります。

したがって、non-nullのContextを取得したいときには、@NonNullアノテーションがついているrequireContext()という新たなメソッドで取得する必要が出てきます。

FragmentのgetArguments()に@Nullableアノテーションがつく

これまたFragmentの話ですが、今までFragmentからargumentsを取得したいときにはgetArguments()使っていました。SupportLibrary28では、このgetArguments()が@Nullableになります。

requireContext()のようにnon-nullのものが取得できればよいのですが、getArguments()に関してはそれはできず、Kotlinから取得する際には、Bundle?で取得し、nullチェックをして中のargumentを取り出す必要があります。

私のチームでは、以下のようにargumentsがないFragmentに対してargumentsを取得してしまったときに、例外を出すようにするrequireArguments()というメソッドをFragmentの基底クラスに実装しています。

これをすることで、argumentsがないFragmentに対してgetArgumentsをする必要性はないですし、NonNullのargumentsを取得することができます。

    @NonNull
    protected Bundle requireArguments() {
        Bundle args = getArguments();
        if (args == null) {
            throw new IllegalStateException("Fragment $this doesn't have arguments.");
        } else {
            return args;
        }
    }

LargeTestアノテーションのパッケージが変わる

こちらはUIテストを書いている場合、対応が必要になると思います。

28に上げた際に、LargeTestアノテーションにて以前のandroid.test.suitebuilder.annotationのものが使えなくなっていたので、android.support.test.filtersのものに変える必要がありました。

移行前: import android.test.suitebuilder.annotation.LargeTest;

移行後: import android.support.test.filters.LargeTest;

Android8.0で透過してるかつscreenOrientaionが指定されてるActivityを起動すると落ちる

これは正確には28ではなく27に上げたときに起こるバグだそうです。ちなみにAndroid8.1からはこちらは修正されているので再現しません。

このバグは以下の条件を満たすと発生するバグのようです。

  • TargetSdkVersionが27以上
  • 使用端末のOSがOreo(8.0)
  • AndroidManifestにてActivityに、<item name="android:windowBackground">@android:color/transparent</item> <item name="android:windowIsTranslucent">true</item>のThemeが設定されている
  • screenOrientationがportraitかlandscapeで固定されている

対応策としては、screenOrientationを以下のようにbehindにすることで対応しました。

android:screenOrientation="behind"

こちらの記事を参考にさせていただきました。

tech.studyplus.co.jp

ClearTextによる通信の制限対応

AndroidPieからは、ClearTextによる通信、つまりhttpでの通信は制限され、例外をスローします。

httpで通信したいのであれば、ホワイトリスト的に以下のようにAndroidManifest内にnetworkSecurityConfigを設定しなければなりません。

<!--AndroidManifest.xml-->
<application
        ...
        android:networkSecurityConfig="@xml/network_security_config">
    ...
</application>
<!--res/xml/network_security_config.xml-->
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">hoge.com</domain> <!--http通信を許可するドメインを記述する-->
    </domain-config>
</network-security-config>

ApacheHttpClientを使っている場合は、AndroidManifestに<uses-library>を明示的に宣言する必要がある

ここが一番落とし穴でした。ApacheHttpClientはAndroid6.0からdeprecatedになっていましたが、まだ使える状態ではありました。ただ、Android PieではこちらのAPIがついに削除されました。そのため今までApacheHttpClientを使っている場合は、参照するものがないので、NoClassDefFoundErrorがスローされクラッシュします。

プロダクトコードで使っていなくても、古いバージョンの依存ライブラリが以外と使っている場合があるので、こちらは要確認です。念の為書いておいた方が安全かもしれません。

<!--AndroidManifest.xml-->
<application
    ... >
    <!--以下を記述-->
    <uses-library
         android:name="org.apache.http.legacy"
         android:required="false" />
    ...
</application>

ちなみに私の場合は、PlayServiceが古めのバージョンを使っており、それがどうやらApacheHttpClientに依存していたために、AdsのライブラリがExceptionを吐いていました。ただ、AndroidPie以上の端末を使用していれば、必ず再現するというわけではないようです。

他にもGoogleMapのSDKが同様のエラーを吐いていたりする現象をいくつかの記事で確認しているのでそのあたりのライブラリを使用している方は要注意です。

こちらの方の記事を参考にさせていただきました。

https://qiita.com/devnokiyo/items/60823d74c71c4ebb5309

終わりに

対応すべき内容が依存しているライブラリにおいても該当することがあったため、対応必要箇所を洗い出すのが難しい部分がありました。

次はSdkVersion29が出てきているので、その際も対応すべき事項をちゃんとキャッチアップして対応していきたいです。

最後までご覧いただきありがとうございました。