android - 如何在前台服务中将多个任务排队,以便它们一项一项地执行?

标签 android android-asynctask android-service android-intentservice

public class CopyService extends Service {

private List<CustomFile> taskList;
private AsyncTask fileTask;

@Override
public void onCreate() {
    super.onCreate();
    taskList = new ArrayList<>();
    fileTask = new fileTaskAsync();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {

    String filePath = intent.getStringExtra("filePath");
    String fileType = intent.getStringExtra("fileType");
    String taskType = intent.getStringExtra("taskType");
    String fileName = intent.getStringExtra("fileName");

    CustomFile customFile = new CustomFile();
    customFile.filePath = filePath;
    customFile.fileType = fileType;
    customFile.taskType = taskType;
    customFile.fileName = fileName;
    taskList.add(customFile);

    Notification notification = getNotification();

    startForeground(787, notification);

    if (fileTask.getStatus() != AsyncTask.Status.RUNNING) {

        CustomFile current = taskList.get(0);
        taskList.remove(current);

        fileTask = new fileTaskAsync().execute(current);
    }

    stopSelf();
    return START_NOT_STICKY;
}

@Nullable
@Override
public IBinder onBind(Intent intent) {
    return null;
}

private class fileTaskAsync extends AsyncTask<CustomFile, Void, String> {

    @Override
    protected String doInBackground(CustomFile... customFiles) {

        CustomFile customFile = customFiles[0];

        FileUtils.doFileTask(customFile.filePath, customFile.fileType,
                customFile.taskType);

        return customFile.fileName;
    }

    @Override
    protected void onPostExecute(String name) {
        sendResult(name);

        if (!taskList.isEmpty()) {
            CustomFile newCurrent = taskList.get(0);
            taskList.remove(newCurrent);
            fileTask = new fileTaskAsync().execute(newCurrent);
        }
    }
}

private void sendResult(String name) {
    Intent intent = new Intent("taskStatus");
    intent.putExtra("taskName", name);
    LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}

}

我需要在一个服务中一一执行多个任务。任务是复制或移动本地文件。假设用户正在复制一个大文件,并且他想要复制或移动其他文件。我需要将后续任务逐一排队执行。

目前,我正在服务内创建一个列表并运行异步任务。在 onPostExecute 中,我检查列表中的剩余任务并从那里再次启动异步任务。如代码所示。

但是,我担心内存泄漏。而且我对编程非常陌生,所以我不知道在这种情况下的最佳实践是什么。

我无法使用 IntentService,因为我希望即使用户点击主页按钮打开其他应用程序,任务也能继续。

最佳答案

正如我在评论中所说,我认为你的解决方案是合理的。前台服务非常适合需要立即执行的长时间运行的工作,并且根据您的描述,您的文件复制任务符合该条件。

也就是说,我不认为 AsyncTask 适合解决您的问题。当您需要在主线程上执行一些快速工作(最多几百毫秒)时,最好部署 AsyncTasks,而您的复制任务可能需要几秒钟。

由于您有多个任务需要完成,并且这些任务彼此之间并不直接依赖,因此我建议您使用线程池来执行这项工作。为此,您可以使用 ExecutorService:

public class CopyService extends Service {

private final Deque<CustomFile> tasks = new ArrayDeque<>();
private final Deque<Future<?>> futures = new LinkedBlockingDequeue<>();
private final ExecutorService executor = Executors.newCachedThreadPool();

@Override
public int onStartCommand(Intent intent, int flags, int startId) {

    //May as well add a factory method to your CustomFile that creates one from an Intent
    CustomFile customFile = CustomFile.fromIntent(intent);
    tasks.offer(customFile);

    //...Add any other tasks to this queue...

    Notification notification = getNotification();

    startForeground(787, notification);

    for(CustomFile file : tasks) {
       final Future<?> future = executor.submit(new Runnable() {
           @Override
           public void run() {
               final CustomFile file = tasks.poll();
               //Ddo work with the file...
               LocalBroadcastManager.getInstance(CopyService.this).sendBroadcast(...);
               //Check to see whether we've now executed all tasks. If we have, kill the Service.
               if(tasks.isEmpty()) stopSelf();
           }
       });
       futures.offer(future);
    }
    return START_NOT_STICKY;
}

@Override
public void onDestroy() {
    super.onDestroy();
    //Clear pending and active work if the Service is being shutdown
    //You may want to think about whether you want to reschedule any work here too
    for(Future<?> future : futures) {
        if(!future.isDone() && !future.isCancelled()) {
            future.cancel(true); //May pass "false" here. Terminating work immediately may produce side effects.
        } 
    }
}

@Nullable
@Override
public IBinder onBind(Intent intent) {
    return null;
}

这不会导致任何内存泄漏,因为任何挂起的工作都会与服务一起被销毁。

关于android - 如何在前台服务中将多个任务排队,以便它们一项一项地执行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55777460/

相关文章:

API 29 上未显示 Android 通知

Android AsyncTask 访问 Activity Context 的更好方式

android - 使用 AsyncTask 的服务每 X 次执行一次请求

安卓 : How can I avoid android os to kill my service

android - 获取库播放列表艺术品 Apple Music API

android - 在底部导航 View 中选择的每个项目下方添加一行

在 targetSdkVersion 8 上运行 AsyncTask 时 Android 崩溃

java - Android:如何从工作线程到服务进行通信

android - 从 FragmentStatePagerAdapter 获取资源

java - Android AsycTask运行时间计算