아래 내용은 공식문서의 내용 중 일부를 번역한 내용이다.
PendingIntent는 getActivity(Context, int, Intent, int), getActivities(Context, int, Intent, int), getBroadcast(Context, int, Intent, int), and getService(Context, int, Intent, int) 로 생성되며, 반환되는 객체는 추후에 작성된 작업을 수행할 수 있도록 다른 애플리케이션에 전달된다
전달된 객체 (PendingIntent)는 자신의 Intent와 동일하게 작업을 수행할 권한을 얻는다. 다른 앱에서 작업을 실행할 수 있는 만큼, 보안에 각별히 귀 기울여야 한다.
또한 PendingIntent를 생성한 애플리케이션이 종료되더라도, 시스템에서 이를 유지하기 때문에 PendingIntent를 제공받은 다른 애플리케이션에서는 해당 객체를 이용하여 작업 가능하다.
이해하기 쉽게 채팅 푸시알림을 터치하면 해당 채팅방으로 접속하는 동작을 생각해보자.
안드로이드 시스템의 NotificationManager 가 Intent 를 실행 -> 다른 프로세스에서 Intent 를 수행하는 것이므로 Intent가 아닌, PendingIntent가 필연적.
또한 PendingIntent로 전달받은 채팅 앱에서는 특정 동작을 그대로 이어서 수행 가능.
위의 동작에서 PendingIntent가 아닌 일반 Intent 객체를 넘겨주면, 다른 애플리케이션에서 정의한 Intent를 실행하는것 이기 때문에 절대 불가능하다.
이제 PendingIntent를 사용하는 방법을 알아보자.
PendingIntent.getActivity (Context context,
int requestCode,
Intent intent,
int flags)
flags :
PendingIntent.FLAG_UPDATE_CURRENT
시스템이 유지하고 있는 PendingIntent가 있다면, 해당 PendingIntent의 Extra data를 업데이트한다.
PendingIntnet.FLAG_CANCEL_CURRENT
시스템이 유지하고 있는 PendingIntent가 있다면 취소하고 다시 생성한다.
기존 Intent의 Extra data를 업데이트하고 싶을 때 사용할 수 있다. (like PendingIntent.FLAG_UPDATE_CURRENT)
PendingIntent.FLAG_NO_CREATE
시스템이 유지하고 있는 PendingIntent가 없다면, 굳이 새롭게 생성하지 않는다.
수신하는 곳에서 Intent가 null이다.
PendingIntent.FLAG_ONE_SHOT
PendingIntent를 오직 한 번만 사용한다.
만약 동일한 PendingIntent에 대해 재호출(e.g. PendingIntent.send() 두 번 호출)하면 예외가 발생한다.
PendingIntent.FLAG_IMMUTABLE
PendingIntent.send()에 intent를 전달해도 값을 채울 수 없다.
공식문서에서는 해당 FLAG 사용을 강력히 권장한다. 그 이유는 다른 어플리케이션에서 PendingIntent의 Intent를 함부로 수정하지 못하게 함으로써 보안을 높일 수 있기 때문이다.
PendingIntent 생성자(Creator)는 항상 PendingIntent.FLAG_UPDATE_CURRENT를 함께 사용하여 자체 PendingIntent를 변경할 수 있다.
PendingIntent.FLAG_MUTABLE
PendingIntent.send()에 intent를 전달하여 채워지지 않은 Extra Data를 추가할 수 있다.
API 31 이전에는 따로 설정하지 않으면 기본값이 MUTABLE이다.
Notification의 RemoteInput(알림에서 답장), Bubble(도움말 풍선) 처럼 사용자 입력 텍스트를 intent에 추가(= 수정)해야 하는 경우에만 사용해야 한다.
For security reasons, the Intent objects you supply here should almost always be explicit intents, that is specify an explicit component to be delivered to through Intent.setClass
보안상의 이유로, Intent.setClass() 를 통해 항상 명시적 인텐트를 지정해주어야 한다.
val intent1 = Intent(this, ChatActivity::class.java).apply {
putExtra("hello", "hello")
}
val pendingIntent1 =
PendingIntent.getActivity(this, 77, intent1, PendingIntent.FLAG_IMMUTABLE)
val intent2 = Intent(this, ChatActivity::class.java).apply {
putExtra("hello1", "hello1")
}
val pendingIntent2 =
PendingIntent.getActivity(this, 77, intent1, PendingIntent.FLAG_IMMUTABLE)
intent1, intent2는 분명 다른 객체이다. (주소값이 다르기 때문)
따라서 이를 PendingIntent에 전달하면, 다른 PendingIntent를 반환받을 것 같지만 그렇지 않다. 그 이유는 동일한 Request Code, intent.filterEquals()가 참인 Intent에 대해서는 동일한 PendingIntent를 반환받기 때문이다.
이러한 이유로 위와같은 코드를 실행하면
pendingIntent1.cancel()
pendingIntent2.send()
pendingIntent1 과 pendingIntent2는 같은 객체이기 때문에, 이미 cancel()된 pendingIntent에 send() 함수를 호출하면 Exception이 발생하면서 앱이 죽게된다. 따라서 pendingIntent 객체를 생성할 때 위와 같은 사항을 잘 고려하여 생성해야 한다. ( e.g putExtra로 다른 값만 넘겨줄 경우 동일한 객체로 인식함.)