안드로이드 시스템은 디바이스에 다른 App이 설치되거나 삭제될 때 브로드캐스트 이벤트를 전달합니다. 앱이 이벤트를 받으려면 관련된 인텐트를 BroadcastReceiver로 등록을 하면 됩니다.
다음과 같은 상황에 다음과 같은 Action의 인텐트가 브로드캐스트 됩니다.
- 앱이 디바이스에 처음 설치될 때 :
android.intent.action.PACKAGE_ADDED
- 앱이 업데이트 될 때 :
android.intent.action.PACKAGE_REPLACED
- 앱이 (현재 User에서만) 삭제될 때 :
android.intent.action.PACKAGE_REMOVED
- 앱이 디바이스에서 완전히 삭제될 때 :
android.intent.action.PACKAGE_FULLY_REMOVED
특별히, 앱이 업데이트 될 때 업데이트 되는 앱 자신에게는 android.intent.action.MY_PACKAGE_REPLACED
가 전달됩니다.
이 인텐트는 이름처럼 다른 앱으로는 전달되지 않습니다.
각각의 인텐트에 대한 리시버를 어떻게 등록하는지 알아보겠습니다.
ACTION_PACKAGE_ADDED : 앱 설치 이벤트
Android 8.0의 Broadcast Limitations 정책으로, 암시적 인텐트로 전달되는 브로드캐스트는 App의 AndroidManifest.xml에 등록된 리시버로는 전달되지 않습니다. 이것은 Target API가 26 이상인 앱을 대상으로 합니다. API 26 미만인 앱에게는 모두 전달됩니다.
Target API 26 이상인 앱들이 브로드캐스트를 받으려면 Context.registerReceiver()
API를 사용해서 리시버를 등록해야 합니다.
암시적 인텐트는 다음과 같이 인텐트에 package 정보가 없는 것을 말합니다.
val intent = Intent(Intent.ACTION_MAIN)
명시적 인텐트는 다음과 같이 인텐트에 package 정보가 있는 것을 말합니다.
val intent = Intent(Intent.ACTION_MAIN)
intent.`package` = "target.package.name"
ACTION_PACKAGE_ADDED
는 암시적 인텐트로 전달되는 인텐트입니다. 따라서 Target 26 이상에서는 App의 Manifest에 리시버를 등록하면 인텐트가 전달되지 않습니다.
Context를 통해 등록해야 합니다.
API 26 이상에서 리시버 등록
ACTION_PACKAGE_ADDED
는 API 26 이상에서 Context로만 등록해야 합니다.
다음과 같이 리시버를 등록할 수 있습니다.
val intentFilter = IntentFilter()
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED)
intentFilter.addDataScheme("package")
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Log.d("MyReceiver", "onReceive: $intent")
}
}
context.registerReceiver(receiver, intentFilter)
예제에서 Receiver는 익명 객체로 생성하였습니다.
그리고 IntentFilter를 생성할 때 scheme을 꼭 "package"로 설정해야 합니다. 그 이유는 전달되는 인텐트의 data의 scheme이 "package"로 설정되어있기 때문입니다. 그렇기 때문에 scheme을 "package"로 설정하지 않으면 이벤트를 받을 수 없습니다.
아래 로그는 위의 코드로 리시버를 등록하고 앱을 설치했을 때 출력되는 로그입니다.
07-02 21:12:48.021 6331 6331 D MyReceiver: onReceive: Intent { act=android.intent.action.PACKAGE_ADDED dat=package:com.codechacha.myapplication flg=0x4000010 (has extras) }
API 26 미만에서 리시버 등록
API 26 미만에서는 Context와 Manifest를 이용하여 모두 리시버를 등록할 수 있습니다.
다음과 같이 AndroidManifest.xml에 리시버를 등록할 수 있습니다.
<receiver android:name=".MyReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED" />
<data android:scheme="package" />
</intent-filter>
</receiver>
위에서 사용한 MyReceiver
클래스는 다음과 같이 구현하였습니다.
class MyReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Log.d("MyReceiver", "onReceive: $intent")
}
}
Context 코드는 위에서 소개한 것과 동일합니다.
ACTION_PACKAGE_REPLACED : 앱 업데이트 이벤트
ACTION_PACKAGE_REPLACED
는 앱이 업데이트될 때 전달되는 인텐트입니다. 이 인텐트도 암시적 인텐트입니다.
API 26 이상에서 리시버 등록
API 26 이상에서는 Context로만 등록이 가능합니다.
다음과 같이 등록할 수 있습니다. action만 다르고 ACTION_PACKAGE_ADDED
코드와 동일합니다.
val intentFilter = IntentFilter()
intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED)
intentFilter.addDataScheme("package")
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Log.d("MyReceiver", "onReceive: $intent")
}
}
API 26 미만에서 리시버 등록
다음과 같이 리시버를 등록할 수 있습니다.
<receiver android:name=".MyReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<data android:scheme="package" />
</intent-filter>
</receiver>
앱 업데이트 시, 인텐트 전달 순서
디바이스에 앱이 최초로 설치가 되면 ACTION_PACKAGE_ADDED
만 전달됩니다.
하지만 앱이 업데이트될 때는 다음 인텐트가 순차적으로 전달됩니다.
1. ACTION_PACKAGE_REMOVED
2. ACTION_PACKAGE_ADDED
3. ACTION_PACKAGE_REPLACED
ACTION_PACKAGE_REMOVED
가 전달되는 이유는 업데이트 이전 버전의 앱이 삭제되었다는 의미이고, 새로운 버전이 설치되고 업데이트되었다는 의미로 ADDED와 REPLACED 인텐트가 전달됩니다.
ACTION_PACKAGE_ADDED 인텐트로 앱 업데이트 정보 확인
ACTION_PACKAGE_ADDED
인텐트는 Intent.EXTRA_REPLACING
라는 이름의 Extra로 REPLACED에 대한 정보를 갖고 있습니다.
따라서, PACKAGE_ADDED
인텐트를 받는다면 PACKAGE_REPLACED
를 추가로 받지 않아도 됩니다.
다음과 같이 ACTION_PACKAGE_ADDED
에서 Replaced 정보를 얻을 수 있습니다.
val intentFilter = IntentFilter()
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED)
intentFilter.addDataScheme("package")
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val replaced = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
Log.d("MyReceiver", "onReceive: $intent")
Log.d("MyReceiver", "Replaced ? $replaced")
}
}
아래는 처음 설치될 때의 로그와 업데이트될 때의 로그입니다.
업데이트될 때 Intent.EXTRA_REPLACING
가 true로 전달됩니다.
// first install
07-02 21:35:20.022 7040 7040 D MyReceiver: onReceive: Intent { act=android.intent.action.PACKAGE_ADDED dat=package:com.codechacha.myapplication flg=0x4000010 (has extras) }
07-02 21:35:20.022 7040 7040 D MyReceiver: Replaced ? false
// upgrade
07-02 21:35:20.667 7040 7040 D MyReceiver: onReceive: Intent { act=android.intent.action.PACKAGE_ADDED dat=package:com.codechacha.myapplication flg=0x4000010 (has extras) }
07-02 21:35:20.667 7040 7040 D MyReceiver: Replaced ? true
ACTION_PACKAGE_REMOVED : 앱 삭제 이벤트
ACTION_PACKAGE_REMOVED
는 앱이 삭제될 때 전달되는 인텐트이며, 암시적 인텐트로 전달됩니다.
안드로이드는 Multi user를 지원하며 각각의 User에 앱이 설치될 수 있습니다. 이 인텐트는 어떤 User에서 앱이 삭제될 때 전달됩니다. 이 인텐트를 받았다고해서 디바이스에서 완전히 앱이 삭제되었다는 것을 의미하진 않습니다.
디바이스에서(모든 Multi user에서) 앱이 완전히 삭제되었을 때는 ACTION_PACKAGE_FULLY_REMOVED
인텐트가 전달됩니다.
API 26 이상에서 리시버 등록
API 26 이상인 앱은 다음과 같이 리시버를 등록할 수 있습니다.
val intentFilter = IntentFilter()
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED)
intentFilter.addDataScheme("package")
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d("MyReceiver", "onReceive: $intent")
}
}
context.registerReceiver(receiver, intentFilter)
API 26 미만에서 리시버 등록
API 26 미만인 앱은 다음과 같이 인텐트를 전달할 수 있습니다.
<receiver android:name=".MyReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REMOVED" />
<data android:scheme="package" />
</intent-filter>
</receiver>
ACTION_PACKAGE_FULLY_REMOVED : 디바이스 전체에서 앱 삭제
ACTION_PACKAGE_FULLY_REMOVED
는 디바이스 전체에서 앱이 삭제될 때 전달되는 이벤트입니다.
암시적 이벤트이지만, 이 인텐트는 예외적으로 Manifest에서 등록할 수 있습니다.
예외적으로 몇몇 암시적 인텐트는 Manifest에 리시버 등록을 허용합니다. Implicit Broadcast Exceptions를 보시면 어떤 인텐트가 예외인지 알 수 있습니다.
따라서, Target API 레벨 구분 없이 Manifest와 Context로 등록 가능합니다.
다른 인텐트와 비슷한 형식으로 Manifest에 리시버를 등록할 수 있습니다.
<receiver android:name=".MyReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
<data android:scheme="package" />
</intent-filter>
</receiver>
Output:
07-02 21:46:31.388 7845 7845 D MyReceiver: onReceive: Intent { act=android.intent.action.PACKAGE_FULLY_REMOVED dat=package:com.codechacha.myapplication flg=0x5000010 cmp=com.codechacha.packageintent/.MyReceiver (has extras) }
Context로 등록하는 것도 다른 인텐트의 예제와 유사합니다.
ACTION_MY_PACKAGE_REPLACED : 앱 자신에게 업데이트 이벤트 전달
ACTION_MY_PACKAGE_REPLACED
인텐트는, 예를 들어 com.codechacha.myapplication
라는 앱을 업데이트할 때 com.codechacha.myapplication
앱에게 전달하는 인텐트입니다.
이 인텐트는 명시적 인텐트로, Manifest와 Context로 모두 리시버 등록이 가능합니다. 하지만 업데이트되면서 앱 자신은 종료되기 때문에 Context로 받지 못합니다.
다음과 Manifest에 리시버를 등록할 수 있습니다. 전달되는 인텐트에는 scheme 정보가 없기 때문에 IntentFilter에도 scheme을 설정하면 안됩니다.
<receiver android:name=".MyReceiver">
<intent-filter>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
</intent-filter>
</receiver>
Related Posts
- Android 14 - 사진/동영상 파일, 일부 접근 권한 소개
- Android - adb push, pull로 파일 복사, 다운로드
- Android 14 - 암시적 인텐트 변경사항 및 문제 해결
- Jetpack Compose - Row와 Column
- Android 13, AOSP 오픈소스 다운로드 및 빌드
- Android 13 - 세분화된 미디어 파일 권한
- Android 13에서 Notification 권한 요청, 알림 띄우기
- Android 13에서 'Access blocked: ComponentInfo' 에러 해결
- 에러 해결: android gradle plugin requires java 11 to run. you are currently using java 1.8.
- 안드로이드 - 코루틴과 Retrofit으로 비동기 통신 예제
- 안드로이드 - 코루틴으로 URL 이미지 불러오기
- Android - 진동, Vibrator, VibrationEffect 예제
- Some problems were found with the configuration of task 에러 수정
- Query method parameters should either be a type that can be converted into a database column or a List
- 우분투에서 Android 12 오픈소스 다운로드 및 빌드
- Android - ViewModel을 생성하는 방법
- Android - Transformations.map(), switchMap() 차이점
- Android - Transformations.distinctUntilChanged() 소개
- Android - TabLayout 구현 방법 (+ ViewPager2)
- Android - 휴대폰 전화번호 가져오는 방법
- Android 12 - Splash Screens 알아보기
- Android 12 - Incremental Install (Play as you Download) 소개
- Android - adb 명령어로 bugreport 로그 파일 추출
- Android - adb 명령어로 App 데이터 삭제
- Android - adb 명령어로 앱 비활성화, 활성화
- Android - adb 명령어로 특정 패키지의 PID 찾기
- Android - adb 명령어로 퍼미션 Grant 또는 Revoke
- Android - adb 명령어로 apk 설치, 삭제
- Android - adb 명령어로 특정 패키지의 프로세스 종료
- Android - adb 명령어로 screen capture 저장
- Android - adb 명령어로 System 앱 삭제, 설치
- Android - adb 명령어로 settings value 확인, 변경
- Android 12 - IntentFilter의 exported 명시적 선언
- Android - adb 명령어로 공장초기화(Factory reset)
- Android - adb logcat 명령어로 로그 출력