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
相关产品推荐
相关产品推荐

