Android Download Manager请求自动启停及界面卡顿问题求助
批量PDF下载问题解决方案
问题梳理
你用Service结合DownloadManager做批量PDF下载,每批6个任务,完成一批再启下一批,但现在遇到两个问题:
- 下载时打开已完成的PDF,界面卡顿,要点击2-3次才响应
- 下载会自动停止,1-2分钟后又自动恢复
问题原因及修复方案
问题1:界面卡顿
核心原因
- AsyncTask执行方式错误:直接调用
downloadAllExecutor.doInBackground()会把下载初始化逻辑跑在主线程(Service的onStartCommand本身在主线程),文件数量多的时候,遍历、文件存在性判断这些操作会阻塞主线程,导致UI卡顿。 - 数据填充逻辑缺失:新建空的
toDownloadList后,没把从DocumentHelper获取的list数据存入,导致hashMap为空,后续下载逻辑混乱,间接引发UI线程异常。
修复步骤
修正AsyncTask启动方式:
把直接调用doInBackground()改成execute(),让AsyncTask在后台线程正确执行:// 原错误代码 // downloadAllExecutor.doInBackground(); // 修改为 downloadAllExecutor.execute();补全数据填充逻辑:
获取到list后,把数据存入toDownloadList,否则后续的positionList和documentList都是空的:if(isFromFragment){ list = new ArrayList<>(DocumentHelper.getInstance(this).getDocumentHashmap().values()); } else{ list = new ArrayList<>(DocumentHelper.getInstance(this).getDocumentsOfParticularGroup().getValues()); } // 新增:将list数据存入toDownloadList for (int i = 0; i < list.size(); i++) { toDownloadList.put(i, list.get(i)); } hashMap = toDownloadList; positionList = new ArrayList<>(hashMap.keySet()); documentList = new ArrayList<>(hashMap.values());确保IO操作在后台执行:
文件存在性判断属于IO操作,要保证它始终在后台线程执行(避免在主线程调用downloadFile方法)。
问题2:下载自动停止又恢复
核心原因
- 分批逻辑未真正实现:只发送了第一批下载任务,但没有监听任务完成状态,后续批次无法启动;系统会限制后台下载任务数量,队列拥堵后会暂停,资源释放后又恢复。
- 后台Service易被系统回收:Android 8.0+对后台Service限制严格,你的Service容易被系统杀死,之后系统根据
START_REDELIVER_INTENT重启Service,导致下载看起来“停止后恢复”。 - 变量逻辑混乱:
downloadCount、itemSentCount等变量没有正确更新,无法触发下一批任务。
修复步骤
实现下载完成监听,真正分批执行:
注册DownloadManager的下载完成广播,每完成一批任务再启动下一批:private BroadcastReceiver downloadCompleteReceiver; private DownloadManager downloadManager; private int currentBatchSize = 6; // 按需求设置每批数量 private int totalDownloaded = 0; private int totalToDownload = 0; private int itemSentCount = 0; @Override public void onCreate() { super.onCreate(); downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE); // 注册下载完成广播 downloadCompleteReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { long downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1); if (downloadId != -1) { totalDownloaded++; // 检查当前批次是否完成,或所有任务是否完成 if (totalDownloaded % currentBatchSize == 0 || totalDownloaded == totalToDownload) { sendNextBatch(); } } } }; registerReceiver(downloadCompleteReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)); } // 初始化下载任务,发送第一批 private void initDownloadBatch() { totalToDownload = documentList.size(); totalDownloaded = 0; itemSentCount = 0; sendNextBatch(); } // 发送下一批任务 private void sendNextBatch() { if (itemSentCount >= totalToDownload) { // 所有任务完成,停止前台服务并销毁Service stopForeground(true); stopSelf(); return; } int remaining = totalToDownload - itemSentCount; int batchSize = Math.min(currentBatchSize, remaining); for (int i = 0; i < batchSize; i++) { int index = itemSentCount + i; initiateDownload(documentList.get(index), positionList.get(index), totalToDownload); } itemSentCount += batchSize; }修改
DownloadAllExecutor的doInBackground方法,调用初始化逻辑:private class DownloadAllExecutor extends AsyncTask<Void, Void,Void> { @Override protected Void doInBackground(Void...voids) { initDownloadBatch(); return null; } }将Service改为前台服务:
避免系统杀死后台Service,添加前台通知:@Override public int onStartCommand(Intent intent, int flags, int startId) { if(intent != null){ // ... 原有数据处理逻辑 ... DownloadAllExecutor downloadAllExecutor = new DownloadAllExecutor(); downloadAllExecutor.execute(); } // 启动前台服务 createForegroundNotification(); return START_STICKY; // 或保留START_REDELIVER_INTENT,按需选择 } private void createForegroundNotification() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel("download_channel", "批量下载", NotificationManager.IMPORTANCE_LOW); NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); manager.createNotificationChannel(channel); } Notification notification = new NotificationCompat.Builder(this, "download_channel") .setContentTitle("PDF批量下载") .setContentText("已完成 " + totalDownloaded + "/" + totalToDownload) .setSmallIcon(R.drawable.ic_download) // 替换成你的应用图标 .setPriority(NotificationCompat.PRIORITY_LOW) .build(); startForeground(1, notification); }优化DownloadManager请求配置:
允许后台下载,避免系统因流量限制暂停:// 在downloadFile方法的Request中添加 request.setAllowedOverMetered(true); // 允许计量网络下载 request.setAllowedOverRoaming(true); // 允许漫游下载(按需开启)清理广播接收器:
在Service销毁时注销广播,避免内存泄漏:@Override public void onDestroy() { super.onDestroy(); if (downloadCompleteReceiver != null) { unregisterReceiver(downloadCompleteReceiver); } }
内容的提问来源于stack exchange,提问作者Prashant Kumar
相关产品推荐
相关产品推荐

