未收到 ACTION_PACKAGE_REMOVED 广播 - Android 10/设置
Not getting ACTION_PACKAGE_REMOVED broadcast - Android 10/Settings
我们想知道何时从 Android 设备上卸载应用程序。我们设置有关设备应用程序的广播接收器(不在清单中)并在接收到它们时进行处理。问题:卸载应用程序时(使用 Android 设置 -> Apps/Notifications -> AppYouWantToUninstall -> 点击垃圾桶)收不到广播......然而,只有当应用程序安装在Android 10 台设备。
当然还有另外两种主要的卸载应用程序的方法:1)在应用程序上tap/hold并调出"App Info"菜单;和 2) 进入 Play 商店。其他两种方法传送 ACTION_PACKAGE_REMOVED.
的广播
OS(Android 7、8、9 和 10)的所有组合和三种卸载方法都提供 ACTION_PACKAGE_REMOVED 广播 除了设置卸载当应用程序在 Android 10 设备上时的方法。 我也持怀疑态度,所以我将回答几个可能的问题:"Yes, if we use the Play Store uninstall for an app on Android 10, we do get a broadcast" 和 "Yes, if we use the Settings uninstall for an app on Android 7, 8, 9 we do get a broadcast"
class中的定义:
class AppInstallBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent == null || context == null) return
when (intent.action) {
Intent.ACTION_PACKAGE_ADDED ->
enqueueWork(context, getIntent(context, ACTION_PACKAGE_ADDED, intent.data?.schemeSpecificPart))
Intent.ACTION_PACKAGE_REPLACED ->
enqueueWork(context, getIntent(context, ACTION_PACKAGE_REPLACED, intent.data?.schemeSpecificPart))
Intent.ACTION_PACKAGE_CHANGED ->
enqueueWork(context, getIntent(context, ACTION_PACKAGE_CHANGED, intent.data?.schemeSpecificPart))
Intent.ACTION_PACKAGE_REMOVED ->
enqueueWork(context, getIntent(context, ACTION_PACKAGE_REMOVED, intent.data?.schemeSpecificPart))
Intent.ACTION_PACKAGE_FULLY_REMOVED ->
enqueueWork(context, getIntent(context, ACTION_PACKAGE_FULLY_REMOVED, intent.data?.schemeSpecificPart))
}
}
...这是我们处理收到的操作的地方
private suspend fun processAction(action:String, packageName: String, context: Context) {
when (action) {
ACTION_PACKAGE_ADDED -> updateAppInfo(context, packageName, false)
ACTION_PACKAGE_REPLACED -> updateAppInfo(context, packageName, true)
ACTION_PACKAGE_REMOVED -> deleteAppInfo(context, packageName)
ACTION_PACKAGE_FULLY_REMOVED -> deleteFullyAppInfo(context, packageName)
}
}
这是我们定义(在应用程序中,而不是清单中)接收者的地方:
private val installBroadcastReceiver = AppInstallBroadcastReceiver()
private val installReceiverFilter = IntentFilter().apply {
addAction(Intent.ACTION_PACKAGE_ADDED)
addAction(Intent.ACTION_PACKAGE_REMOVED)
addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED)
addAction(Intent.ACTION_PACKAGE_CHANGED)
addAction(Intent.ACTION_PACKAGE_REPLACED)
addDataScheme("package")
}
...我们注册的地方,过滤器
private fun registerAppChanges() {
Timber.d("Registering the installation receiver ..")
registerReceiver(installBroadcastReceiver, installReceiverFilter)
registerReceiver(unlockReceiver, unlockReceiverFilter)
}
为了运行一个Broadcast receiver你必须知道它是如何工作的。以前当你注册一个广播接收器时,它会在任何注册的动作发生时被触发,即使应用程序不在后台。然后 android 团队进行了一些更改以优化电池。如果你的应用程序目标是 26 或以上,你必须动态声明它而不是清单。这是正文
Note: If your app targets API level 26 or higher, you cannot use the
manifest to declare a receiver for implicit broadcasts (broadcasts
that do not target your app specifically), except for a few implicit
broadcasts that are exempted from that restriction. In most cases, you
can use scheduled jobs instead.
那么有什么选择呢?您必须使用上下文或 applicationContext 动态注册广播。因此,应用程序会在您的上下文处于活动状态时接收广播事件。这是来自 Google 的文本:
Context-registered receivers receive broadcasts as long as their
registering context is valid. For an example, if you register within
an Activity context, you receive broadcasts as long as the activity is
not destroyed. If you register with the Application context, you
receive broadcasts as long as the app is running.
那么即使您的应用不是 运行ning,您如何收听广播事件?
由于广播接收器不可靠,我想不出任何可行的解决方案,但您可以考虑 运行一直使用后台服务。这也太过分了。检查这个答案
Making BroadcastReceiver work in the background in API 26 or above
还有这个来自commonsware
Android O and the Implicit Broadcast Ban
我能想到的另一个解决方案,如果 API 级别低于 26,它应该按原样工作,但对于 api 26 及以上,在你的应用程序 onResume 中,你将检查每次可用应用程序和您当前的应用程序列表。然后删除多余的应用程序。
接受接收器基本上无法(不再)以对我们的情况有用的方式工作的现实,所使用的解决方案是 1) 在 onResume 中嵌入一些检查代码以查看两者之间是否存在差异设备现实和应用程序对现实的理解; 2)使用一种接收器类型(ACTION_PACKAGE_FULLY_REMOVED)至少跟上那些(我们案例中的主要问题)。
要影响新的接收器类型,您必须在清单中注册接收器,如下所示:
<receiver
android:name=".receiver.FullyRemovedBroadcastReceiver">
<!-- <android:exported="true"> -->
<intent-filter>
<action android:name="android.intent.action.PACKAGE_FULLY_REMOVED"/>
<data android:scheme="package"/>
</intent-filter>
</receiver>
其余部分(广播接收器的创建class):
class FullyRemovedBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent == null || context == null) return
when (intent.action) {
ACTION_PACKAGE_FULLY_REMOVED ->
enqueueWork(context, getIntent(context, ACTION_PACKAGE_FULLY_REMOVED, intent.data?.schemeSpecificPart))
}
}
private fun getIntent(context: Context?, action: String, packageName: String?) =
Intent(context, AppChangeJobIntentService::class.java).apply {
this.action = action
putExtra(EXTRA_PACKAGE_NAME, packageName)
putExtra(EXTRA_DATA_REMOVED, true)
putExtra(EXTRA_REPLACING, false)
}
private fun enqueueWork(context: Context, intent: Intent) {
JobIntentService.enqueueWork(
context,
AppChangeJobIntentService::class.java,
APP_CHANGE_JOB_ID,
intent)
}
}
我们想知道何时从 Android 设备上卸载应用程序。我们设置有关设备应用程序的广播接收器(不在清单中)并在接收到它们时进行处理。问题:卸载应用程序时(使用 Android 设置 -> Apps/Notifications -> AppYouWantToUninstall -> 点击垃圾桶)收不到广播......然而,只有当应用程序安装在Android 10 台设备。
当然还有另外两种主要的卸载应用程序的方法:1)在应用程序上tap/hold并调出"App Info"菜单;和 2) 进入 Play 商店。其他两种方法传送 ACTION_PACKAGE_REMOVED.
的广播OS(Android 7、8、9 和 10)的所有组合和三种卸载方法都提供 ACTION_PACKAGE_REMOVED 广播 除了设置卸载当应用程序在 Android 10 设备上时的方法。 我也持怀疑态度,所以我将回答几个可能的问题:"Yes, if we use the Play Store uninstall for an app on Android 10, we do get a broadcast" 和 "Yes, if we use the Settings uninstall for an app on Android 7, 8, 9 we do get a broadcast"
class中的定义:
class AppInstallBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent == null || context == null) return
when (intent.action) {
Intent.ACTION_PACKAGE_ADDED ->
enqueueWork(context, getIntent(context, ACTION_PACKAGE_ADDED, intent.data?.schemeSpecificPart))
Intent.ACTION_PACKAGE_REPLACED ->
enqueueWork(context, getIntent(context, ACTION_PACKAGE_REPLACED, intent.data?.schemeSpecificPart))
Intent.ACTION_PACKAGE_CHANGED ->
enqueueWork(context, getIntent(context, ACTION_PACKAGE_CHANGED, intent.data?.schemeSpecificPart))
Intent.ACTION_PACKAGE_REMOVED ->
enqueueWork(context, getIntent(context, ACTION_PACKAGE_REMOVED, intent.data?.schemeSpecificPart))
Intent.ACTION_PACKAGE_FULLY_REMOVED ->
enqueueWork(context, getIntent(context, ACTION_PACKAGE_FULLY_REMOVED, intent.data?.schemeSpecificPart))
}
}
...这是我们处理收到的操作的地方
private suspend fun processAction(action:String, packageName: String, context: Context) {
when (action) {
ACTION_PACKAGE_ADDED -> updateAppInfo(context, packageName, false)
ACTION_PACKAGE_REPLACED -> updateAppInfo(context, packageName, true)
ACTION_PACKAGE_REMOVED -> deleteAppInfo(context, packageName)
ACTION_PACKAGE_FULLY_REMOVED -> deleteFullyAppInfo(context, packageName)
}
}
这是我们定义(在应用程序中,而不是清单中)接收者的地方:
private val installBroadcastReceiver = AppInstallBroadcastReceiver()
private val installReceiverFilter = IntentFilter().apply {
addAction(Intent.ACTION_PACKAGE_ADDED)
addAction(Intent.ACTION_PACKAGE_REMOVED)
addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED)
addAction(Intent.ACTION_PACKAGE_CHANGED)
addAction(Intent.ACTION_PACKAGE_REPLACED)
addDataScheme("package")
}
...我们注册的地方,过滤器
private fun registerAppChanges() {
Timber.d("Registering the installation receiver ..")
registerReceiver(installBroadcastReceiver, installReceiverFilter)
registerReceiver(unlockReceiver, unlockReceiverFilter)
}
为了运行一个Broadcast receiver你必须知道它是如何工作的。以前当你注册一个广播接收器时,它会在任何注册的动作发生时被触发,即使应用程序不在后台。然后 android 团队进行了一些更改以优化电池。如果你的应用程序目标是 26 或以上,你必须动态声明它而不是清单。这是正文
Note: If your app targets API level 26 or higher, you cannot use the manifest to declare a receiver for implicit broadcasts (broadcasts that do not target your app specifically), except for a few implicit broadcasts that are exempted from that restriction. In most cases, you can use scheduled jobs instead.
那么有什么选择呢?您必须使用上下文或 applicationContext 动态注册广播。因此,应用程序会在您的上下文处于活动状态时接收广播事件。这是来自 Google 的文本:
Context-registered receivers receive broadcasts as long as their registering context is valid. For an example, if you register within an Activity context, you receive broadcasts as long as the activity is not destroyed. If you register with the Application context, you receive broadcasts as long as the app is running.
那么即使您的应用不是 运行ning,您如何收听广播事件?
由于广播接收器不可靠,我想不出任何可行的解决方案,但您可以考虑 运行一直使用后台服务。这也太过分了。检查这个答案 Making BroadcastReceiver work in the background in API 26 or above 还有这个来自commonsware Android O and the Implicit Broadcast Ban
我能想到的另一个解决方案,如果 API 级别低于 26,它应该按原样工作,但对于 api 26 及以上,在你的应用程序 onResume 中,你将检查每次可用应用程序和您当前的应用程序列表。然后删除多余的应用程序。
接受接收器基本上无法(不再)以对我们的情况有用的方式工作的现实,所使用的解决方案是 1) 在 onResume 中嵌入一些检查代码以查看两者之间是否存在差异设备现实和应用程序对现实的理解; 2)使用一种接收器类型(ACTION_PACKAGE_FULLY_REMOVED)至少跟上那些(我们案例中的主要问题)。
要影响新的接收器类型,您必须在清单中注册接收器,如下所示:
<receiver
android:name=".receiver.FullyRemovedBroadcastReceiver">
<!-- <android:exported="true"> -->
<intent-filter>
<action android:name="android.intent.action.PACKAGE_FULLY_REMOVED"/>
<data android:scheme="package"/>
</intent-filter>
</receiver>
其余部分(广播接收器的创建class):
class FullyRemovedBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent == null || context == null) return
when (intent.action) {
ACTION_PACKAGE_FULLY_REMOVED ->
enqueueWork(context, getIntent(context, ACTION_PACKAGE_FULLY_REMOVED, intent.data?.schemeSpecificPart))
}
}
private fun getIntent(context: Context?, action: String, packageName: String?) =
Intent(context, AppChangeJobIntentService::class.java).apply {
this.action = action
putExtra(EXTRA_PACKAGE_NAME, packageName)
putExtra(EXTRA_DATA_REMOVED, true)
putExtra(EXTRA_REPLACING, false)
}
private fun enqueueWork(context: Context, intent: Intent) {
JobIntentService.enqueueWork(
context,
AppChangeJobIntentService::class.java,
APP_CHANGE_JOB_ID,
intent)
}
}