如何使用WorkManager实现应用关闭时运行后台服务?
解决WorkManager PeriodicWorkRequest在应用关闭后停止运行的问题
看起来你遇到的核心问题是Worker在异步操作完成前就被系统回收了,再加上部分厂商系统的电池优化限制,导致应用关闭后下载任务中断。下面一步步给你解决思路和代码修改方案:
1. 修复Worker的异步操作问题(最关键)
你的doWork()方法里启动AndroidNetworking的异步下载后,立刻返回了WorkerResult.SUCCESS——这会让WorkManager认为任务已经完成,直接回收Worker实例。一旦应用被关闭,这个Worker进程就会被系统杀掉,下载自然就停了。
解决方法是用CountDownLatch阻塞doWork()方法,直到异步下载完成或出错再返回结果:
@Override public WorkerResult doWork() { context = getApplicationContext(); Data data = getInputData(); String url = data.getString("url", ""); String mainName = "renaldi"; String mimeType = url.substring(url.length()-4); String namePath = mainName + "Files-"+changeFormat(new Date())+mimeType; notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); createNotification(); AndroidNetworking.initialize(context); // 用CountDownLatch等待异步操作完成 final CountDownLatch latch = new CountDownLatch(1); final WorkerResult[] taskResult = {WorkerResult.SUCCESS}; AndroidNetworking.download(url, context.getExternalFilesDir(DIRECTORY_DOWNLOADS)+"/video/" , namePath) .setTag("download") .setPriority(Priority.HIGH) .build() .setDownloadProgressListener(new DownloadProgressListener() { @Override public void onProgress(long bytesDownloaded, long totalBytes) { final int dl_progress = (int) ((bytesDownloaded * 100l) / totalBytes); updateNotification(dl_progress); } }) .startDownload(new DownloadListener() { @Override public void onDownloadComplete() { onDownloadCompleted(); latch.countDown(); // 下载完成,释放 latch } @Override public void onError(ANError error) { taskResult[0] = WorkerResult.FAILURE; // 标记任务失败 latch.countDown(); } }); try { latch.await(); // 阻塞当前线程,直到下载完成 } catch (InterruptedException e) { e.printStackTrace(); taskResult[0] = WorkerResult.RETRY; // 中断时标记重试 } return taskResult[0]; }
2. 处理系统电池优化限制
国内很多厂商(小米、华为、OPPO等)的定制系统有严格的后台管控,即使WorkManager设计为后台运行,也可能被电池优化策略拦截。
方案一:引导用户手动关闭电池优化
你可以在应用设置里添加一个入口,引导用户关闭当前应用的电池优化:
// 检查是否已获得电池优化豁免权 PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); boolean isIgnored = powerManager.isIgnoringBatteryOptimizations(context.getPackageName()); if (!isIgnored) { Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); intent.setData(Uri.parse("package:" + context.getPackageName())); // 注意:要在Activity中启动这个Intent,不能在Worker里直接启动 startActivity(intent); }
同时需要在AndroidManifest.xml中添加权限:
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
方案二:申请后台运行权限
对于Android 10及以上版本,你还可以申请后台运行权限,但这个权限需要向用户说明用途,否则容易被拒绝。在AndroidManifest.xml中添加:
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
3. 优化WorkManager配置与初始化
- 建议在
Application类中初始化AndroidNetworking,避免在每个Worker实例中重复初始化:
public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); AndroidNetworking.initialize(this); } }
然后在AndroidManifest.xml中注册这个Application类:
<application android:name=".MyApplication" ...> <!-- 其他配置 --> </application>
- 你的
PeriodicWorkRequest周期设置为12小时是符合要求的(WorkManager要求周期最短15分钟),无需调整,但要注意系统会根据负载情况弹性调整执行时间,不是严格准时触发。
4. 验证任务状态
你可以通过WorkManager的getWorkInfoByIdLiveData方法监听任务状态,确认任务是否在后台正常运行:
WorkManager.getInstance(context) .getWorkInfoByIdLiveData(request.getId()) .observe(this, workInfo -> { if (workInfo != null) { WorkInfo.State state = workInfo.getState(); Log.d("WorkManager", "任务状态:" + state.name()); } });
这样修改后,你的PeriodicWorkRequest就能在应用关闭后继续执行下载任务了,只要系统没有强制杀掉进程且网络条件满足。
内容的提问来源于stack exchange,提问作者Renaldi
相关产品推荐
相关产品推荐

