モバイル通知を統合する

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 つのパラメータは、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 つは、ForegroundServiceManager にアクセスしなくても updateNotification() を呼び出す方法を提供することです。通知プロバイダのインスタンスを使用して新しい通知メッセージを受信する場合は、この内部メソッドを直接呼び出して、通知にメッセージをレンダリングできます。

利用シナリオ

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

他のアプリのフォアグラウンド サービスの永続的な通知を非表示にする
最も簡単なシナリオは、現在の動作を維持し、Navigation SDK 情報をレンダリングする場合にのみ、永続的な通知を使用することです。他のサービスは、フォアグラウンド サービス マネージャーの startForeground() メソッドと stopForeground() メソッドを使用して、この通知の背後に隠すことができます。
他のアプリのフォアグラウンド サービスの永続的な通知を非表示にしますが、ナビゲートしていないときに表示されるデフォルトのテキストを設定します。
2 番目に簡単なシナリオは、現在の動作を保持し、システムがナビゲートしていない場合を除き、Navigation SDK 情報のレンダリングにのみ永続通知を使用することです。システムがナビゲーションを行っていない場合、initForegroundServiceManagerMessageAndIntent() に指定された文字列が表示されます。デフォルトの Navigation SDK 文字列には「Google マップ」と記載されています。この呼び出しを使用して、通知がクリックされたときにトリガーされる再開インテントを設定することもできます。
永続通知のレンダリングを完全に制御する
最後のシナリオでは、通知プロバイダを定義して作成し、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() を早めに呼び出してください。新しいナビゲータを作成する前に、このメソッドを呼び出す必要があります。
  • コードパスウェイが複数回入力される場合に備えて、initForegroundServiceManagerMessageAndIntent() または initForegroundServiceManagerProvider() の呼び出しによる例外を必ずキャッチしてください。Navigation SDK v2.0 では、このメソッドを複数回呼び出すと、ランタイム例外ではなくチェック例外がスローされます。
  • ヘッダーのスタイル設定と一致する通知の全期間にわたって、一貫したスタイル設定を得るために、Google が行う作業がまだ残っている可能性があります。
  • 通知プロバイダを定義するときに、優先度でヘッドアップの動作を制御できます。
  • Google は、通知プロバイダが通知に挿入するルート案内情報を取得するための簡単な手段を提供していません。