モバイル通知を統合する

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

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

永続的な通知を統合する

コンポーネント

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


Navigation SDK には、ForegroundServiceManager シングルトンを初期化して取得するための静的メソッドが含まれています。このシングルトンは、Navigation SDK のライフタイム中に 1 回だけ初期化できます。そのため、初期化呼び出し(initForegroundServiceManagerMessageAndIntent() または initForegroundServiceManagerProvider())のいずれかを使用する場合は、そのパスが再入力された場合に備えて、try-catch ブロックで囲む必要があります。どちらかのメソッドを複数回呼び出すと、Navigation SDK はランタイム例外をスローします。ただし、ForegroundServiceManager への参照をすべてクリアし、後続の呼び出しの前に clearForegroundServiceManager() を呼び出した場合は例外をスローしません。

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

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

Navigation SDK の getForegroundServiceManager() メソッドは、フォアグラウンド サービス マネージャーのシングルトンを返します。まだ生成していない場合は、notificationId、defaultMessage、resumeIntent に 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 "";
   }
 }
}

NotificationContentProviderImpl を作成したら、次のコードを使用して Navigation SDK を接続します。

ForegroundServiceManager f = NavigationApi.getForegroundServiceManager(getApplication());
mNotification = new NotificationContentProviderImpl(getApplication());
NavigationApi.clearForegroundServiceManager();
NavigationApi.initForegroundServiceManagerProvider(getApplication(), null, mNotification);

注意事項と今後の計画

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