You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何检测Android应用卸载状态并向App Server上报该信息

Android应用卸载状态检测与服务器上报方案

Hey there, let's walk through how to detect when your Android app is being uninstalled (or has already been uninstalled) and report that status to your backend server. I've tested a few approaches over the years, here are the most reliable ones:

1. 利用独立进程监听包变化(检测卸载中状态)

The biggest challenge with detecting your own app's uninstall is that Android kills your app's main process immediately when the uninstall starts. To get around this, you can run a separate background process that monitors package changes—this process might stay alive just long enough to send the uninstall report.

实现步骤:

  • Create a ContentObserver in a separate process to watch changes to the system package database:
public class UninstallMonitorObserver extends ContentObserver {
    private final Context mContext;
    private final String mTargetPackage;

    public UninstallMonitorObserver(Handler handler, Context context, String packageName) {
        super(handler);
        mContext = context;
        mTargetPackage = packageName;
    }

    @Override
    public void onChange(boolean selfChange) {
        super.onChange(selfChange);
        // 检查当前应用是否已被卸载
        if (!isAppInstalled(mContext, mTargetPackage)) {
            // 发送卸载上报到服务器(必须用同步请求!)
            sendUninstallReportToServer();
            // 注销观察者清理资源
            mContext.getContentResolver().unregisterContentObserver(this);
        }
    }

    private boolean isAppInstalled(Context context, String packageName) {
        PackageManager pm = context.getPackageManager();
        try {
            pm.getPackageInfo(packageName, PackageManager.GET_META_DATA);
            return true;
        } catch (PackageManager.NameNotFoundException e) {
            return false;
        }
    }

    private void sendUninstallReportToServer() {
        // 使用OkHttp或Volley发起同步请求(异步请求可能来不及完成进程就被杀死)
        OkHttpClient client = new OkHttpClient();
        RequestBody body = RequestBody.create(
            MediaType.parse("application/json"),
            "{\"packageName\": \"" + mTargetPackage + "\"}"
        );
        Request request = new Request.Builder()
                .url("https://your-backend-url.com/report-uninstall")
                .post(body)
                .build();
        try {
            client.newCall(request).execute();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • AndroidManifest.xml中注册一个运行在独立进程的服务:
<service
    android:name=".UnmonitorMonitorService"
    android:process=":uninstall_monitor" /> <!-- 指定独立进程 -->
  • 在服务的onCreate()中初始化观察者:
public class UnmonitorMonitorService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        // 注册内容观察者
        getContentResolver().registerContentObserver(
            Uri.parse("content://packages"),
            true,
            new UninstallMonitorObserver(new Handler(), this, getPackageName())
        );
    }
}
  • 在应用启动时启动该服务(比如在Application类的onCreate()中):
startService(new Intent(this, UnmonitorMonitorService.class));

注意: 这种方法无法100%保证成功——Android可能会快速杀死独立进程,但这是捕捉卸载中状态的最优方案。

2. 周期性存活上报(确认已卸载状态)

更可靠的确认卸载方式是让应用定期向服务器发送"我还活着"的信号。如果服务器在设定阈值(比如24小时)内没有收到信号,就可以标记该应用为已卸载。

实现步骤:

  • 使用WorkManager调度周期性后台任务(它比旧API更适配打盹模式和电池优化):
// 调度每12小时执行一次的周期性任务
PeriodicWorkRequest keepAliveWork = new PeriodicWorkRequest.Builder(
    KeepAliveReportWorker.class,
    12, TimeUnit.HOURS,
    1, TimeUnit.HOURS) // 调度的弹性窗口
    .build();

// 入队任务(用唯一名称避免重复)
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
    "app-alive-report",
    ExistingPeriodicWorkPolicy.KEEP,
    keepAliveWork
);
  • 创建Worker类发送存活上报:
public class KeepAliveReportWorker extends Worker {
    public KeepAliveReportWorker(@NonNull Context context, @NonNull WorkerParameters params) {
        super(context, params);
    }

    @NonNull
    @Override
    public Result doWork() {
        sendAliveReport();
        return Result.success();
    }

    private void sendAliveReport() {
        OkHttpClient client = new OkHttpClient();
        RequestBody body = RequestBody.create(
            MediaType.parse("application/json"),
            "{\"packageName\": \"" + getApplicationContext().getPackageName() + "\"}"
        );
        Request request = new Request.Builder()
                .url("https://your-backend-url.com/report-alive")
                .post(body)
                .build();
        try {
            client.newCall(request).execute();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

服务器端逻辑: 记录每个用户最后一次发送存活上报的时间戳。如果距离上次上报超过阈值,就标记应用为已卸载。这种方法比直接捕捉卸载事件可靠得多。

3. Android 11+ 关联应用监听(可选)

如果用户设备上安装了你的品牌旗下其他应用,可以在该应用中注册ACTION_PACKAGE_FULLY_REMOVED广播接收器。这个广播会在应用完全卸载后发送,你的关联应用可以将该事件上报给服务器。

局限性: 仅当用户安装了你的其他应用时生效,因此只能作为补充方案,而非主要方案。

关键注意事项

  • 同步网络请求: 捕捉卸载事件时一定要用同步请求——异步请求很可能在进程被杀死前无法完成。
  • 权限: Android 11+ 上需要QUERY_ALL_PACKAGES权限来检查应用是否已安装(在清单中声明,并在Google Play上说明权限用途)。
  • 电池优化: 引导用户将应用加入电池优化白名单,确保周期性存活上报不会被阻断。

内容的提问来源于stack exchange,提问作者vikram thakur

火山引擎 最新活动