永続的な共有通知

Android API レベル 26 以降、フォアグラウンド サービスには永続的な通知が必要です。この要件は、特にバッテリーなどのシステム リソースに過剰な負荷をかける可能性のあるサービスが非表示にならないようにするためのものです。この要件により、潜在的な問題が発生する可能性があります。複数のフォアグラウンド サービスがあるアプリが、すべてのサービスで共有されるように通知を慎重に管理していない場合、閉じられない通知が複数残り、通知のアクティブ リストに望ましくない煩雑さが生じます。

この問題は、アプリから独立してフォアグラウンド サービスを実行する Navigation SDK などの SDK を使用すると困難になります。こうした SDK には独自の独立した永続的な通知が存在する可能性があり、統合が困難になります。これらの問題に対処するため、Navigation SDK v1.11 では、SDK 内を含め、アプリ全体で永続的な通知を管理できるシンプルな API が導入されました。

永続的な通知を統合する

コンポーネント

フォアグラウンド サービス マネージャーは、Android フォアグラウンド サービス クラスと永続通知クラスのラッパーを提供します。このラッパーの主な機能は、通知 ID を再利用して、マネージャーを使用してすべてのフォアグラウンド サービス間で通知を共有することです。


NavigationAPI には、ForegroundServiceManager シングルトンを初期化して取得するための静的メソッドが含まれています。このシングルトンは、Navigation SDK の存続期間中に 1 回だけ初期化できます。したがって、初期化呼び出し(initForegroundServiceManagerMessageAndIntent() または initForegroundServiceManagerProvider())のいずれかを使用する場合は、そのパスが再入される場合に備えて try/catch ブロックで囲む必要があります。互換性の問題を回避するため、いずれかのメソッドを複数回呼び出すと、Navigation SDK はランタイム例外をスローします。ただし、最初に ForegroundServiceManager への参照をすべてクリアして、後続の呼び出しを行う前に clearForegroundServiceManager() を呼び出す必要があります。Navigation SDK v2.0 では、この目的のためにチェック例外が API に追加されています。

initForegroundServiceManagerMessageAndIntent() の 4 つのパラメータは、applicationnotificationIddefaultMessageresumeIntent です。最後の 3 つのパラメータが null の場合、通知は標準の Navigation SDK 通知です。この通知の背後にあるアプリ内の他のフォアグラウンド サービスを非表示にすることは可能です。notificationId パラメータには、通知に使用する通知 ID を指定します。null の場合は任意の値が使用されます。明示的に設定することで、他の SDK からの通知など、他の通知との競合を回避できます。defaultMessage は、システムがナビ中でないときに表示される文字列です。resumeIntent は、通知がクリックされたときに発生するインテントです。resumeIntent が null の場合、通知のクリックは無視されます。

initForegroundServiceManagerProvider() の 3 つのパラメータは、applicationnotificationIdnotificationProvider です。最後の 2 つのパラメータが null の場合、通知は標準の Navigation SDK 通知です。notificationId パラメータには、通知に使用する通知 ID を指定します。null の場合は任意の値が使用されます。明示的に設定することで、他の SDK からの通知など、他の通知との競合を回避できます。notificationProvider が設定されている場合、レンダリングする通知の生成は常にプロバイダが行います。

Navigation SDK の getForegroundServiceManager() メソッドは、フォアグラウンド サービス マネージャー シングルトンを返します。まだ生成していない場合は、notificationIddefaultMessageresumeIntent に null パラメータを指定して initForegroundServiceManagerMessageAndIntent() を呼び出すのと同じ結果になります。

ForegroundServiceManager には 3 つのシンプルなメソッドがあります。最初の 2 つは、サービスをフォアグラウンドとの間で移動するためのもので、通常は作成されたサービス内から呼び出されます。これらのメソッドを使用すると、サービスを共有の永続通知に関連付けることができます。最後のメソッドである updateNotification() は、通知が変更されたため再レンダリングする必要があることをマネージャーに報告します。

共有の永続通知のコンテンツを完全に制御する必要がある場合、新しい API には通知プロバイダを定義する NotificationContentProvider インターフェースが用意されています。このインターフェースには、現在のコンテンツの通知を受け取るためのメソッドが 1 つ含まれています。また、基本クラスも用意されており、これを使用してプロバイダを定義することもできます。基本クラスの主な目的の 1 つは、ForegroundServiceManager にアクセスせずに updateNotification() を呼び出す簡単な方法を提供することです。このヘルパー メソッドは、通知プロバイダのインスタンスを使用して新しい通知メッセージを受信する場合に便利です。この場合、この内部メソッドを直接呼び出して通知にメッセージをレンダリングできます。

利用シナリオ

このセクションでは、共有の永続的な通知を使用する使用シナリオについて詳しく説明します。

他のアプリ フォアグラウンド サービスの永続的な通知を非表示にする
最も簡単なシナリオは、現在の動作を維持し、Navigation SDK 情報のレンダリングには永続的な通知のみを使用することです。他のサービスがこの通知の背後に隠れるようにするには、フォアグラウンド サービス マネージャーの startForeground() メソッドと stopForeground() メソッドを使用します。
他のアプリ フォアグラウンド サービスの永続的な通知を非表示にするが、移動していないときに表示されるデフォルトのテキストを設定する
2 番目に簡単なシナリオは、現在の動作を維持し、Navigation SDK 情報のレンダリングには永続的な通知のみを使用することです。ただし、システムがナビゲートしていない場合を除きます。システムがナビゲーションを行っていない場合は、「Google マップ」というデフォルトの Navigation SDK 文字列ではなく、initForegroundServiceManagerMessageAndIntent() に指定された文字列が表示されます。この呼び出しは、通知がクリックされたときに起動する再開インテントの設定にも使用できます。
永続的な通知のレンダリングを完全に制御できる
最後のシナリオでは、通知プロバイダを定義して作成し、initForegroundServiceManagerProvider() を介して ForegroundServiceManager に渡す必要があります。このオプションを使用すると、通知のレンダリング内容を完全に制御できますが、Navigation SDK の通知情報と通知が切断されるため、通知に表示される有用なターンバイターン プロンプトが削除されます。Google では、この情報を取得して通知に挿入するシンプルな手段をまだ提供していません。

通知プロバイダの例

次のコード例は、シンプルな通知コンテンツ プロバイダを使用して通知を作成し、返す方法を示しています。

public class NotificationContentProviderImpl
   extends NotificationContentProviderBase
   implements NotificationContentProvider {
 private String channelId;
 private Context context;
 private String message;

 /** Constructor */
 public NotificationContentProviderImpl(Application application) {
   super(application);
   message = "-- uninitialized --";
   channelId = null;
   this.context = application;
 }

 /**
  * Sets message to display in the notification. Calls updateNotification
  * to display the message immediately.
  *
  * @param msg The message to display in the notification.
  */
 public void setMessage(String msg) {
   message = msg;
   updateNotification();
 }

 /**
  * Returns the notification as it should be rendered.
  */
 @Override
 public Notification getNotification() {
   Notification notification;

   if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
     Spanned styledText = Html.fromHtml(message, FROM_HTML_MODE_LEGACY);
     String channelId = getChannelId(context);
     notification =
         new Notification.Builder(context, channelId)
             .setContentTitle("Notifications Demo")
             .setStyle(new Notification.BigTextStyle()
                 .bigText(styledText))
             .setSmallIcon(R.drawable.ic_navigation_white_24dp)
             .setTicker("ticker text")
             .build();
   } else {
     notification = new Notification.Builder(context)
         .setContentTitle("Notification Demo")
         .setContentText("testing non-O text")
         .build();
   }

   return notification;
 }

 // Helper to set up a channel ID.
 private String getChannelId(Context context) {
   if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
     if (channelId == null) {
       NotificationManager notificationManager =
           (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
       NotificationChannel channel = new NotificationChannel(
           "default", "navigation", NotificationManager.IMPORTANCE_DEFAULT);
       channel.setDescription("For navigation persistent notification.");
       notificationManager.createNotificationChannel(channel);
       channelId = channel.getId();
     }
     return channelId;
   } else {
     return "";
   }
 }
}

注意点と今後の計画

  • 予想される使用シナリオが明確に定義されるように、initForegroundServiceManagerMessageAndIntent() または initForegroundServiceManagerProvider() を早めに呼び出してください。新しい Navigator を作成する前に、このメソッドを呼び出す必要があります。
  • コードパスウェイが複数回入力される場合に備えて、initForegroundServiceManagerMessageAndIntent() または initForegroundServiceManagerProvider() の呼び出しで例外を必ずキャッチしてください。Navigation SDK v2.0 では、このメソッドを複数回呼び出すと、ランタイム例外ではなく、チェック例外がスローされます。
  • 通知の全期間を通じて、ヘッダー スタイルと一致する一貫したスタイル設定を実現するための作業がまだ残っている場合があります。
  • 通知プロバイダを定義すると、優先度でヘッドアップ動作を制御できます。
  • Google は、通知プロバイダが通知に挿入する可能性のあるターンバイターンの情報を取得するシンプルな手段はまだ提供していません。