공유된 영구 알림

Android API 수준 26부터는 포그라운드 서비스에 지속적인 알림이 필요합니다. 이 요구사항은 특히 배터리를 포함하여 시스템 리소스를 과도하게 요구할 수 있는 서비스를 숨기는 것을 방지하기 위한 것입니다. 이 요구사항은 잠재적인 문제를 야기합니다. 여러 포그라운드 서비스가 있는 앱이 알림을 신중하게 관리하여 모든 서비스에서 공유되도록 하지 않으면 닫을 수 없는 여러 알림이 지속적으로 존재하여 활성 알림 목록에 원치 않는 잡음이 발생합니다.

앱과는 별개로 포그라운드 서비스를 실행하는 Navigation SDK와 같은 SDK를 사용하면 이러한 SDK를 사용하기가 더 어려워집니다. 이러한 SDK는 자체적인 독립적인 영구 알림이 있어 통합이 어려울 수 있습니다. 이러한 문제를 해결하기 위해 Navigation SDK v1.11에는 SDK 내부를 포함하여 앱 전반에서 지속적인 알림을 관리하는 데 도움이 되는 간단한 API가 도입되었습니다.

지속적인 알림 통합

구성요소

포그라운드 서비스 관리자는 Android 포그라운드 서비스 클래스와 영구 알림 클래스 주위에 래퍼를 제공합니다. 이 래퍼의 기본 기능은 관리자를 사용하는 모든 포그라운드 서비스 간에 알림이 공유되도록 알림 ID 재사용을 적용하는 것입니다.


NavigationAPI에는 ForegroundServiceManager 싱글톤을 초기화하고 가져오는 정적 메서드가 포함되어 있습니다. 이 싱글톤은 Navigation SDK의 전체 기간 동안 한 번만 초기화할 수 있습니다. 따라서 초기화 호출 중 하나 (initForegroundServiceManagerMessageAndIntent() 또는 initForegroundServiceManagerProvider())를 사용하는 경우 경로가 다시 입력될 경우를 대비해 try/catch 블록으로 호출을 둘러싸야 합니다. 비호환성 문제를 방지하기 위해 먼저 ForegroundServiceManager의 모든 참조를 지우고 각 후속 호출 전에 clearForegroundServiceManager()를 호출하지 않는 한, 한 메서드를 두 번 이상 호출하면 Navigation SDK에서 런타임 예외가 발생합니다. Navigation SDK v2.0에서는 이를 위해 확인된 예외가 API에 추가되었습니다.

initForegroundServiceManagerMessageAndIntent()의 네 가지 매개변수는 application, notificationId, defaultMessage, resumeIntent입니다. 마지막 세 개의 매개변수가 null이면 알림은 표준 탐색 SDK 알림입니다. 여전히 이 알림 뒤에 앱의 다른 포그라운드 서비스를 숨길 수 있습니다. notificationId 매개변수는 알림에 사용해야 하는 알림 ID를 지정합니다. null인 경우 임의의 값이 사용됩니다. 다른 SDK의 알림과 같은 다른 알림과의 충돌을 해결하도록 명시적으로 설정할 수 있습니다. defaultMessage는 시스템이 탐색하지 않을 때 표시되는 문자열입니다. resumeIntent는 알림을 클릭하면 실행되는 인텐트입니다. resumeIntent가 null이면 알림 클릭이 무시됩니다.

initForegroundServiceManagerProvider()의 세 가지 매개변수는 application, notificationId, notificationProvider입니다. 마지막 두 매개변수가 null이면 알림은 표준 탐색 SDK 알림입니다. notificationId 매개변수는 알림에 사용해야 하는 알림 ID를 지정합니다. null인 경우 임의의 값이 사용됩니다. 다른 SDK의 알림과 같은 다른 알림과의 충돌을 해결하도록 명시적으로 설정할 수 있습니다. notificationProvider가 설정된 경우 제공자는 항상 렌더링할 알림을 생성해야 합니다.

Navigation SDK의 getForegroundServiceManager() 메서드는 포그라운드 서비스 관리자 싱글톤을 반환합니다. 아직 생성하지 않았다면 notificationId, defaultMessage, resumeIntent의 null 매개변수를 사용하여 initForegroundServiceManagerMessageAndIntent()를 호출하는 것과 같습니다.

ForegroundServiceManager에는 세 가지 간단한 메서드가 있습니다. 처음 두 개는 서비스를 포그라운드 안팎으로 이동하기 위한 것으로, 일반적으로 만들어진 서비스 내에서 호출됩니다. 이러한 메서드를 사용하면 서비스가 공유된 영구 알림과 연결됩니다. 마지막 메서드인 updateNotification()는 알림이 변경되었으며 다시 렌더링되어야 한다고 관리자에게 신고합니다.

공유 영구 알림의 콘텐츠를 완전히 제어하려는 경우 새 API는 알림 제공자를 정의하기 위한 NotificationContentProvider 인터페이스를 제공합니다. 이 인터페이스에는 현재 콘텐츠로 알림을 받는 단일 메서드가 포함되어 있습니다. 또한 제공자를 정의하는 데 도움이 되도록 선택적으로 사용할 수 있는 기본 클래스를 제공합니다. 기본 클래스의 주요 목적 중 하나는 ForegroundServiceManager에 액세스하지 않고도 updateNotification()를 쉽게 호출할 수 있도록 하는 것입니다. 이 도우미 메서드는 알림 제공자의 인스턴스를 사용하여 새 알림 메시지를 수신하는 경우에 편리하며, 이때 이 내부 메서드를 직접 호출하여 알림의 메시지를 렌더링할 수 있습니다.

사용 시나리오

이 섹션에서는 공유된 영구 알림을 사용하기 위한 사용 시나리오를 자세히 설명합니다.

다른 앱 포그라운드 서비스의 지속적인 알림 숨기기
가장 쉬운 시나리오는 현재 동작을 유지하고 Navigation SDK 정보 렌더링에만 지속적인 알림만 사용하는 것입니다. 다른 서비스는 포그라운드 서비스 관리자 startForeground()stopForeground() 메서드를 사용하여 이 알림 뒤에 숨길 수 있습니다.
다른 앱 포그라운드 서비스의 지속적인 알림은 숨기고 탐색하지 않을 때 표시되는 기본 텍스트를 설정합니다.
두 번째로 가장 쉬운 시나리오는 현재 동작을 유지하고 시스템이 탐색 중이 아닌 경우를 제외하고 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은 헤더 스타일 지정과 일치하는 알림의 전체 기간 동안 스타일을 일관되게 지정하기 위해 할 일이 남아 있을 수 있습니다.
  • 알림 제공자를 정의할 때 우선순위로 헤드업 동작을 제어할 수 있습니다.
  • Google에서는 알림 제공자가 알림에 삽입할 수 있는 세부 경로 안내 정보를 검색하는 간단한 방법을 아직 제공하지 않습니다.