Images in this article are AI-generated for illustrative purposes.
Android の Behavior changes を実務目線で整理した記事です。
本記事のターゲット
本記事は、以下の方をターゲットに執筆しました。
- AndroidのOS仕様把握の進め方について知りたい方
- Behavior changesってなに?って思った方
- これからAndroid 16のアップデート対応を始める方
はじめに
私はAndroidエンジニアとして、実装だけでなく設計にも関わるようになる中で、プロダクトの品質にどう向き合うべきかを考える機会が増えてきました。
その中で特に重要だと感じたのが、OSアップデートによる影響と変更点の確認です。
Androidは毎年メジャーアップデートが行われ、そのたびに仕様変更が追加されます。これらは単なる新機能ではなく、既存アプリの挙動に直接影響する変更であり、見落とすと不具合やUXの劣化といった問題につながります。
本記事では、その入口となる「Behavior changes」について、Android 16をベースに実務での優先度を意識しながら整理していきます。
Behavior changesとは?
「Behavior changes」とは、Android OSのメジャーアップデートに伴い、既存アプリの挙動に影響を与える仕様変更のことを指します。
主な特徴は次の通りです。
- 単なる新機能追加ではなく、既存の機能やコードの動作が変わる可能性がある
- OSが変更した動作に従わない場合、アプリがクラッシュしたり、UIが崩れたり、バックグラウンド処理が正常に動作しなくなることがある
- 特にAndroidでは、毎年のメジャーアップデートごとに複数のBehavior changesが発生するため、アップデート対応時には必ず確認が必要
Behavior changesを正しく理解することは、アップデート対応の優先度を決める上での最初のステップになります。
Behavior changesの確認はアップデート対応の入口
実務上では、Behavior changesの確認と影響整理は「アップデート対応の入口(Update Readiness Check)」にあたり、次のステップであるAPI変更の確認や実機・エミュレーターでの動作検証へつなげるための土台となります。
/** アップデート対応に必要な確認 **/
1. Behavior changesの確認 // 影響範囲の整理: OSアップデートによる挙動変更点を洗い出し、優先度を決める
2. API changesの確認 // API対応チェック: 新APIや非推奨APIを確認し、アプリの修正方針を検討
3. 実機・エミュレーターでの動作検証 // 挙動確認: 実機・エミュレーターで動作を確認し、問題がないか検証
次に、Android 16をベースにステップ1であるBehavior changesの確認を進めていきます。
なお、現時点ではAPI changesの確認および実機・エミュレーターでの動作検証までは実施していないため、本記事ではBehavior changesの確認と影響整理にフォーカスしています。
Android 16のBehavior changes
Android 16では、既存アプリに影響を与える複数のBehavior changesが追加されています。
Behavior changesは大きく分けて「全アプリ対象」のものと「Target SDK依存」のものの2つがあります。
Behavior changes
├── ① 全アプリ対象 // (OSアップデート時にすべてのアプリで)自動的に適用される変更
└── ② Target SDK 依存 // (targetSdkVersionを上げたときに)targetSdkVersion引き上げ時に適用される変更
まずは、「① 全アプリ対象」の変更を優先的に確認します。これらはOSアップデートだけで適用されるため、対応していない場合でもユーザー環境では自動的に挙動が変わり、意図しない不具合や仕様変更が発生する可能性があります。
② Target SDK依存の変更への対応方針
「② Target SDK依存」の変更については、自アプリでtargetSdkVersionを引き上げるタイミングや機能改修に伴って影響を受けるため、その時点での対応でも問題ありません。ただし、影響範囲が大きい変更も含まれるため、事前に内容を把握した上で計画的に対応していくことが重要です。
① 全アプリ対象(behavior-changes-all)
このページに記載されている項目は、targetSdkVersionに限らずAndroid 16 端末上で動作するすべてのアプリに適用されます。
## Core functionality
- JobScheduler quota optimizations
- Abandoned empty jobs stop reason
- Ordered broadcast priority scope no longer global
- ART internal changes
- 16 KB page size compatibility mode
## User experience and system UI
- Deprecating disruptive accessibility announcements
- Support for 3-button navigation
- Automatic themed app icons
## Device form factors
- Virtual device owner overrides
## Security
- Improved security against Intent redirection attacks
- Companion apps no longer notified of discovery timeouts
## Connectivity
- Improved bond loss handling
全アプリ対象(behavior-changes-all)の変更点は、targetSdkVersionに関係なく適用され、ユーザーのOSアップデートによって挙動が変わる可能性があります。
一部は互換モードで制御できる場合もありますが、基本的にはアプリ側の対応有無にかかわらず影響を受けるため、可能であれば正式リリース前から把握しておくことが重要です。
② Target SDK 依存(behavior-changes-16)
このページに記載されている項目は、targetSdkVersionを36に上げたときに適用されます。
## User experience and system UI
- Edge to edge opt-out going away
- Migration or opt-out required for predictive back
- Elegant font APIs deprecated and disabled
## Core functionality
- Fixed rate work scheduling optimization
## Device form factors
- Adaptive layouts
## Health and fitness
- Health and fitness permissions
## Connectivity
- New intents to handle bond loss and encryption changes
- New way to remove bluetooth bond
## Security
- MediaStore version lockdown
- Safer Intents
- GPU syscall filtering
## Privacy
- Local Network Permission
- App-owned photos
Target SDK 依存(behavior-changes-16)の変更点は、targetSdkVersionを引き上げた際に適用されるため、アップデート時には必ず影響有無を確認し、必要に応じて対応を行う必要があります。
Behavior changesの影響整理と対応優先度
Behavior changesは項目数も多く、いきなり全体を確認しようとするとハードルが高くなりがちです。
そのため本記事では、Android 16のBehavior changesをもとに、まず自アプリにおける「対応が必要か(対応の要否)」、「優先して対応すべきか(対応の優先度)」を判断できるよう整理します。
まず、対応の要否を判断するために、各項目を以下の観点で整理します。
1. どんな変更かを理解する(概要)
2. 具体的に何が変わるのか(変更点)
3. なぜ変わったのか(背景)
4. 自アプリに影響があるか(対象/例外, 影響予測)
5. どのように対応するか(対応方法)
そのうえで、複数あるBehavior changesの中から対応の優先順位を判断するため、以下の3段階で分類します。
高:
- 多くのユーザーに影響する
- または、アプリの根幹機能に関わる
- 気づかずリリースすると重大な不具合につながる
中:
- 一部ユーザーや特定条件下で影響が出る
- 回避策や代替手段が存在する
低:
- 影響範囲が限定的
- または既存実装で問題になりにくい
次の章「Behavior changesの確認」では、Behavior changesを元にアプリ対応の要否と優先度をまとめています。
まずは、自アプリに影響がありそうなもので、①全アプリ対象の優先度「高」の項目から確認していきましょう。
Behavior changesの確認
それでは、Android 16をベースにBehavior changesを見ていきます。
概要以外の観点については詳細にまとめているため、必要に応じて折りたたみセクションをご確認ください
Android 16 behavior changes for all apps
まずは、Android 16へのOSアップデート時にすべてのアプリで影響を受ける変更点から見ていきます。
【優先度: 高】 Support for 3-button navigation
- 概要
- 3ボタンナビゲーション(戻るボタン)でも Predictive Back が有効になり、戻る操作時に前画面のプレビューが表示されるようになった
詳細
- 変更点(何が変わるのか)
- 戻るボタン押下時に「即時遷移」ではなく、スワイプジェスチャーと同様の遷移プレビュー付きアニメーションになる
- 変更の背景(なぜ変更したのか)
- ジェスチャーナビゲーションとボタンナビゲーションで戻る体験が分断されていたため
- Android全体で「戻る操作の一貫したアニメーション体験」を提供するため
- 対象
- Android 16 端末上で動作するすべてのアプリ(targetSdkVersion不問)
- 例外(非対象)
- Android 15以下の端末で動作する場合
- 影響予測
- 同記事内の『【優先度: 高】 Migration or opt-out required for predictive back』を参照
- 対応方法
- 同記事内の『【優先度: 高】 Migration or opt-out required for predictive back』を参照
【優先度: 高】 Abandoned empty jobs stop reason
- 概要
- バッテリー保護、リソース最適化、バグ排除のため、バックグラウンド処理の管理が厳密になり、不適切な処理は自動的に制限されるようになった。
詳細
-
変更点(何が変わるのか)
- 実体のないジョブ(abandoned empty job)は
STOP_REASON_TIMEOUT_ABANDONEDで停止される
- 実体のないジョブ(abandoned empty job)は
-
変更の背景(なぜ、変更したのか)
-
完了処理漏れのジョブがシステム資源を占有するのを防ぎ、バックグラウンド実行の公平性を保つため
[① Job開始] ↓ [② 処理中] ↓ [③ JobParameters が消える(GC)] ↓ [④ jobFinished() が呼ばれない] ↓ [⑤ OSの認識] 「まだ動いてるはず…でも中身がない」 ↓ [⑥ Abandoned Job と判定(強制終了)] (STOP_REASON_TIMEOUT_ABANDONED)
-
-
対象
- Android 16 端末上で動作するすべてのアプリ(targetSdkVersion不問)
-
例外(非対象)
- Android 15以下の端末で動作する場合
-
影響予測
- JobSchedulerの直接使用、WorkManagerのカスタムを行っている箇所に警戒が必要
- WorkManager、DownloadManagerは内部でJobSchedulerを利用するため、実行タイミングの影響を受ける可能性はある
-
対応方法
- 基本的によほどの理由がない限り、WorkManagerを使用する
- 使用する場合は、実行するジョブの後で
jobFinished()を呼び出して必ず終了する - タイムアウトにより永続待機を防ぐ(
withTimeout) - リトライ設計を見直す
-
backoff(リトライの間隔制御)を設定する(LINEAR or EXPONENTIAL)
失敗 → 10秒待つ → retry → 20秒待つ → retry → 40秒待つ → retry -
リトライ回数を決める
-
完全失敗を避けるため、fallbackを用意する
if (runAttemptCount >= 3) { saveToLocalCache() return Result.success() }
-
【優先度: 高】 JobScheduler quota optimizations
- 概要
- 従来からあるバックグラウンド制御がさらに強化され、より効率的な実行が求められるようになった。
詳細
-
変更点(何が変わるのか)
- ジョブ実行中、常に監視されるようになる(runtime quota)
- quotaは、バックグラウンド処理に使える「時間と回数」の予算
- 状態に応じて、厳密にquotaが調節される
- quotaが調節される3つの状態
- アプリケーションが属するアプリ スタンバイ バケット
- アプリが最上位の状態のときにジョブの実行が開始される
- フォアグラウンド サービスの実行中にジョブが実行されている
- quotaの評価が下がると実行制限がかかるので、より効率的なバックグラウンド処理が必要になる
- quotaが調節される3つの状態
- ジョブ実行中、常に監視されるようになる(runtime quota)
-
変更の背景(なぜ、変更したのか)
-
バッテリー寿命とシステム全体の応答性を守るため、長時間実行や放置ジョブを抑制する必要があるため
Abandoned Jobのような無駄なJobはquotaの評価を下げ、以下のように実行制限をかけられる可能性がある
Job実行したい ↓ quota不足 ↓ すぐ実行されない ↓ 遅延 or 無視- わかりやすく例えると。。。予算を無駄に使いすぎて、お小遣いを減らされるイメージ↓
あなたのアプリ = バッテリー予算 Job実行 = お金を使う 無駄遣いすると… ↓ 信用下がる ↓ 使えるお金減る(quota減少)
- わかりやすく例えると。。。予算を無駄に使いすぎて、お小遣いを減らされるイメージ↓
-
-
対象
- Android 16 端末上で動作するすべてのアプリ(targetSdkVersion不問)
-
例外(非対象)
- Android 15以下の端末で動作する場合
-
影響予測
- これからはバックグラウンド処理全般、特にquota枯渇/standby bucket低下/Abandoned
Jobが起きないように気をつける必要がある
- これからはバックグラウンド処理全般、特にquota枯渇/standby bucket低下/Abandoned
-
対応方法
- まず、getStopReasonを使用してジョブ停止の原因を探る
- 原因を特定し、以下のケースに応じて対応
- quota枯渇の場合
- Jobをまとめる
- 実行頻度減らす
- standby bucket低下
- Jobを放置せず、ユーザー操作にひも付ける
- Foreground化
- Abandoned Job
- ジョブを実行し終わったらjobFinishedを必ず呼ぶ(try-catchを使用している場合は、finallyに記述)
- paramsの状態保持(Jobのキャンセル・終了に必要)
- quota枯渇の場合
【優先度: 高】 Improved security against Intent redirection attacks
- 概要
- Intentの不正利用(Intentリダイレクト攻撃)をデフォルトで防御
**攻撃のイメージ** 1. 攻撃アプリがIntentの中身を細工 2. 被害アプリがそのIntentをそのまま実行 → 内部コンポーネント起動 / データ漏洩 / 任意コード実行
- Intentの不正利用(Intentリダイレクト攻撃)をデフォルトで防御
詳細
-
変更点(何が変わるのか)
- Intent起動時の安全性チェックが強化され、危険なリダイレクトパターンはデフォルトで拒否される
- 変更の背景(なぜ、変更したのか)
-
意図しない内部コンポーネント起動や権限悪用を減らし、既定で安全な挙動に寄せるため
Intentの不正操作
Intentはどこからでも自由に書き換えることができ、アプリを不正操作することが可能 Intent経由で攻撃できる場所はたくさんある - 他アプリ(=攻撃者になり得る) - OS - ブラウザ(Deep Link) - 通知タップ 外部が「次に何を起動するか」を完全にコントロールすることが可能なため、とても危険 - 内部Activityを強制起動 - 想定外の画面遷移 - 権限を持ったコンポーネントを悪用 攻撃のイメージ(ネストIntentを対象とした例) [ 外部アプリ(攻撃に使われるインストールされた不正なアプリ) ] ↓ 送信 ↓ ┌────────────────────┐ │ Outer Intent │ ← あなたのアプリが受け取る(起動) │ │ │ Extra: │ │ "next_intent" → ─────────────┐ └────────────────────┘ │ ↓ ┌──────────────────┐ │ Inner Intent │ ← 攻撃者が自由に作れる(次にやることの指示) │ component: ??? │ │ data: ??? │ └──────────────────┘ // 内側のIntent(悪意あるやつ) val maliciousIntent = Intent().apply { setClassName("com.your.app", "com.your.app.AdminActivity") } // 外側のIntent val outerIntent = Intent().apply { putExtra("next_intent", maliciousIntent) } // あなたのアプリに送る startActivity(outerIntent)
-
-
対象
- Android 16 端末上で動作するすべてのアプリ(targetSdkVersion不問)
-
例外(非対象)
-
Android 15以下の端末で動作する場合
-
意図的にIntentリダイレクト攻撃の保護を無効化している
val iSublevel: Intent? = i.getParcelableExtra("sub_intent") iSublevel?.removeLaunchSecurityProtection() // Opt out from hardening
-
-
影響予測
- 通常のアプリはほぼ影響なし(Googleによる事前調査では大きな影響は少ないとされている)
- ただ、「ロジックレベルの脆弱性」や「サニタイズ不足」は防げないため依然として対応が必要
-
ロジックレベルの脆弱性の例
// 他人のデータ参照 👈️ OSは止められない(アプリの責任) val userId = intent.getStringExtra("user_id") loadUser(userId) -
サニタイズ不足の例
// URLを開く 👈️ 悪意あるURLでもそのまま開く val url = intent.data webView.loadUrl(url.toString())
-
-
対応方法
-
ネストIntentを使用しない
// ❌️ Intentの中にIntentを入れる(ネストIntent) intent.putExtra("next_intent", anotherIntent) -
サニタイズをしっかり行う(getParcelableExtraの値は必ず検証)
// 「Intent = ユーザー入力と同じ(むしろそれ以上に危険)」であり、外部入力として扱う // nullチェック val userId = intent.getStringExtra("user_id") ?: return // フォーマットチェック if (!userId.matches(Regex("[0-9]+"))) return // URIの検証 val uri = intent.data if (uri?.scheme != "https") return
-
【優先度: 中】 Ordered broadcast priority scope no longer global
- 概要
- ブロードキャストのグローバル(他のアプリと連携)な順序保証がなくなる
詳細
-
変更点
- Broadcast(他のアプリまたはOSからのイベント通知)を受け取る順番(
android:priority)は、同一アプリ内でのみ有効となる - 他アプリとの間で「priorityが高いから先に受ける」は保証されない
- Broadcast(他のアプリまたはOSからのイベント通知)を受け取る順番(
-
変更の背景(なぜ、変更したのか)
- アプリ間の暗黙的な順序依存を減らし、受信順の安全性・一貫性を高めるため
-
対象
- Android 16 端末上で動作するすべてのアプリ(targetSdkVersion不問)
-
例外(非対象)
- Android 15以下の端末で動作する場合
-
影響予測
-
android:priorityを使用して外部アプリと協調して動くようなアプリは順序が保証されなくなるため、動かなくなる可能性がある
-
以下のようなアプリで順序付きのBroadcastを使用している場合は要注意
Aアプリ:データ加工 Bアプリ:そのデータを使う
-
-
対応方法
- 順序(
android:priority)ではなく状態に依存する設計にする- Broadcastはイベント発生の最初の通知のみにし、実処理は別でやる
- または、Broadcastによる不特定多数への通知を行わずに明示的Intentでピンポイントの命令を送る形式に変える
- 必要な順序制御はアプリ内キュー(WorkManager等)で担保する
- 順序(
【優先度: 中】 ART internal changes
- 概要
- ARTが従来の「OSのアップデートに合わせて更新」ではなく、頻繁にアップデートされるようになる
詳細
- 変更点(何が変わるのか)
- ARTモジュール更新の影響を受けやすくなり、非SDK API依存コードの破綻が顕在化しやすくなる
- 変更の背景(なぜ、変更したのか)
- パフォーマンス改善やセキュリティ修正をOSメジャー更新に依存せず迅速に配信するため
- 対象
- Android 16 端末上で動作するすべてのアプリ(targetSdkVersion不問)
- 例外(非対象)
- Android 15以下の端末で動作する場合
- 影響予測
- 以下のように内部APIを触るリフレクションを使用している可能性がある非SDKのAPIは次の日に動かなくなっていてもおかしくない
- Class.forNameで非公開になっているAndroidの内部クラスにアクセス
- getDeclaredMethodで本来アクセス不可の非公開メソッドにアクセス
- setAccessible(true)でアクセス制御を無理やり外す
- 以下のように内部APIを触るリフレクションを使用している可能性がある非SDKのAPIは次の日に動かなくなっていてもおかしくない
- 対応方法
-
放置されているようなライブラリは使用せず、最新化されているものを使用する
- 最新化されているライブラリはライブラリ開発者が制限に準拠すること(無視した場合はクラッシュ)を求められるので必然的に安全となる
-
StrictModeにより危険なコードを実行時に検出
-
Logcatで以下のようなログがでないか監視
Accessing hidden method Reflection access to Illegal access -
Crashlyticsでリフレクション絡みの例外が発生していないか確認
NoSuchMethodException IllegalAccessException
-
【優先度: 中】 Companion apps no longer notified of discovery timeouts
- 概要
- Companion Deviceのタイムアウト通知がユーザー拒否に吸収された
- Companion Deviceは、スマホとペアで使う外部デバイス
- スマートウォッチ
- Bluetoothイヤホン
- スマートリング
- IoT機器(スマートロックなど)など
- Companion Deviceは、スマホとペアで使う外部デバイス
- Companion Deviceのタイムアウト通知がユーザー拒否に吸収された
詳細
-
変更点(何が変わるのか)
-
RESULT_DISCOVERY_TIMEOUTが返らなくなり、タイムアウト時もRESULT_USER_REJECTEDとして通知される// Android15まで 成功 → RESULT_OK ユーザー拒否 → RESULT_USER_REJECTED タイムアウト → RESULT_DISCOVERY_TIMEOUT // Android 16 成功 → RESULT_OK ユーザー拒否, タイムアウト → RESULT_USER_REJECTED 👈️ 「ユーザーに拒否された」という通知に1本化
-
-
変更の背景(なぜ、変更したのか)
- デバイス探索結果からユーザーの位置や行動を推測されにくくするため
- タイムアウトは「位置情報」を含むため、ユーザーの位置を推測できてしまう
デバイスが近くにある → 見つかる デバイスが遠い → タイムアウト
- タイムアウトは「位置情報」を含むため、ユーザーの位置を推測できてしまう
- デバイス探索結果からユーザーの位置や行動を推測されにくくするため
-
対象
- Android 16 端末上で動作するすべてのアプリ(targetSdkVersion不問)
-
例外(非対象)
- Android 15以下の端末で動作する場合
-
影響予測
-
タイムアウト専用にダイアログまたは処理を用意しているアプリ
-
専用ダイアログを用意していた場合、OS側でもダイアログが表示されるため設計を見直す必要がある
// Android15まで デバイス見つからない(タイムアウト) ↓ アプリに RESULT_DISCOVERY_TIMEOUT が返る// Android 16 デバイス見つからない ↓ ユーザーにダイアログ表示(OSが管理) ↓ ユーザーが閉じる ↓ アプリには RESULT_USER_REJECTED が返る
-
-
ユーザーに選択させる設計に変化
タイムアウト → リトライ 拒否 → 終了 // ではなく、ユーザーに選択させる 失敗 → ユーザーに選ばせる(リトライ or 終了)
-
-
対応方法
-
タイムアウト専用に用意していた処理を「ユーザー拒否」としてまとめて失敗扱いを行うようにする
if (resultCode == RESULT_USER_REJECTED) { // まとめて失敗扱い } -
接続に失敗した場合、ユーザーがリトライまたは終了を選択できるようにする
-
【優先度: 中】 Improved bond loss handling
- 概要
- Bluetoothのbond(ペアリング状態)が失われた場合、アプリやOSが自動で再ペアリングすることはできなくなり、ユーザーの明示的な操作が必要になった(再接続はOK)
詳細
- 変更点(何が変わるのか)
- bond loss後の自動再ペアリングが禁止され、再ペアリングはユーザー操作経由のみとなる
- 変更の背景(なぜ、変更したのか)
- なりすましデバイスとの再接続リスクを下げ、接続意思をユーザーに明示させるため
- 対象
- Android 16 端末上で動作するすべてのアプリ(targetSdkVersion不問)
- 例外(非対象)
- Android 15以下の端末で動作する場合
- 影響予測
- 再ペアリングを自動的に行い、明示的な再ペアリング手段を用意していなかった場合、bond lossが発生した際に接続不可となる
- 対応方法
-
bond loss時、明示的に再ペアリングを求めるUIを表示
fun onBondLost(device: BluetoothDevice) { showDialog( title = "接続が解除されました", message = "再度ペアリングが必要です", onConfirm = { startPairing(device) } ) }
-
【優先度: 中】 16 KB page size compatibility mode
- 概要
- Android15から対応が始まった16KBページサイズへの移行のための互換モードサポート
- ページサイズはメモリを区切る最小単位
- Android15から対応が始まった16KBページサイズへの移行のための互換モードサポート
詳細
- 変更点(何が変わるのか)
- 4KBページ前提の
.soを含む場合、互換モード経由で動作し、初回起動時警告や性能影響の可能性が出る-
.soのロードの際に、4kbページサイズのものが含まれていた場合、アプリ初回起動時に警告がでる -
.soはネイティブライブラリであり、SDK(Kotlin/JavaでAndroid開発するためのツール)または、NDK(C/C++でAndroid開発するためのツール)を使用していた場合、apk内に含まれる可能性がある
-
- 4KBページ前提の
- 変更の背景(なぜ、変更したのか)
- 新しいページサイズ環境への移行期間に互換性を維持しつつ、16KB対応を促進するため
- 対象
- Android 16 端末上で動作するすべてのアプリ(targetSdkVersion不問)
- apk内に
.soが含まれるアプリ
- 例外(非対象)
- Android 15以下の端末で動作する場合
- 影響予測
- 高速処理、ハードウェア制御を行っているような以下のアプリ
- ゲーム
- 動画・画像処理系アプリ
- 音声・配信・通話系アプリ
- Firebase Crashlytics、AdMobなどネイティブライブラリを含むSDK
- 高速処理、ハードウェア制御を行っているような以下のアプリ
- 対応方法
- 自作SDKである場合は再ビルド、外部SDKである場合は更新またはお問い合わせを行う。
- 本番アプリ、ユーザーが多い、SDKへの依存が大きいものは早めに対応
- 互換モードがあるから急ぎではないが、調査については今すぐやるべき
- 自作SDKである場合は再ビルド、外部SDKである場合は更新またはお問い合わせを行う。
【優先度: 中】 Deprecating disruptive accessibility announcements
- 概要
- アクセシビリティユーザーの操作を妨げる過剰なアナウンスを抑制するため、割り込み的な通知の扱いが見直された
詳細
- 変更点(何が変わるのか)
-
TYPE_ANNOUNCEMENTなど割り込みの強い通知への依存は非推奨の方向になり、今後はより文脈に沿った通知方法が推奨される
-
- 変更の背景(なぜ、変更したのか)
- 読み上げの過負荷や文脈分断を防ぎ、支援技術ユーザーの操作体験を維持するため
- 対象
- Android 16 端末上で動作するすべてのアプリ(targetSdkVersion不問)
- 例外(非対象)
- Android 15以下の端末で動作する場合
- 影響予測
- 画面遷移や更新のたびにアナウンスを多発しているアプリは、読み上げ体験の低下や将来の互換性リスクがある
- 対応方法
- 重要イベントのみに絞って通知する
-
accessibilityLiveRegion、contentDescription、フォーカス制御など標準のアクセシビリティ設計で情報を伝える - 通知の必要性をTalkBack有効時に実機で検証する
【優先度: 低】 Virtual device owner overrides
- 概要
- 仮想デバイスオーナー(企業管理端末など)が、アプリの一部ウィンドウポリシーを上書きできるようになった
詳細
- 変更点(何が変わるのか)
- アプリ側が指定した表示制約(向き、リサイズ挙動など)が、管理ポリシーによって上書きされる場合がある
- 変更の背景(なぜ、変更したのか)
- 企業管理環境で、業務要件に応じた表示制御を端末管理側から一貫して適用する必要があるため
- 対象
- Android 16 端末上で動作するアプリのうち、仮想デバイスオーナー配下で実行されるもの
- 例外(非対象)
- 仮想デバイスオーナー配下でない通常端末で動作する場合
- 影響予測
- 企業管理デバイスでのみ再現するレイアウト崩れや表示差分が発生する可能性がある
- 対応方法
- 端末管理ポリシー下での表示確認をQA項目に追加
- 画面サイズ・向きの変化に追従するレイアウト(レスポンシブ)を基本設計
【優先度: 低】 Automatic themed app icons
- 概要
- システムによる強制的なテーマアイコン適用
詳細
- 変更点(何が変わるのか)
- モノクロに対応していない場合でも、自動的にテーマアイコンが適用される
- また、Material Designの思想により、テーマをONにしている場合はアイコン表示でブランドカラーを適用することはできない
- ユーザーが選択したテーマを優先する設計
- アイコン色はユーザー設定に追従する
- 変更の背景(なぜ、変更したのか)
- ホーム画面全体の見た目の一貫性を高め、ユーザー選択のテーマを優先するため
- 対象
- Android 16以降の端末上で動作するすべてのアプリ(targetSdkVersion不問)
- 例外(非対象)
- Android 15以下の端末で動作する場合
- 影響予測
- アイコン表示時の見た目が壊れる可能性がある
- 強制表示のためブランドカラーが損なわれる
- 対応方法
- monochromeアイコンに移行する(モノクロのSVG用意→XMLとして読み込む)
- テーマONによりロゴが潰れる、意図しない形になるなどがある場合はコントロール不能なブランド毀損となる
- 作業コストも低いため、リスク回避として対応を推奨する
- monochromeアイコンに移行する(モノクロのSVG用意→XMLとして読み込む)
Apps targeting Android 16
続いて、targetSdkVersionを上げたときのみ起こる「Target SDK依存」の変更点を見ていきます。
【優先度: 高】 Edge to edge opt-out going away
- 概要
-
targetSdkVersion=36では、R.attr#windowOptOutEdgeToEdgeEnforcementは無効化され、edge-to-edge
をopt-outできない
-
詳細
- 変更点(何が変わるのか)
- Android 15(target 35)で使えていたopt-out属性が、Android 16(target 36)では適用されない
- 変更の背景(なぜ、変更したのか)
- edge-to-edgeを標準挙動として統一し、端末間で一貫した表示体験に寄せるため
- 対象
- APIレベル36(Android 16)をターゲットにしたアプリ
- 例外(非対象)
- APIレベル35以下をターゲットにしている場合
- 影響予測
- ステータスバー/ナビゲーションバー領域の余白やInsets処理が不十分な画面で、UIの重なりが顕在化する
- 対応方法
-
WindowInsets対応を全画面で点検し、表示崩れのある画面を先に解消する
-
【優先度: 高】 Migration or opt-out required for predictive back
- 概要
- targetSdkVersionを36に上げる場合、Predictive Backへの移行または暫定opt-outが必要
詳細
- 変更点(何が変わるのか)
-
OnBackPressed依存の実装は、新しい戻る遷移(Predictive Back)への移行対応が必要になる
-
- 変更の背景(なぜ、変更したのか)
- 「戻る」の操作がキー(
OnBackPressed)ではなく、ジェスチャー(Predictive Back)の扱いになるため- 旧Androidは物理ボタンを搭載しており、他のデバイス(キーボード、リモコン)などと入力システムを統一するために「戻る」もキーの一つとして扱っていたため
- 「戻る」の操作がキー(
- 対象
- APIレベル36(Android 16)をターゲットにしたアプリ
- 例外(非対象)
- APIレベル35以下をターゲットにしている場合
- 影響予測
- 独自バックスタック処理や
onBackPressed()中心の実装で戻る挙動不整合が起きやすい
- 独自バックスタック処理や
- 対応方法
-
OnBackPressedDispatcher/OnBackInvokedCallbackへの移行を行う - 一時的にopt-outする場合は、撤廃時期を見据えて計画的に移行する
-
【優先度: 高】 Adaptive layouts
- 概要
- 大画面向けに、向き固定・リサイズ不可・アスペクト比制約などの互換設定が効きにくくなる
詳細
- 変更点(何が変わるのか)
- 次の指定はシステムにより無視される可能性がある
screenOrientationresizeableActivityminAspectRatiomaxAspectRatiosetRequestedOrientation()
- 次の指定はシステムにより無視される可能性がある
- 変更の背景(なぜ、変更したのか)
- 大画面での表示領域を有効活用し、固定レイアウト依存によるUX低下を防ぐため
- 対象
- APIレベル36(Android 16)をターゲットにしたアプリ
- 主に
sw >= 600dp環境で動作する場合
- 例外(非対象)
-
sw < 600dp環境で動作する場合 -
android:appCategoryに基づくゲームなど、一部互換対象外カテゴリの場合
-
- 影響予測
- 縦固定前提・固定比率前提の画面でレイアウト崩れや状態欠損が発生しやすい
- 対応方法
-
WindowSizeClassベースでレイアウトを切り替える - 状態保存(
ViewModel/SavedStateHandle/rememberSaveable)を前提にする - 必要時のみ暫定opt-outを使い、最終的にはレスポンシブ対応に移行する
-
【優先度: 高】 Safer Intents
- 概要
- Intent受け渡しを安全に行うための制限強化
詳細
-
変更点
- Intent受け渡しに関するLintからの非推奨・警告および例外の強化
-
Lintからの非推奨・警告
- 型未指定のgetParcelableExtra
- exported未明示
- 暗黙Intent
-
例外
種類 原因 ClassCastException 型違い NullPointerException null未処理 TransactionTooLargeException データ大きすぎ SecurityException 権限不正
-
- Intent受け渡しに関するLintからの非推奨・警告および例外の強化
-
変更の背景(なぜ、変更したのか)
- Intentの受け渡しに起因する脆弱性や実行時クラッシュを減らすため
-
対象
- APIレベル36(Android 16)をターゲットにしたアプリ
-
例外(非対象)
- APIレベル35以下をターゲットにしている場合
-
影響予測
- 既存のIntent受け渡し実装で、型安全性や入力検証が弱い箇所がクラッシュ/警告として顕在化しやすい
-
対応方法
- Intent送信時にパッケージを明示する
- Intent受取時に値を検証する
- 受け取るIntentの型明示
- nullチェック
- サニタイズ(受け取る値の検証)
- 外部Intentを受け取らない(android:exported="false")
【優先度: 中】 Fixed rate work scheduling optimization
- 概要
- 定期実行(fixed rate)のタスクがまとめて実行されなくなる
詳細
-
変更点
- 復帰後に最大1回しか実行されないようになる
-
変更の背景(なぜ、変更したのか)
- 時間ベースのまとめて実行はCPUスパイク、ネットワーク負荷増加、バッテリー消費量増加などの問題があるため
-
対象
- APIレベル36(Android 16)をターゲットにしたアプリ
-
例外(非対象)
- APIレベル35以下をターゲットにしている場合
-
影響予測
- バックグラウンドでポーリング(API定期取得)、カウントタイマー、バッチ処理・キュー、ログ送信などを行っている処理に影響を受ける
- フォアグラウンド限定で処理をしている場合には、影響を受けない
-
対応方法
- 状態ベースでの処理に切り替える(最後の実行時刻をどこかに保存し、差分のみ実行)
- WorkManager(OS管理)に移行
【優先度: 中】 Local Network Permission
- 概要
- ローカルネットワークアクセス保護に向けた変更で、LANアクセスを権限で保護する方向に移行している
詳細
- 変更点(何が変わるのか)
- Android 16 では互換フラグによるオプトイン検証が可能になり、ローカルネットワーク依存処理の洗い出しが必要になる
- 将来的なリリースでは、ローカルネットワークアクセスに専用ランタイム権限が必須になる予定
- 変更の背景(なぜ、変更したのか)
- ローカルネットワークのスキャンは、個人の行動を特定できてしまうため
- ローカルネットワークのスキャンでわかること
- どんな機器があるか(プリンター、NAS、スマート家電、TV、ゲーム機)
- 家の構成(複数のスマホ、子供向けのデバイス → 家族の人数・子どもの有無)
- 利用サービス(Chromecast → Googleエコシステム)
- 在宅状況(デバイスのオンライン/オフライン状態 → 今家にいるか、いつ帰宅するか)
- 企業・開発環境(仕事環境・使用技術)
- 個人の特定(ネットワーク構成はほぼユニークなため、Cookieがなくても追跡可能)
- ローカルネットワークのスキャンでわかること
- ローカルネットワークのスキャンは、個人の行動を特定できてしまうため
- 対象
- APIレベル36(Android 16)をターゲットにしたアプリ(主にオプトイン検証の対象)
- 例外(非対象)
- APIレベル35以下をターゲットにしている場合
- 影響予測
- ローカルネットワークを使用しているアプリ
- 同一Wi-Fiのデバイス探索
- mDNS / SSDPの使用
- 192.168.x.x に直接アクセス
- IoT / Chromecast / NAS連携
- 事前準備が不足すると、将来の権限必須化時に機能停止や審査リスクにつながる
- ローカルネットワークを使用しているアプリ
- 対応方法
- ローカルネットワークが不要な場合は削除する
- 使っていない機能
- デバッグコード
- ライブラリ側で暗黙利用している処理
- 互換フラグを有効にして、失敗する通信処理を洗い出す
- 必要時のみ権限を要求する導線(理由説明付き)を準備する
- ローカルネットワークが不要な場合は削除する
【優先度: 中】 MediaStore version lockdown
-
概要
- MediaStoreのversionの扱いが制限(ロックダウン)された
MediaStoreとversionについての補足
MediaStoreは、画像・動画・音楽などを管理するしくみ versionは、ストレージ全体の変更を表す「バージョン番号」 - versionは、曖昧な変更検知を行うので、あまり正確なバージョン管理には向いていない - 曖昧な変更検知 - 実際の変更を正しく検知できてないことがある - 変更範囲がMediaStore全体 - 関係のない変更も拾う - 何が変わったかがわからない - 即時性なく、タイミングが保証されない - 補助的な用途のみ利用可能 - 補助的な用途 - 次回起動時に、versionが変わっていた場合に一応キャッシュをリフレッシュ - ContentObserver(正確なバージョン管理処理)のfallback(取りこぼす可能性のある、アプリ停止中や登録してない間に対応する補助としての役割)
詳細
- 変更点
- 他アプリからの変更検知の限定(グローバルな変更検知としては使えなくなる)
- ストレージ全体の変更を表さなくなった(一部の変更を検知しない)
- 仕様として、「正確な変更検知は使えない」という方向性に変わった
- 変更の背景(なぜ、変更したのか)
- 「他のアプリが画像を追加した」「なにか操作があった」などユーザー行動の推測をできないようにするため(プライバシー強化)
- 「正確なバージョン管理」という用途としての誤用が多かったため
- 対象
- APIレベル36(Android 16)をターゲットにしたアプリ
- 例外(非対象)
- APIレベル35以下をターゲットにしている場合
- 影響予測
- versionにより、バージョン比較・更新判断を行っているアプリ
- メディア管理、クラウド同期を行っているものは要注意
- versionにより、バージョン比較・更新判断を行っているアプリ
- 対応方法
- versionを使用しない(または補助的な扱いにする)
- 個々のイベントで検知(ContentObserver)し、差分で必要な分だけ更新
【優先度: 中】 App-owned photos
- 概要
- メディアアクセスを最小化する方針が強化され、アプリ所有メディアとユーザー選択メディアを分けて扱う設計が重要になる
詳細
- 変更点(何が変わるのか)
- 自アプリが所有するメディアと、それ以外の共有メディアでアクセス方針を分ける必要がある
- 共有メディアを扱う場合は、Photo Pickerなどユーザー選択ベースの取得が推奨される
- 変更の背景(なぜ、変更したのか)
- 選択した画像のみを表示(最小限のデータ取得)により、プライバシーを保護するため
- 対象
- APIレベル36(Android 16)をターゲットにしたアプリ
- 例外(非対象)
- APIレベル35以下をターゲットにしている場合
- 影響予測
- 共有メディア前提の機能(一覧取得、同期、編集)でアクセス設計の見直しが必要になる
- 権限前提の旧実装では、取得できる範囲が想定より狭くなる可能性がある
- 対応方法
- 自分の画像にはMediaStore経由で、他人の画像にはPhoto Pickerでアクセスする
- Photo Pickerは、ユーザーが選んだメディアだけを安全にアプリへ渡すOSの仕組み
- Photo PickerとMediaStore比較
観点 Photo Picker MediaStore アクセス範囲 ユーザーが選んだものだけ 条件に合うもの全部 権限 基本不要 必要 プライバシー 高い 低め 実装責任 OS アプリ UX OS提供UI 自前UI
- Photo PickerとMediaStore比較
- Photo Pickerは、ユーザーが選んだメディアだけを安全にアプリへ渡すOSの仕組み
- 自分の画像にはMediaStore経由で、他人の画像にはPhoto Pickerでアクセスする
【優先度: 低】 Elegant font APIs deprecated and disabled
- 概要
- Elegant font APIの廃止
詳細
- 変更点
- Elegant fontの高さ調節フラグの無効化(設定しても完全に無視される)
- android:elegantTextHeight
- TextView#setElegantTextHeight()
- Elegant fontの高さ調節フラグの無効化(設定しても完全に無視される)
- 変更の背景(なぜ、変更したのか)
- 特定言語向けに「美しい行間や高さ」を使用するためのフラグ
elegantTextHeight
が用意されていたが、現在はフォント自体が適切な行間を持つようになったため、OSで調節する必要がなくなった
- 特定言語向けに「美しい行間や高さ」を使用するためのフラグ
- 対象
- APIレベル36(Android 16)をターゲットにしたアプリ
- 例外(非対象)
- APIレベル35以下をターゲットにしている場合
- 影響予測
- テキストの高さ(行間)が変わる
- 多言語対応しているアプリ
- 特に
- アラビア語(RTL)
- タイ語
- ヒンディー語
- 特に
- 対応方法
- 基本的にテキストの高さがwrap_content(デフォルト)になっていれば、対応の必要なし
- 必要であれば、lineHeightを言語ごとに指定して調節する
【優先度: 低】 Health and fitness permissions
- 概要
- 従来のざっくりしたセンサー権限が、細かく分割される
詳細
- 変更点
- これまで一つの権限で全ての健康状態を取得できていたが、必要な権限だけを許可して選択するように変更された
- 変更の背景(なぜ、変更したのか)
- プライバシー強化(まとめて許可はNG)・ユーザー透明性(このアプリは心拍数だけ使います)
- 対象
- APIレベル36(Android 16)をターゲットにしたアプリ
- 例外(非対象)
- APIレベル35以下をターゲットにしている場合
- 影響予測
- Healthデータを扱うアプリ
- フィットネスアプリ
- ヘルスケアアプリ
- ウェアラブル連携
- センサー利用アプリ
- HealthデータはPlay Storeで厳しくみる項目であり、最悪審査に落ちる可能性もある
- Healthデータを扱うアプリ
- 対応方法
-
BODY_SENSORS一つでHealthデータ全取得ではなく、用途ごとに権限を分割(READ_HEART_RATE,
READ_SKIN_TEMPERATURE)して取得する
-
【優先度: 低】 New intents to handle bond loss and encryption changes
- 概要
- Bluetoothの状態変化をより細かく検知できる新しいIntentが追加された
詳細
- 変更点
- Bond(ペアリング)喪失と暗号化状態の変化がわかるIntentが追加
未接続 ↓ 接続 ↓ (必要なら)ペアリング // 追加: ペアリングキー消失(ユーザーが設定削除、デバイス側でリセット)による接続失敗を判断できる ↓ 暗号化開始 // 追加: 暗号化失敗により、GATT通信の段階で失敗したことを判断できる ↓ 通信可能
- Bond(ペアリング)喪失と暗号化状態の変化がわかるIntentが追加
- 変更の背景(なぜ、変更したのか)
- 接続までの状態の流れを確認する手段がこれまではなく、失敗したとき「どの段階で失敗したか」原因がわからなかった。
- 対象
- APIレベル36(Android 16)をターゲットにしたアプリ
- 例外(非対象)
- APIレベル35以下をターゲットにしている場合
- 影響予測
- Bluetoothを使用しているアプリ
- IoT
- ウェアラブル
- Bluetoothを使用していないアプリには影響なし
- Bluetoothを使用しているアプリ
- 対応方法
- 失敗時のログを仕込む
- BondLoss時に再ペアリングをユーザー促す
【優先度: 低】 New way to remove bluetooth bond
- 概要
- Bluetoothのペアリング(bond)を削除する正式な方法が追加された
詳細
- 変更点
- 公式APIとしてbond削除がサポートされた
- 変更の背景(なぜ、変更したのか)
- これまで非公式な方法しか存在せず、非公開API(hidden API)が使われていたため
- 対象
- APIレベル36(Android 16)をターゲットにしたアプリ
- 例外(非対象)
- APIレベル35以下をターゲットにしている場合
- 影響予測
- Bluetoothを使用しているアプリ
- IoT
- ウェアラブル
- Bluetoothを使用していないアプリには影響なし
- Bluetoothを使用しているアプリ
- 対応方法
- 公式APIによりbond削除を行う
【優先度: 低】 GPU syscall filtering
- 概要
- GPUまわりで使えるシステムコール(syscall)を制限した
- syscallは、アプリからOSカーネルに処理をお願いする仕組み
- GPUまわりで使えるシステムコール(syscall)を制限した
詳細
- 変更点(何が変わるのか)
- GPU関連で使えるsyscallをホワイトリスト化(許可された安全なものだけOK)
- 変更の背景(なぜ、変更したのか)
- GPUドライバの脆弱性をつき、syscall経由で不正にアクセスできる余地があったため
- 対象
- APIレベル36(Android 16)をターゲットにしたアプリ
- 例外(非対象)
- APIレベル35以下をターゲットにしている場合
- 影響予測
- NDK/グラフィックスまわりを利用するアプリ
- 対応方法
- 使用しているライブラリ(ゲームエンジン、OpenGLラッパー、ML SDKなど)を更新して最新にする
まとめ
- Behavior changesは、Android OSのメジャーアップデートに併せて発生する既存アプリの挙動に影響を与える仕様変更
- Behavior changesは、変更範囲の切り分けと対応優先度の整理により、確認コストや対応コストを抑えることができる
- 変更範囲の切り分け
- 「全アプリ対象(behavior-changes-all)」 と 「Target SDK依存(behavior-changes-16)」 を切り分ける
- 対応優先度の整理
- 各項目を「概要・変更点・背景・対象/例外/影響予測・対応方法」で整理し、優先度(高→中→低) を整理する
- 変更範囲の切り分け
- Behavior changesをOSアップデート前、またはtargetSdkVersionを上げる前に確認しておくことで、アップデート対応がスムーズにできる(ミスによる手戻りを抑えながら開発を進めることが可能)
さいごに
本記事では、Behavior changesを起点にしたAndroidのOS仕様の確認方法について紹介しました。
今回整理したAndroid 16のBehavior changesでは全体的な変更点として「アプリの自由度」よりも「安全性・電池効率・一貫したUX」を優先する方向に、OSの制御がより強化されたことを感じられたかと思います。
これらBehavior changesは毎年必ず発生するOSの仕様変更であり、プロダクトの継続的な品質向上の取り組みとして事前に変更点を確認し、対応コストを下げていくことが大切です。


