You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android高版本下ActivityRecognition从IntentService迁移WorkManager实现后台运行

迁移改造方案


前提说明

Android 8.0+对后台服务做了严格限制,IntentService已被官方弃用,你的场景属于Activity识别回调触发的后台任务,我们用广播+WorkManager组合实现,保证应用关闭后任务依然可以正常执行。


改造步骤

1. 替换原有PendingIntent触发逻辑

原来直接触发Service的PendingIntent改为触发广播接收器,中转后唤醒WorkManager处理任务:

// 替换原启动代码里的PendingIntent.getService逻辑
Intent activityRecIntent = new Intent(this, ActivityRecReceiver.class);
// Android12+必须指定FLAG_IMMUTABLE/MUTABLE,这里用FLAG_IMMUTABLE即可
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, activityRecIntent, 
        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
activityRecognitionClient.requestActivityUpdates(timeInLong, pendingIntent);
2. 实现广播接收器接收Activity识别结果

在AndroidManifest.xml里先注册广播:

<receiver android:name=".ActivityRecReceiver"
    android:exported="false"/>

实现广播逻辑:

public class ActivityRecReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 把识别结果封装为Worker的输入参数
        Data inputData = new Data.Builder()
                .putParcelable("activity_rec_result", intent)
                .build();
        // 构建一次性加急任务,保证立刻执行
        OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(ActivityRecWorker.class)
                .setInputData(inputData)
                .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
                .build();
        WorkManager.getInstance(context).enqueue(workRequest);
    }
}
3. 把原IntentService逻辑迁移到Worker类

原IntentService里的业务逻辑完全复用,仅把Timer定时器替换为WorkManager周期性任务,避免任务超时被系统杀死:

public class ActivityRecWorker extends Worker {
    public static final String TAG = "###RECOGNISED WORKER###";
    private static int detectedActivity;
    private static PeriodicWorkRequest currentLocationWork = null;

    public ActivityRecWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
    }

    @NonNull
    @Override
    public Result doWork() {
        Intent intent = getInputData().getParcelable("activity_rec_result");
        if (intent == null || !ActivityRecognitionResult.hasResult(intent)) {
            return Result.success();
        }
        ActivityRecognitionResult result = ActivityRecognitionResult.extractResult(intent);
        handleDetectedActivities(result.getProbableActivities(), getApplicationContext());
        return Result.success();
    }

    private void handleDetectedActivities(List<DetectedActivity> probableActivities, Context mContext) {
        // 这里直接复用你原来的handleDetectedActivities业务逻辑,仅替换setTimer调用
        for (DetectedActivity activity : probableActivities) {
            switch (activity.getType()) {
                case DetectedActivity.IN_VEHICLE:
                    Log.d(TAG, "In Vehicle " + activity.getConfidence());
                    if (activity.getConfidence() >= 75 && activity.getType() != detectedActivity) {
                        // 原通知逻辑直接复用,注意Android8+必须指定通知渠道
                        NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext, "你自己的通知渠道ID");
                        builder.setContentText("In Vehicle");
                        builder.setSmallIcon(R.drawable.demo_x);
                        builder.setContentTitle("demo");
                        NotificationManagerCompat.from(mContext).notify(0, builder.build());
                        // 替换原setTimer(10)
                        setPeriodicLocationTask(10);
                        detectedActivity = activity.getType();
                    }
                    break;
                // 剩下的ON_BICYCLE、ON_FOOT、RUNNING等分支逻辑和你原来的代码完全一致,仅替换setTimer调用即可
                case DetectedActivity.STILL:
                    if (activity.getConfidence() >=75 && activity.getType() != detectedActivity) {
                        // 原通知、写文件逻辑直接复用
                        setPeriodicLocationTask(60*5);
                        detectedActivity = activity.getType();
                    }
                    break;
                // 其余分支省略,和原有逻辑完全一致
            }
        }
    }

    // 用WorkManager周期性任务替换原Timer实现
    private void setPeriodicLocationTask(int minutes) {
        Context context = getApplicationContext();
        // 取消之前正在运行的定时定位任务
        if (currentLocationWork != null) {
            WorkManager.getInstance(context).cancelWorkById(currentLocationWork.getId());
        }
        // 新建指定间隔的周期性定位任务
        currentLocationWork = new PeriodicWorkRequest.Builder(LocationUpdateWorker.class, minutes, TimeUnit.MINUTES)
                .build();
        WorkManager.getInstance(context).enqueue(currentLocationWork);
    }

    // 定位任务单独封装为Worker
    public static class LocationUpdateWorker extends Worker {
        public LocationUpdateWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
            super(context, workerParams);
        }

        @NonNull
        @Override
        public Result doWork() {
            Log.d(TAG, "AsyncTask Start Here For Location Update");
            // 把原来Timer里的GetLocation逻辑放到这里执行
            new GetLocation(getApplicationContext(),2);
            return Result.success();
        }
    }
}

注意事项

  • Android 8.0+发送通知必须提前创建通知渠道,否则通知无法正常弹出
  • Android 10+需要申请ACCESS_BACKGROUND_LOCATION后台定位权限,Android12+需要申请ACTIVITY_RECOGNITION行为识别权限,需提前做好动态权限申请
  • 国产ROM普遍有后台进程限制,如需更高保活率可以引导用户把应用加入后台运行白名单

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 03:24:01