Android API レベル 26 以降では、フォアグラウンド サービスに永続通知が必要です。この要件は、システム リソース(特にバッテリー)に過剰な負荷をかける可能性のあるサービスを隠すことを防ぐためのものです。この要件により、潜在的な問題が生じます。複数のフォアグラウンド サービスを持つアプリが、すべてのサービスで共有されるように通知を慎重に管理しないと、複数の永続的な閉じることができない通知が表示され、通知のアクティブ リストが煩雑になる可能性があります。
この問題は、Navigation SDK などの SDK を使用するとさらに複雑になります。これらの SDK は、アプリとは独立してフォアグラウンド サービスを実行し、独自の独立した永続通知を表示するため、統合が困難になります。こうした問題に対処するため、Navigation SDK v1.11 では、SDK 内を含め、アプリ全体で永続通知を管理するためのシンプルな API が導入されました。
コンポーネント
フォアグラウンド サービス マネージャーは、Android のフォアグラウンド サービス クラスと永続通知クラスのラッパーを提供します。このラッパーの主な機能は、Notification ID の再利用を強制して、マネージャーを使用するすべてのフォアグラウンド サービス間で通知が共有されるようにすることです。
Navigation SDK には、ForegroundServiceManager
シングルトンを初期化して取得するための静的メソッドが含まれています。このシングルトンは、Navigation SDK のライフタイムで 1 回だけ初期化できます。したがって、初期化呼び出し(initForegroundServiceManagerMessageAndIntent()
または initForegroundServiceManagerProvider()
)のいずれかを使用する場合は、そのパスが再入力された場合に備えて、try-catch ブロックで囲む必要があります。ForegroundServiceManager
へのすべての参照を最初にクリアし、後続の各呼び出しの前に clearForegroundServiceManager()
を呼び出さない限り、いずれかのメソッドを複数回呼び出すと、Navigation SDK は実行時例外をスローします。
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 つは、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()
を早めに呼び出すようにしてください。このメソッドは、新しい Navigator を作成する前に呼び出す必要があります。 - コードパスが複数回入力された場合に備えて、
initForegroundServiceManagerMessageAndIntent()
またはinitForegroundServiceManagerProvider()
への呼び出しから例外をキャッチするようにしてください。Navigation SDK v2.0 では、このメソッドを複数回呼び出すと、実行時例外ではなくチェック済み例外がスローされます。 - Google は、通知の有効期間にわたってヘッダーのスタイルと一致する一貫したスタイルを適用するために、まだ作業が必要になる可能性があります。
- 通知プロバイダを定義するときに、優先度を使用してヘッドアップの動作を制御できます。
- Google は、通知プロバイダが通知に挿入する可能性があるターンバイターンの情報を取得する簡単な手段を提供していません。