MessageQueue の動作変更に関するガイダンス

Android 17 以降、Android 17(API レベル 37)以降をターゲットとするアプリは、android.os.MessageQueue の新しいロックフリー実装を受け取ります。新しい実装では、パフォーマンスが向上し、フレームの欠落が減少しますが、MessageQueue のプライベート フィールドとメソッドを反映するクライアントが破損する可能性があります。

Android 17 では、基盤となる MessageQueue クラスを書き換えることで、Looper と Handler の仕組みが大幅に刷新されています。Android オペレーティング システムの最初のリリース以降、MessageQueue はメインスレッドのタスクキューを管理するために単一のロックに依存していました。この設計では、ロックの競合が頻繁に発生していました。メインスレッドがバックグラウンド スレッドによってブロックされ、フレーム落ちや UI ジャンクが発生する可能性がありました。

影響を軽減する

アプリまたはその依存関係がランタイム リフレクションに依存して MessageQueue の内部を覗いている場合、この変更の影響を受ける可能性があります。MessageQueue を検査するためにランタイム リフレクションを使用しないでください。

以前の実装では、デベロッパーが MessageQueue.mMessages などのプライベート フィールドにアクセスして、保留中のメッセージを検査することがありました。新しいロックフリーの実装により、内部データ構造が完全に変更されました。バイナリ互換性を維持するため、Android 17 では mMessages フィールドが保持されますが、新しい実装では、キューにメッセージがあるかどうかにかかわらず、このフィールドは常に null になります。

また、よく使用されるテスト ライブラリを使用している場合は、新しい MessageQueue 実装と互換性があるようにライブラリを更新する必要があります。

Espresso

Espresso は UI テストによく使用されます。Espresso ライブラリは、UI の状態を正しくアサートするために、メインスレッドがアイドル状態になるタイミングを把握する必要があります。以前のバージョンの Espresso は、ロックフリーの MessageQueue と互換性のないリフレクション手法に依存していました。

アクション

Espresso 3.7.0 以降に更新します。このバージョンでは、TestLooperManager API(特に Android 16 で導入された新しい API)を使用して、内部実装の詳細に依存せずに Looper と安全にやり取りします。

Robolectric

同様に、Robolectric を使用して単体テストを実行する場合、テストが以前の Looper モードに依存していると、問題が発生することがあります。

アクション

Robolectric 4.17 以降に更新します。@LooperMode(LEGACY) を使用している場合は、テストを新しい @LooperMode(PAUSED) に移行する必要があります。詳しくは、Robolectric の移行ガイドをご覧ください。

動作をテストする

次のコマンドを実行すると、targetSDK を更新せずに、Android 17 の動作変更を適用してアプリをテストできます。

adb am compat enable USE_NEW_MESSAGEQUEUE <your-package-name>

このコマンドは、デバッグ可能なビルドの場合、アプリでロックフリーの MessageQueue を有効にします。

アプリが Android 17(API レベル 37)をターゲットとしている場合、新しい動作はデフォルトで有効になります。この API レベルをターゲットにした後に予期しない動作やクラッシュが発生した場合は、新しい実装を一時的に無効にして、MessageQueue が原因かどうかを確認できます。

この変更は、次の 2 つの方法のいずれかで切り替えることができます。

  1. 開発者向けオプションの [アプリの互換性の変更] メニュー。

  2. 次の ADB コマンドを実行します。

    adb am compat disable USE_NEW_MESSAGEQUEUE <your-package-name>
    

これにより、アプリは以前のロックベースの実装に戻り、問題がメッセージ キューの動作変更によるものかどうかを特定できます。