新しい機能に加えて、Android 8.0 では、さまざまなシステムおよび API の動作が変更されています。このドキュメントでは、アプリ開発において把握しておくべき主な変更点について説明します。
これらの変更点の多くは、対象とする Android バージョンに関係なく、すべてのアプリに影響するものです。ただし、Android 8.0 を対象とするアプリにのみ影響する変更点もあります。このページではそれらを明確にするために、すべての API レベルを対象としたアプリと Android 8.0 を対象としたアプリの 2 つに分けて説明します。
すべての API レベルを対象としたアプリ
これらの動作の変更点は、対象となる API レベルに関係なく、Android 8.0 プラットフォームで実行される
バックグラウンド実行制限
電池寿命を伸ばすために Android 8.0 に導入された変更点の 1 つとして、アプリがアクティブなコンポーネントのないキャッシュ状態になると、そのアプリが保持しているすべての WakeLock が解放されます。
また、端末のパフォーマンスを改善するために、フォアグラウンドで実行されていないアプリの特定の動作はシステムによって制限されます。具体的には次のような制限があります。
- バックグラウンドで実行されているアプリがバックグラウンド サービスにアクセスできる頻度と時間に制限が設定されました。
- 大半の暗黙的なブロードキャスト(特定のアプリを対象としていないブロードキャスト)は、アプリでマニフェストを使用して登録することができません。
デフォルトでは、これらの制限は O を対象とするアプリのみに適用されますが、アプリが O を対象としていない場合でも、ユーザーは [Settings] 画面でアプリに対してこれらの制限を有効にすることができます。
Android 8.0 には次の特定のメソッドの変更も含まれます。
- バックグラウンド サービスの作成が許可されない状況下で Android 8.0 を対象とするアプリが
startService()
メソッドを呼び出した場合、IllegalStateException
がスローされます。 - 新しい
Context.startForegroundService()
メソッドはフォアグラウンド サービスを起動します。アプリがバックグラウンドにあるときに、そのアプリによるContext.startForegroundService()
の呼び出しが許可されます。ただし、アプリは、サービスが作成されてから 5 秒以内にそのサービスのstartForeground()
メソッドを呼び出す必要があります。
詳細については、バックグラウンド実行制限をご覧ください。
Android のバックグラウンド位置情報の制限
Android 8.0 搭載の端末では、電池の消耗を抑え、ユーザー エクスペリエンスやシステムの健全性を保つために、バックグラウンド アプリが最新の位置情報を受信する頻度を低くしました。この変更は、Google Play サービスなどの最新の位置情報を受信するすべてのアプリに影響します。
具体的には以下の API に影響があります。
- Fused Location Provider(FLP)
- Geofencing
- GNSS Measurements
- Location Manager
- Wi-Fi Manager
アプリが想定どおりに動作していることを確認するには、次の手順を実行します。
- アプリのロジックをレビューし、最新の Location API を使用していることを確認します。
- 各ユースケースに対して想定している動作がアプリで発生しているかをテストします。
- ユーザーの現在地情報に応じたユースケースを処理するため、Fused Location Provider(FLP)または Geofencing を使用することをご検討ください。
これらの変更点の詳細については、バックグラウンド位置情報の制限をご覧ください。
アプリ ショートカット
Android 8.0 では、アプリのショートカットが次のように変更されています。
com.android.launcher.action.INSTALL_SHORTCUT
ブロードキャストは、プライベートで暗黙的なブロードキャストになったため、アプリに影響を与えることはなくなりました。代わりに、ShortcutManager
クラスのrequestPinShortcut()
メソッドを使ってアプリのショートカットを作成する必要があります。ACTION_CREATE_SHORTCUT
インテントによって、ShortcutManager
クラスを使用して管理するアプリ ショートカットを作成できるようになりました。このインテントでは、ShortcutManager
とやり取りをしない以前のランチャーのショートカットも作成できます。これまで、このインテントでは以前のランチャーのショートカットしか作成できませんでした。requestPinShortcut()
を使用して作成されたショートカットとACTION_CREATE_SHORTCUT
インテントを処理するアクティビティで作成されたショートカットは本格的なアプリ ショートカットです。その結果として、アプリはShortcutManager
でメソッドを使用してショートカットをアップデートできます。- 以前のショートカットは、Android の前のバージョンからの機能を引き継いでいますが、それらをアプリ内でアプリ ショートカットに手動で作り替える必要があります。
アプリ ショートカットの変更の詳細については、プレビュー機能ガイドのピン留めされたショートカットとウィジェットをご覧ください。
ロケールと国際化
Android 7.0(API レベル 24)において、デフォルトのカテゴリ ロケールを指定できる仕組みが導入されました。ただし API によっては、デフォルトの DISPLAY
カテゴリ ロケールを使用すべき場面でも、汎用的な Locale.getDefault()
メソッドを引数なしで使い続けているものありました。Android 8.0 においては、次のメソッドで Locale.getDefault()
の代わりに Locale.getDefault(Category.DISPLAY)
を使用しています。
Locale
引数に指定された displayScript の値が利用できない場合は、Locale.getDisplayScript(Locale)
も Locale.getDefault()
にフォールバックします。
追加されたロケールと国際化に関連する変更内容は次のとおりです。
- ドキュメントに記載された動作と同じように、
Currency.getDisplayName(null)
を呼び出すとNullPointerException
がスローされます。 - タイムゾーン名の解析方法が変更されました。これまでの Android 端末では、起動時にシステム クロックの値をサンプリングして、日時の解析に使用するタイムゾーン名をキャッシュしていました。その場合、起動時やその他のまれなケースにおいてシステム クロックの値が正しくないと、解析に悪影響が生じてしまいます。
今回のバージョンからは、一般的なケースでは解析ロジックに ICU と現在のシステム クロックの値を使用してタイムゾーン名を解析するようになっています。この変更によって、アプリで
SimpleDateFormat
などのクラスを使用している場合は、以前の Android バージョンよりも精度の高い結果が得られる場合があります。 - Android 8.0 では、ICU のバージョンが 58 に更新されています。
警告ウィンドウ
アプリで SYSTEM_ALERT_WINDOW
パーミッションと以下のウィンドウ タイプのいずれかを使用して、他のアプリやシステム ウィンドウ上に警告ウィンドウを表示しようとしている場合:
このような場合、警告ウィンドウは必ず TYPE_APPLICATION_OVERLAY
ウィンドウ タイプを使用しているウィンドウの下に表示されます。Android 8.0 を対象とするアプリの場合は、TYPE_APPLICATION_OVERLAY
ウィンドウ タイプを使用して警告ウィンドウを表示します。
詳細については、Android 8.0 を対象としたアプリの動作変更の説明に含まれる警告ウィンドウの共通ウィンドウ タイプのセクションをご覧ください。
入力とナビゲーション
Chrome OS やタブレットなどの大きなフォーム ファクタでの Android アプリの登場と合わせて、Android アプリで使われているキーボード ナビゲーションが復活します。Android 8.0 で、ナビゲーション入力デバイスとしてのキーボードの使用に再度対応しました。その結果、矢印およびタブベース ナビゲーションに対して信頼性が高い予測可能なモデルが実現しました。
特に、要素のフォーカス動作に対して次の点を変更しました。
-
View
オブジェクト(その前景または背景ドローアブル)にフォーカスが当たった状態の色を定義していないと、フレームワークはView
にデフォルトのフォーカス ハイライト色を設定するようになりました。このフォーカス ハイライトは、アクティビティのテーマに基づく波紋ドローアブルです。View
オブジェクトにフォーカスが当たった場合に、このデフォルトのハイライトを使用しないようにしたい場合、View
を含むレイアウト XML ファイルでandroid:defaultFocusHighlightEnabled
属性をfalse
に設定するか、アプリの UI ロジックでsetDefaultFocusHighlightEnabled()
にfalse
を渡します。 - キーボード入力が UI 要素のフォーカスにどのように影響を与えているかテストする場合は、[Drawing] > [Show layout bounds] デベロッパー オプションを有効にします。 Android 8.0 では、このオプションにより、現在フォーカスのある要素の上に、「X」アイコンが表示されます。
また、Android 8.0 のすべてのツールバー要素は自動的にキーボード ナビゲーション クラスタとなり、各ツールバー全体の内外へのナビゲーションが簡単にできるようになります。
アプリで使用されるキーボード ナビゲーションに対するサポートを改善する方法について詳しくは、キーボード ナビゲーションのサポートガイドをご覧ください。
ウェブフォーム自動入力
Android のオートフィル フレームワークが、オートフィル機能へのサポートを内蔵するようになったことを受け、WebView
オブジェクトに関連する次のメソッドが変更されました。この変更は、Android 8.0 を実行する端末にインストールされたアプリを対象としています。
WebSettings
-
getSaveFormData()
メソッドは、false
を返すようになります。以前は、このメソッドはtrue
を返していました。setSaveFormData()
を呼び出しても、何の効果も発生しません。
WebViewDatabase
-
clearFormData()
を呼び出しても、何の効果も発生しません。hasFormData()
メソッドは、false
を返すようになります。以前は、フォームがデータを含んでいた場合、このメソッドはtrue
を返していました。
ユーザー補助機能
ユーザー補助機能サービスが、アプリの TextView
オブジェクト内にあるすべての ClickableSpan
インスタンスを認識するようになりました。
アプリを利用しやすくする方法の詳細については、ユーザー補助機能の説明をご覧ください。
ネットワークと HTTP(S) 接続
Android 8.0 では、ネットワークと HTTP(S) 接続の動作が次のように変更されています。
- 本文のない OPTIONS リクエストには
Content-Length: 0
ヘッダーが含まれます。これまでContent-Length
ヘッダーはありませんでした。 - HttpURLConnection では、空のパスを含む URL を正規化するために、ホスト名や認証局名の後にスラッシュを追加します。たとえば、
http://example.com
の場合はhttp://example.com/
になります。 - ProxySelector.setDefault() によって設定されたカスタム プロキシ セレクターは、リクエストされた URL のアドレス(スキーム、ホスト、ポート)のみをターゲットにします。よって、これらの値のみに基づいてプロキシが選択される場合があります。カスタム プロキシ セレクターに渡された URL には、リクエストされた URL のパス、クエリ パラメータ、フラグメントは含まれません。
- URI に空のラベルを含めることはできません。
これまでは、ホスト名に含まれる空のラベルを受け入れるための回避策(URI の使用法としては不適切)がプラットフォームでサポートされていました。これは、以前リリースされた libcore に対する互換性を保つための回避策です。この API を適切に使用していないと、次のような ADB メッセージが表示されます。「URI example..com has empty labels in the hostname.This is malformed and will not be accepted in future Android releases.」Android 8.0 ではこの回避策が廃止されているため、不正な形式の URI に対しては null が返されます。
- Android 8.0 の HttpsURLConnection 実装では、安全ではない TLS/SSL プロトコル バージョンのフォールバックは実施されません。
- HTTP(S) 接続をトンネリングする処理は次のように変更されました。
- システムではネットワーク上に HTTP(S) のトンネリング接続を確立するために、ポート番号(:443)を Host ラインに正しく設定して、その情報を中間サーバーに送信します。これまでポート番号は CONNECT ラインでのみ生じていました。
- トンネリングされたリクエストからプロキシ サーバーに対して、ユーザー エージェントやプロキシ認証ヘッダーが送信されなくなりました。
トンネルの設定時に、システムではトンネリングされた Http(s)URLConnection 上のプロキシ認証ヘッダーをプロキシに送信しなくなりました。代わりに、プロキシ認証ヘッダーを生成して、プロキシが初期リクエストへの応答として HTTP 407 を送信した時に、生成したヘッダーをプロキシに送信します。
同様に、システムではユーザー エージェントのヘッダーをトンネリングされたリクエストからトンネルを設定したプロキシ リクエストにコピーしなくなりました。このリクエスト用のユーザー エージェント ヘッダーはライブラリによって生成されます。
- 前に実行された connect() メソッドが失敗していた場合、
send(java.net.DatagramPacket)
メソッドは SocketException をスローします。- 内部エラーが発生していない場合、DatagramSocket.connect() は pendingSocketException を設定します。Android 8.0 よりも前のバージョンでは、send() の呼び出しに成功していても、後続する recv() の呼び出しで SocketException がスローされていました。現在は、どちらを呼び出しても一貫して SocketException がスローされます。
- InetAddress.isReachable() では、TCP Echo プロトコルにフォールバックする前に ICMP を試みます。
- ポート 7(TCP Echo)をブロックする google.com などの一部のホストも、ICMP Echo プロトコルに対応していればアクセス可能になっている可能性があります。
- 完全にアクセス不可能なホストにおいては、この変更によって呼び出しの応答時間が 2 倍になります。
Bluetooth
Android 8.0 では、ScanRecord.getBytes()
メソッドで取得するデータ長に対して次のような変更が行われています。
getBytes()
メソッドでは、受信するバイト数を推定しません。従って、アプリは返されるバイト数の最小値または最大値に依存しないようにしてください代わりに、返される配列の長さをアプリ側で見積もる必要があります。- Bluetooth 5 と互換性のある端末では、以前の上限である 60 バイトを超える長さのデータが返される場合があります。
- リモート端末がスキャン応答を返さない場合は、60 バイト未満のデータが返される場合もあります。
シームレスな接続
Android 8.0 では、Wi-Fi 設定に対して多くの改善を実施しており、これにより、最高のユーザー エクスペリエンスを提供する Wi-Fi ネットワークを容易に選択できるようになります。具体的な変更点は以下を含みます。
- 安定性と信頼性の改善。
- より直感的で見やすい UI。
- 統合された単一の Wi-Fi 設定メニュー。
- 互換性のある端末では、高品質のネットワークとして保存されたネットワークが近くにある場合、自動的に Wi-Fi がアクティベートされます。
セキュリティ
Android 8.0 でのセキュリティ関連の変更点は次のとおりです。
- このプラットフォームでは SSLv3 がサポートされなくなりました。
- TLS プロトコル バージョンのネゴシエーションを適切に実装していないサーバーへの HTTPS 接続を確立する際に、
HttpsURLConnection
では古い TLS プロトコル バージョンにフォールバックしてリトライするという回避策を試みなくなります。 - Android 8.0 は、すべてのアプリに Secure Computing(SECCOMP)フィルタを適用します。許可されたシステムコールのリストは、Bionic を通じて公開されたものに制限されます。他にも下位互換性を確保するために提供されているシステムコールがいくつかありますが、それらの使用は控えることをお勧めします。
- アプリの
WebView
オブジェクトはマルチプロセス モードで実行されます。セキュリティを強化するために、ウェブ コンテンツは、そのコンテンツを含むアプリのプロセスとは別の独立したプロセスとして処理されます。 -
-1 や -2 などで終わる名前のディレクトリ内に APK があると想定することはできなくなりました。アプリでは
sourceDir
を使用してディレクトリを取得し、ディレクトリのフォーマットに直接的に依存しないようにする必要があります。 - ネイティブ ライブラリの使用に関連するセキュリティの機能強化について詳しくは、ネイティブ ライブラリの説明をご覧ください。
アプリのセキュリティの向上に関する補足のガイドラインについては、Android デベロッパー向けセキュリティの説明をご覧ください。
プライバシー
Android 8.0 プラットフォームにおけるプライバシー関連の変更点は次のとおりです。
- このプラットフォームでは、識別子の処理方法が変更されています。
-
Android 8.0(API レベル 26)のバージョンへの OTA 以前にインストールされたアプリについては、一度アンインストールし、OTA 後に再インストールしない限り、
ANDROID_ID
の値は同じままになります。OTA 後のアンインストールの前後で値を維持したい場合には、デベロッパーは、 Key/Value Backupを使って新旧の値を紐付けすることができます。 - Android 8.0 を実行する端末にインストールされたアプリについては、
ANDROID_ID
の値は、アプリ サインキーごと、またユーザーごとに範囲付けされています。ANDROID_ID
の値はアプリ サインキー、ユーザー、端末の組み合わせごとに一意です。その結果、たとえユーザーが同じでも、同一端末上で動作する、異なるサインキーを持つアプリには、同じ Android ID は使われません。 - サインキーが同じであり、アプリが O のバージョンへの OTA 以前にインストールされたものでない場合、
ANDROID_ID
の値が、パッケージのアンインストールと再インストールにより変わることはありません。 - システム アップデートにより、パッケージのサインキーが変わった場合でも、
ANDROID_ID
の値は変わりません。
アプリを収益化するためのシンプルで標準的なシステムには、広告 ID を使用してください。広告 ID は Google Play サービスによって提供される広告用の一意の ID で、ユーザーによるリセットが可能です。
-
Android 8.0(API レベル 26)のバージョンへの OTA 以前にインストールされたアプリについては、一度アンインストールし、OTA 後に再インストールしない限り、
net.hostname
システム プロパティのクエリ結果は null になります。捕捉されていない例外のログ記録
デフォルトの Thread.UncaughtExceptionHandler
に対して呼び出しを行わない Thread.UncaughtExceptionHandler
をアプリでインストールする場合、捕捉されていない例外が発生した際にシステム側でアプリを終了することはありません。Android 8.0 以降は、この状況では例外のスタックトレースのログが記録されます。前のバージョンのプラットフォームでは、例外のスタックトレースのログは記録されていませんでした。
カスタムの Thread.UncaughtExceptionHandler
では、必ずデフォルトのハンドラに対して呼び出しを行うことをお勧めします。そのように実装したアプリは、Android 8.0 での変更による影響を受けません。
連絡先プロバイダの利用統計の変更
これまでのバージョンの Android では、Contacts Provider コンポーネントによって各連絡先の利用統計を取得することが可能でした。この利用統計データには、連絡先に関連付けられた各メールアドレスや電話番号、その連絡先に連絡した回数、最後に連絡した日時などの情報が含まれます。READ_CONTACTS
パーミッションをリクエストするアプリでは、このデータを読み込むことができます。
このデータをアプリで読み込むには READ_CONTACTS
パーミッションをリクエストします。Android 8.0 以降は、利用統計データを要求すると、正確な値ではなく概算値が返されます。Android システム側では正確な値を内部的に保持しているため、この変更によってオートコンプリートの API に影響が生じることはありません。
この動作変更の影響を受けるクエリ パラメータは次のとおりです。
コレクションの処理
AbstractCollection.removeAll()
と AbstractCollection.retainAll()
は、必ず NullPointerException
をスローするようになりました。これまでは、コレクションが空の場合、NullPointerException
がスローされませんでした。この変更により、実際の動作がドキュメントと一致するようになります。
エンタープライズ向け Android
Android 8.0 では、デバイス ポリシー コントローラ(DPC)などのエンタープライズ アプリに関して、複数の API と機能の動作を変更しています。変更点は、以下のとおりです。
- 新しい動作によって、完全管理対象端末で、アプリが仕事用プロファイルに対応できるようにします。
- システムアップデートの処理、アプリの検証と認証を変更し、端末とシステムの整合性を高めます。
- プロビジョニング、通知、[Recents] 画面、always-on VPN に関するユーザー エクスペリエンスが向上しました。
Android 8.0 でのすべてのエンタープライズの変更を確認し、アプリにどのように影響するかを把握するには、ビジネスで使用される Androidをご覧ください。
Android 8.0 を対象としたアプリ
これらの動作の変更点は、O プラットフォーム以降を対象としたアプリのみに適用されます。Android 8.0 以降を対象にしてコンパイルするアプリ、または Android 8.0 以降に対して targetSdkVersion
を設定するアプリの場合、必要に応じてアプリを修正して、これらの変更点に適切に対応する必要があります。
警告ウィンドウ
SYSTEM_ALERT_WINDOW
パーミッションを使用するアプリでは、以下のウィンドウ タイプを使用して他のアプリやシステム ウィンドウ上に警告ウィンドウを表示できなくなりました。
代わりに、TYPE_APPLICATION_OVERLAY
という新しいウィンドウ タイプをアプリで使用する必要があります。
TYPE_APPLICATION_OVERLAY
ウィンドウ タイプを使用してアプリ用の警告ウィンドウを表示する場合は、以下の新しいウィンドウ タイプの特性を考慮するようにしてください。
- アプリの警告ウィンドウは、ステータスバーや IME などの重要なシステム ウィンドウの下に常に表示されます。
- システムでは
TYPE_APPLICATION_OVERLAY
ウィンドウ タイプを使用するウィンドウの位置やサイズを調整して、画面上の見栄えをよくすることができます。 - ユーザーは、通知シェードを開くことで、
TYPE_APPLICATION_OVERLAY
ウィンドウ タイプを使用して表示する警告ウィンドウがアプリによって表示されないようにする設定にアクセスできます。
コンテンツ変更通知
Android 8.0 を対象とするアプリに対する ContentResolver.notifyChange()
と registerContentObserver(Uri, boolean, ContentObserver)
の動作方法が変更されています。
これらの API では、すべての URI の認証局に有効な ContentProvider
が定義されている必要があります。関係するパーミッション付きの有効な ContentProvider
を定義することで、悪意のあるアプリが既存のアプリのコンテンツを変更できないようにし、プライベート データが悪意のあるアプリに流出しないようにします。
View のフォーカス
クリックできるView
オブジェクトが、デフォルトでフォーカス可能になりました。もし、View
オブジェクトを、クリックはできるがフォーカスはできないようにしたい場合、 View
を含むレイアウト XML ファイル内で android:focusable
属性をfalse
に設定するか、アプリの UI ロジックで、false
をsetFocusable()
にパスインしてください。
セキュリティ
アプリのネットワーク セキュリティ構成がクリアテキスト トラフィックへの対応をオプトアウトした場合、アプリの WebView
オブジェクトは HTTP からウェブサイトにアクセスすることができなくなります。代わりに WebView
オブジェクトごとに HTTPS を使用してください。
アプリのセキュリティの向上に関する補足のガイドラインについては、Android デベロッパー向けセキュリティの説明をご覧ください。
アカウント アクセスと検出の許可
認証システム側でユーザーのアカウントを所有しているか、ユーザーがアクセス権を付与していない限り、アプリ側でユーザー アカウントにアクセスできなくなりました。現在は GET_ACCOUNTS
パーミッションだけでは不十分です。アカウントへのアクセス権を付与するには、AccountManager.newChooseAccountIntent()
または認証システム固有のメソッドを使用する必要があります。アカウントへのアクセス権を付与されたアプリは、AccountManager.getAccounts()
を呼び出してそのアカウントにアクセスできるようになります。
Android 8.0 では LOGIN_ACCOUNTS_CHANGED_ACTION
が廃止されています。実行時にアカウントの更新情報を取得するには、代わりにアプリで addOnAccountsUpdatedListener()
を使用してください。
アカウント アクセスと検出の許可に関する新規追加された API およびメソッドの情報については、本ドキュメントの新規 API のセクションに含まれるアカウント アクセスと検出の許可の説明をご覧ください。
プライバシー
Android 8.0 でのプライバシーに影響する変更点は次のとおりです。
-
プライバシー強化のため、このプラットフォームではシステム プロパティの
net.dns1
、net.dns2
、net.dns3
、およびnet.dns4
は利用できません。 -
DNS サーバーなどのネットワーク情報を取得するには、
ACCESS_NETWORK_STATE
パーミッションが付与されたアプリで、NetworkRequest
またはNetworkCallback
オブジェクトを登録してください。これらのクラスは Android 5.0(API レベル 21)以上で利用できます。 -
Build.SERIAL へのサポートは終了します。ハードウェアのシリアル番号が必要なアプリでは、代わりに新しい
Build.getSerial()
メソッドを使用してください。このメソッドにはREAD_PHONE_STATE
パーミッションが必要です。 -
現在
LauncherApps
API では、仕事用プロファイルのアプリによるプライマリ プロファイル情報の取得を許可していません。ユーザーが仕事用プロファイルを使用している場合、LauncherApps
API は、同じプロファイル グループに属する他のプロファイルにアプリが 1 つもインストールされていないかのように振る舞います。関連のないプロファイルに対してアクセスを試みた場合は、これまでと同様に SecurityException が発生します。
パーミッション
Android 8.0 よりも前のバージョンでは、アプリが実行時にパーミッションをリクエストして、そのパーミッションが付与された場合、同じパーミッション グループに属し、かつマニフェストに登録されている残りのパーミッションもアプリに不適切に付与されていました。
Android 8.0 を対象にしたアプリでは、この動作が修正されています。つまり、アプリには明示的にリクエストしたパーミッションのみが付与されます。ただし、いったんユーザーが任意のパーミッションをアプリに付与すると、そのパーミッション グループに属するパーミッションに対する後続のリクエストは自動で許可されます。
たとえば、あるアプリのマニフェストに READ_EXTERNAL_STORAGE
と WRITE_EXTERNAL_STORAGE
が含まれていると仮定します。そのアプリが READ_EXTERNAL_STORAGE
をリクエストして、ユーザーがそれを承認したとしましょう。API レベル 24 以下を対象とするアプリの場合は、WRITE_EXTERNAL_STORAGE
も同時に付与されます。これらは同じ STORAGE
パーミッション グループに属しており、どちらもマニフェストに登録されているためです。一方 Android 8.0 を対象とするアプリの場合は、READ_EXTERNAL_STORAGE
しか付与されません。ただし、後からアプリで WRITE_EXTERNAL_STORAGE
をリクエストした場合は、ユーザーに確認することなく即座にそのアクセス権が付与されます。
メディア
- フレームワークはオーディオのダッキングを実施します。
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
の場合、アプリはフォーカスを失いません。ダッキングの代わりに一時停止を必要とするアプリ向けに新規 API が利用可能です。ただしこの動作は、Android 8.0 デベロッパー プレビュー 1 リリースでは実装されていません。 - ユーザーが電話を受けた場合、アクティブなメディア ストリームは通話中にミュート状態になります。
- すべてのオーディオ関連の API は、オーディオ ストリーム タイプではなく、
AudioAttributes
を使って、オーディオ再生ユース ケースを表す必要があります。今後は、ボリューム コントロールにのみオーディオ ストリーム タイプを使用してください。ストリーム タイプを他の目的(廃止予定のAudioTrack constructor
など)で使用することはできますが、その場合、システムにはエラーとして記録されます。 AudioTrack
を使用しているときにアプリが大きなオーディオ バッファをリクエストすると、フレームワークでは、利用可能な場合、ディープ バッファ出力の使用が試行されます。- Android 8.0 では、メディア ボタン イベントの処理が異なります。
- メディア ボタンの UI アクティビティでの処理は変更ありません。メディア ボタンを処理する上で、フォアグラウンドのアクティビティが今後も優先されます。
- もしフォアグラウンドのアクティビティがメディア ボタンを処理しない場合、システムは、そのメディア ボタンを、最も最近ローカルでオーディオを再生したアプリに回します。メディア ボタン イベントを受け取るアプリを決定する際には、メディア セッションのアクティブ ステイタス、フラグ、再生ステイタスは考慮されなくなります。アプリがsetActive(false)を呼び出した後も、メディア セッションは、メディア ボタン イベントを受け取ることができます。
- アプリのメディア セッションがリリースされている場合、システムは、メディア ボタン イベントを、アプリのMediaButtonReceiverに送信します。
- そのほかの場合、システムは、メディア ボタン イベントを破棄します。間違ったアプリを再生するくらいなら、何も再生しない方がいいからです。
新しいメディア ボタンの処理順序ロジックをまとめると、以下の図のようになります。
ネイティブ ライブラリ
Android 8.0 を対象とするアプリでは、書き込みと実行の両方が可能なロード セグメントが含まれているネイティブ ライブラリはロードされません。この変更により、不正なロード セグメントを含むネイティブ ライブラリを使用しているアプリは機能しなくなる可能性があります。これはセキュリティ強化のための対策です。
詳細については、書き込みおよび実行が可能なセグメントの説明をご覧ください。
これまでの Developer Preview リリースと同様に、Android 8.0 でもリンカーに関連するあらゆる問題が可視化されています。リンカーの変更は、アプリが対象とする API レベルに紐付けられています。対象とする API レベルでリンカーに変更があった場合、アプリはライブラリをロードできません。リンカーに変更があった API レベルよりも低いレベルを対象としている場合は、logcat に警告が表示されます。Preview リリースの期間中は、logcat だけでなくトーストにもリンカーに関連する問題が表示されます。この変更によって、警告がエラーに変わる API レベルに至る前に問題が明確になります。
コレクションの処理
Android 8.0 では、List.sort()
で Collections.sort()
が実装されています。Android 7.x(API レベル 24 と 25)では、これとは逆の実装になっていました。つまり、デフォルトでは List.sort()
の実装で Collections.sort()
を呼び出していました。
この変更によって、Collections.sort()
では List.sort()
の最適化された実装を活用できるようになりますが、以下の制限も伴います。
List.sort()
の実装ではCollections.sort()
を呼び出さないでください。呼び出すと、無限回の再帰処理によってスタック オーバーフローが発生します。List
の実装でデフォルトの動作が必要な場合は、sort()
をオーバーライドしないようにします。親クラスで
sort()
が適切に実装されていない場合は、List.toArray()
、Arrays.sort()
、およびListIterator.set()
で実装された内容でList.sort()
をオーバーライドしても一般的に問題はありません。次に例を示します。@Override public void sort(Comparator<? super E> c) { Object[] elements = toArray(); Arrays.sort(elements, c); ListIterator<E> iterator = (ListIterator<Object>) listIterator(); for (Object element : elements) { iterator.next(); iterator.set((E) element); } }
ほとんどの場合は、API レベルに応じて異なるデフォルト実装が適用される実装によって
List.sort()
をオーバーライドすることもできます。次に例を示します。@Override public void sort(Comparator<? super E> comparator) { if (Build.VERSION.SDK_INT <= 25) { Collections.sort(this); } else { super.sort(comparator); } }
すべての API レベルで
sort()
メソッドを利用可能にするためだけに後者の方法をとる場合は、sort()
をオーバーライドする代わりに、sortCompat()
などの一意の名前をつけることを検討してください。-
Collections.sort()
は、sort()
を呼び出す List 実装の構造変更と見なされるようになりました。たとえば、Android 8.0 より前のバージョンのプラットフォームでは、ArrayList
を順次処理して、その反復処理の途中でsort()
を呼び出している場合は、List.sort()
を呼び出してソートを行うとConcurrentModificationException
がスローされていました。一方Collections.sort()
では例外がスローされませんでした。今回の変更によってプラットフォームの動作が統一され、どちらのアプローチでも
ConcurrentModificationException
がスローされるようになっています。
クラス読み込み動作
Android 8.0 は、新しいクラスを読み込むときに、クラスローダがランタイムの推測どおりに機能するかどうか確認するためのチェックを行います。このチェックでは、クラスが Java(forName()
から)、Dalvik バイトコード、JNI を使用して参照されるかどうかが実行されます。プラットフォームは、Java から loadClass()
メソッドへの直接呼び出しのインターセプトも、この呼び出しの結果のチェックもしません。この動作は、適切に動作するクラスローダの機能には影響しません。
プラットフォームは、クラスローダから戻るクラス記述子が、予測された記述子と一致しているかチェックします。返された記述子が一致しない場合、プラットフォームは NoClassDefFoundError
エラーをスローし、不一致を記述した詳細なメッセージを例外に保存します。
プラットフォームは要求されたクラスの記述子が有効かどうかもチェックします。このチェックは、無効な記述子を GetFieldID()
などのクラスに引き渡し、こうしたクラスを間接的に読み込む JNI 呼び出しをキャッチします。たとえば、java/lang/String
署名付きのフィールドは、この署名が無効であるため見つかりません。署名は Ljava/lang/String;
である必要があります。
これは、java/lang/String
が有効な完全修飾名である場所にある FindClass()
への JNI 呼び出しとは異なります。
Android 8.0 は、同じ DexFile オブジェクトを使用してクラスを定義しようとする複数のクラスローダをサポートしません。これを実行しようとすると、Android ランタイムが 「Attempt to register dex file <filename>
with multiple class loaders」というメッセージとともに InternalError
エラーをスローします。
DexFile API は廃止されたため、代わりに PathClassLoader
や BaseDexClassLoader
などの、プラットフォーム クラスローダのいずれか 1 つを使用することをお勧めします。
注: ファイル システムから同じ APK ファイル コンテナまたは JAR ファイル コンテナを参照する複数のクラスローダを作成できます。これを実行しても、通常はメモリのオーバーヘッドはそれほど発生しません。コンテナ内の DEX ファイルが圧縮されずに保存されている場合、プラットフォームは DEX ファイルを直接抽出しなくても、このファイルで mmap
操作を実行できます。プラットフォームがコンテナから DEX ファイルを抽出する必要がある場合に DEX ファイルを抽出して参照すると大量のメモリを消費することがあります。
Android では、すべてのクラスローダが並行処理可能と見なされます。複数のスレッドがクラスローダが同じである同じクラスを読み込む場合、最初のスレッドが最初に操作を完了し、その結果が他のスレッドに使用されます。この動作は、クラスローダが同じクラスを戻すか、別のクラスを戻すか、例外をスローするかどうかに関係なく発生します。プラットフォームはこのような例外を暗黙的に無視します。
警告: Android 8.0 以前のプラットフォームのバージョンでは、これらの仮定に従っていない場合、同じクラスの複数回定義、クラスの混乱や他の望ましくない状況によるヒープ破壊につながる可能性があります。