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

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

Android 17 では、基盤となる MessageQueue クラスを書き換えることで、Looper and 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 が原因かどうかを確認できます。

次のいずれかのオプションを使用して、変更を切り替えることができます。

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

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

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

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