Tips·检测应用程序被卸载

前言

我们知道广播ACTION_PACKAGE_REMOVED可以监听应用程序卸载,但不幸的是这个意图被卸载的程序是不可知的,所以无法监听到自己的程序被卸载。

正文

当用户操作Settings -> Manage Apps -> Selects a particular application时,会收到一条包含其应用程序包名作为extras的广播消息 android.intent.action.QUERY_PACKAGE_RESTART

当我们点击卸载按钮时,会打开卸载确认界面com.android.packageinstaller.UninstallerActivity

卸载确认界面

我们应监听android.intent.action.QUERY_PACKAGE_RESTART广播,如果发现广播中的extras中的包名与应用程序匹配,我们就启动一个后台线程,并利用ActivityManager持续监控前台运行的Activity

当后台线程发现前台的活动是com.android.packageinstaller.UninstallerActivity,这便确认用户是希望卸载我们的APP。在用户确认卸载前的时刻,我们可以做一些事件(比如:弹出对话框挽留用户等),但是需要确保允许用户卸载APP。

代码实现

1、添加权限

<uses-permission android:name="android.permission.GET_TASKS"/>

2、注册广播接收器

  <receiver android:name=".UninstallIntentReceiver">
      <intent-filter android:priority="0">
            <action android:name="android.intent.action.QUERY_PACKAGE_RESTART" />
            <data android:scheme="package" />
      </intent-filter>
 </receiver>
public class UninstallIntentReceiver extends BroadcastReceiver{

    @Override
    public void onReceive(Context context, Intent intent) {
        // fetching package names from extras
        String[] packageNames = intent.getStringArrayExtra("android.intent.extra.PACKAGES"); 

        if(packageNames!=null){
            for(String packageName: packageNames){
                if(packageName!=null && packageName.equals("YOUR_APPLICATION_PACKAGE_NAME")){
                    // User has selected our application under the Manage Apps settings
                    // now initiating background thread to watch for activity
                    new ListenActivities(context).start();

                }
            }
        }
    }

}

3、后台监视器线程 监听前台活动变化

class ListenActivities extends Thread{
    boolean exit = false;
    ActivityManager am = null;
    Context context = null;

    public ListenActivities(Context con){
        context = con;
        am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    }

    public void run(){

        Looper.prepare();

        while(!exit){

             // get the info from the currently running task
             List< ActivityManager.RunningTaskInfo > taskInfo = am.getRunningTasks(MAX_PRIORITY); 

             String activityName = taskInfo.get(0).topActivity.getClassName();


             Log.d("topActivity", "CURRENT Activity ::"
                     + activityName);

             if (activityName.equals("com.android.packageinstaller.UninstallerActivity")) {
                // User has clicked on the Uninstall button under the Manage Apps settings

                 //do whatever pre-uninstallation task you want to perform here
                 // show dialogue or start another activity or database operations etc..etc..

                // context.startActivity(new Intent(context, MyPreUninstallationMsgActivity.class).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
                 exit = true;
                 Toast.makeText(context, "Done with preuninstallation tasks... Exiting Now", Toast.LENGTH_SHORT).show();
            } else if(activityName.equals("com.android.settings.ManageApplications")) {
                // back button was pressed and the user has been taken back to Manage Applications window
                          // we should close the activity monitoring now
                exit=true;
            }
        }
        Looper.loop();
    }
}

已知的限制

当用户点击管理应用程序的设置下的卸载按钮,我们将执行我们的预卸载任务然后要求用户确认窗口,但是用户可以确认卸载或可以取消操作,上述实现并没有考虑到用户点击取消卸载按钮的逻辑。

我希望这个方法可以帮到你,这是我目前知道的唯一不需要Root权限能够在卸载前拦截的方法。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Android源码框架分析

SharePreference原理及跨进程数据共享的问题

SharedPreferences是Android提供的数据持久化的一种手段,适合单进程、小批量的数据存储与访问。为什么这么说呢?因为SharedPrefere...

18860
来自专栏非著名程序员

Android 事件处理探险

Android提供了两套事件处理机制:基于监听的事件处理;基于回调的事件处理 1.基于监听的事件处理 Android的事件处理是一种委派式事件处理方式(事件源将...

19870
来自专栏Android先生

Android小技巧: 这里涵盖了所有实现 “一键退出 App” 的方法

即 需要2个步骤 才可 完成 一键退出 App 需求。下面,我将根据这两个步骤进行功能实现讲解。

8620
来自专栏Android开发实战

Android 组件化 —— 路由设计最佳实践

Android原生已经支持AndroidManifest去管理App跳转,为什么要有路由库,这可能是大部分人接触到Android各种Router库不太明白的地方...

21920
来自专栏Android研究院

Android组件化专题 - 路由框架进阶模块间的业务通信

上一篇文章,讲解了路由框架实现的原理,并实现了基本的路由框架 页面路由的跳转 Android组件化专题 - 路由框架原理。

15520
来自专栏7号代码

Android应用界面开发——BroadcastReceiver(实现基于Service的音乐播放器)

BroadcastReceiver用于接收程序(开发者开发的程序和系统程序)发出的Broadcast Intent,程序启动BroadcastReceiver需...

14420
来自专栏Android小菜鸡

Android6.0权限控制

  随着android6.0的更新,最大的变化莫过于新的权限控制规则。以前可以直接通过AndroidManifest配置需要的权限。而更新后,为了保证用户隐私的...

7710
来自专栏Android中高级开发

Android开发之漫漫长途 番外篇——内存泄漏分析与解决

该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列。该系列引用了《Android开发艺术探索...

8820
来自专栏技术小黑屋

解密:Android设置默认程序

Android作为一个伟大的系统,自然提供了设置默认打开程序的实现.在这篇文章中,我会介绍如何在Android系统中设置默认的程序. 在设置默认程序之前,无非有...

26620
来自专栏美团技术团队

Toast与Snackbar的那点事

48960

扫码关注云+社区

领取腾讯云代金券