接收包安装和卸载事件

Posted

技术标签:

【中文标题】接收包安装和卸载事件【英文标题】:Receiving package install and uninstall events 【发布时间】:2011-11-20 04:23:29 【问题描述】:

我正在尝试检测何时安装了新应用,但前提是我的应用正在运行。我设法通过制作一个 BroadcastReceiver 并在 androidManifest 文件中激活它来检测应用程序的安装,但即使我的应用程序已关闭,这也会检测到。 所以这就是为什么我需要手动激活和停用广播接收器。为此,我有以下代码:

br = new BroadcastReceiver() 

    @Override
    public void onReceive(Context context, Intent intent) 
        // TODO Auto-generated method stub
        Log.i("Enter", "Enters here");
        Toast.makeText(context, "App Installed!!!!.", Toast.LENGTH_LONG).show();
    
;
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
intentFilter.addAction(Intent.ACTION_PACKAGE_INSTALL);
registerReceiver(br, intentFilter);

安装新应用时,这应该会让人敬佩。但遗憾的是它没有。它不进入 onReceive 方法。任何帮助表示赞赏。

【问题讨论】:

你在哪里注册你的接收器? reciver注册在activity的onCreate方法中。 在显示 toast 时尝试使用您的应用程序上下文 【参考方案1】:

我尝试在清单文件或 java 代码中注册 BroadcastReceiver。但这两种方法都未能触发onReceive()方法。 在谷歌搜索这个问题之后,我从 SO 中的另一个线程中找到了这两种方法的解决方案: Android Notification App

在清单文件中(这种方法自 API 26 (Android 8) 起不再适用,它会导致早期 Android 版本出现性能问题):

<receiver android:name=".YourReceiver">
    <intent-filter>
        <action android:name="android.intent.action.PACKAGE_INSTALL" />
        <action android:name="android.intent.action.PACKAGE_ADDED" />
        <data android:scheme="package"/>
    </intent-filter>
</receiver>

在java代码中:

IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
intentFilter.addAction(Intent.ACTION_PACKAGE_INSTALL);
intentFilter.addDataScheme("package");
registerReceiver(br, intentFilter);

这应该适合你。

【讨论】:

只添加intentFilter.addDataScheme("package");我的代码解决了一切。谢谢。 @HirenDabhi,只需简单地添加相应的intent filter的action:&lt;action android:name="android.intent.action.PACKAGE_REMOVED" /&gt; @HirenDabhi 是的,我也发现了。我认为这是因为当您在自己的应用中注册并卸载应用时,已注册的 BroadcastReceiver 在应用卸载之前已被卸载,因此该 BroadcastReceiver 不会收到它自己的卸载事件。 我认为不需要“ACTION_PACKAGE_INSTALL”,如文档所述(此处:developer.android.com/reference/android/content/…):“此常量在 API 级别 14 中已弃用。此常量从未使用过。”跨度> 对了,收到的包URI(intent.getData().toString())是这样的——package:com.rovio.angrybirds【参考方案2】:

其他答案指出收听ACTION_PACKAGE_ADDEDACTION_PACKAGE_REPLACED 广播。这适用于 Android 7.1 及更低版本。在 Android 8.0+ 上,您无法在清单中注册这些广播。

相反,您需要定期call getChangedPackages() on PackageManager,例如通过定期JobScheduler 作业。这不会为您提供实时结果,但在 Android 8.0+ 上不再提供实时结果。

【讨论】:

谢谢。他们为什么要做出这样的改变? @KarsenGauss:我有 a blog post 涵盖了这一点。 你总是在答案和 cmets 中弹出,在我开发第一个应用程序时,你帮了我很多。所以谢谢你!但是你让我对这个感到困惑。在您的博文中,您建议使用LocalBroadcastManager。但根据this answer LocalBroadcastManager 在版本1.1.0 中被贬低。我基本上想强制更新到较新的版本。如果安装被取消,或者我再次运行安装意图,我要么杀死应用程序。今天我将如何做到这一点(也支持 8.0+)?谢谢。 @Alex:恕我直言,这些广播对于您正在尝试做的事情并不是那么有用。当您的应用启动时,请查看您的版本是否是最新的。如果不是,则引导用户安装最新版本,拒绝执行任何其他操作。 @CommonsWare 哇,感谢您的快速回复。这基本上就是我现在正在做的事情。我已经在引导用户进行安装,但用户可以取消安装程序。抱歉,如果不清楚。这就是为什么我正在考虑处理包安装程序的“取消”按钮。我是否应该循环检查新版本,直到安装最新版本?或者是否有类似处理 Android 8.0+ 中的取消按钮的东西?你有什么建议?我现在的主要目标设备是 API 24。但我想现在做一个面向未来的解决方案比每隔几年更换一次更好。谢谢!【参考方案3】:

只是为了补充上面黄的回答,这里是如何获取新安装的应用程序的包名:

public class YourReceiver extends BroadcastReceiver 
    @Override
    public void onReceive(Context context, Intent intent) 
        String packageName = intent.getData().getEncodedSchemeSpecificPart();
    

【讨论】:

【参考方案4】:

此代码适用于 REMOVED_APPLICATION 。

使用下面的代码,你不需要使用清单。只需在你的Java类中写这段代码。

 BroadcastReceiver  uninstallApplication = new BroadcastReceiver() 

      @Override
      public void onReceive(Context context, Intent intent) 

        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) 
          String packageName = Objects.requireNonNull(intent.getData()).getEncodedSchemeSpecificPart();

       Toast.makeText(context, "USER UNINSTALL : " + packageName, Toast.LENGTH_SHORT).show();




        
      
    ;
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
    intentFilter.addDataScheme("package");
    registerReceiver(uninstallApplication, intentFilter);

【讨论】:

【参考方案5】:

此方案不使用任何类型的ReceiverIntent,仅使用 Kotlin 和协程,因此不受任何 android 版本的限制。

    我使用PackageManager创建了一个方法来返回我的应用程序是否已安装。
private fun isAppInstalled(): Boolean 
        return try 
            val appPackageInfo = packageManager.getPackageInfo(packageName, 0)
            true
         catch (e: PackageManager.NameNotFoundException) 
            false
        
    
    然后我创建了另一个方法,该方法返回一个带有布尔值的 Deferred,仅在我的应用安装或达到超时限制时完成。
override suspend fun returnWhenAppIsInstalledAsync(): Deferred<Boolean> = ioscope.async 
        while (isActive) 
            var delayCount = 0
            if(isAppInstalled()) 
                return@async true
             else 
                delay(1000)
                delayCount++
                if(delayCount == 30) return@async false
            
        
        return@async false
    
    所以我开始等待它在会话提交后返回
...
session.commit(intentSender)
val wasAppInstalled = viewModel.returnWhenAppIsInstalledAsync().await()
if(wasAppInstalled) 
  // Here you can handle any logic

【讨论】:

以上是关于接收包安装和卸载事件的主要内容,如果未能解决你的问题,请参考以下文章

安装和卸载软件包

应用安装和卸载

如何卸载软件包?

adb命令安装、卸载、 获取包名

Linux软件包安装和卸载

卸载时获取包(应用程序)版本名称