Android - 앱 설치, 삭제 이벤트 받기 (BroadcastReceiver 인텐트 받기)

JS · 01 Jul 2020

안드로이드 시스템은 디바이스에 다른 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>
댓글을 보거나 쓰려면 이 버튼을 눌러주세요.
codechachaCopyright ©2019 codechacha