检查所有 AsyncTask 是不是已完成

Posted

技术标签:

【中文标题】检查所有 AsyncTask 是不是已完成【英文标题】:Check if all AsyncTasks have finished检查所有 AsyncTask 是否已完成 【发布时间】:2011-10-18 23:26:28 【问题描述】:

我有 3 个AsyncTasks 和 1 个ProgressBar。我希望当任何任务执行时,进度条是可见的,当所有任务完成时,进度条是不可见的。

在 Java 中,有 ExecutorService::isTerminated 来检查是否所有可运行对象都已完成,但 android 没有。

更新:3 个任务同时执行。

图。

【问题讨论】:

试试我在下面提供的解决方案,它就像一个魅力 【参考方案1】:

漂亮的图形。但恐怕没有内置机制。你必须自己实现它。您可以使用的解决方案很少 -

    保留对所有 3 个任务的引用。当任务完成时,检查其他两个任务是否也完成,如果是,则关闭进度对话框,如果没有,则等待其他任务完成并再次检查。确保在完成后释放参考。 如果您不想保留参考存储,请使用计数器。当任务完成时,增加计数器并检查它是否等于 3。如果所有任务都完成并且你完成了。如果您实施此操作,请确保同步对计数器的访问。

【讨论】:

【参考方案2】:

尝试使用 AsyncTask.getStatus()。这工作得很好。请参考下面的示例代码。

 List<AsyncTask<String, String, String>> asyncTasks = new ArrayList<AsyncTask<String, String, String>>();
 AsyncTask<String, String, String> asyncTask1 = new uploadTask().execute(string);
 AsyncTask<String, String, String> asyncTask2 = new downloadTask().execute(string);
 AsyncTask<String, String, String> asyncTask3 = new createTask().execute(string);
 asyncTasks.add(asyncTask1);
 asyncTasks.add(asyncTask2);
 asyncTasks.add(asyncTask3);

您可以稍后循环 AsyncTaskList 并找到每个任务的状态,如下所示。

 for(int i=0;i<asyncTasks.size();i++)
      AsyncTask<String, String, String> asyncTaskItem = (AsyncTask<String, String, String>)asyncTasks.get(i);
      // getStatus() would return PENDING,RUNNING,FINISHED statuses
      String status = asyncTaskItem.getStatus().toString();
      //if status is FINISHED for all the 3 async tasks, hide the progressbar
 

【讨论】:

我在没有名字的情况下做了,但效果很好。谢谢!之后我确实从 ArrayList 中删除了这些项目(当状态 == 完成时),而不仅仅是检查状态。这将使更大的 asyncTasks 集更容易,比如我的 30 多个任务集..【参考方案3】:

一个简单的解决方法是为每个 AsyncTask 使用三个布尔变量,然后相应地检查它们。

更好的方法是创建一个单独的类来扩展 AsynTask 并定义一个在 onPostExecute 中触发的回调接口。

【讨论】:

【参考方案4】:

创建一个字段来保存所有任务:

private ArrayList<htmlDownloaderTask> mTasks;

以这种方式开始你的任务:

HtmlDownloaderTask = new HtmlDownloaderTask(page.getHtml());
task.execute(page.getUrl());
//if you want parallel execution try this:
//task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,page.getUrl());
mTasks.add(task);

关于 MyAsyncTask 的 onPostExecute:

int unfinishedTasks = 0;
for (HtmlDownloaderTask myDT : mTasks)
    if(!(myDT.getStatus() == AsyncTask.Status.FINISHED))
        unfinishedTasks++;
    

if (unfinishedTasks == 1)
    //We are all done. 1 Because its the current one that hasnt finished post execute
    callWhateverMethod();


【讨论】:

【参考方案5】:

您知道AsyncTask 何时结束(当onPostExecute 被调用时): 一种解决方案可能是创建一个方法setProgressBarVisible() 保持计数器在第一次调用时设置可见,以及一个方法setProgressBarInvisible() 减少计数器并且在零时设置进度条不可见。

【讨论】:

【参考方案6】:

:-?我认为这只是一个把戏。您将在每个AsyntaskonPostExecute 返回一些消息并进行比较。 (例如,此消息可以包含时间)

【讨论】:

【参考方案7】:

自 API 级别 24 起引入了对 CompletableFuture 的官方支持。

它也可用于 Java 8 here。

可以简单地使用类似的东西:

taskA.thenCombine(taskB).thenCombine(taskC)

【讨论】:

【参考方案8】:

我会简单地通过onPostExecute() 通知它,参考onPostExecute and 4 steps in the document for detail,您可以使用EventBus 做一些订阅事情。

【讨论】:

【参考方案9】:

当您想在 THREAD_POOL_EXECUTOR 上运行一堆 AsynTasks 时,这是一个常见问题。这比您只调用.execute() 并且您的所有任务都一项一项完成要快得多。 因此,如果您有多个作业并且对象不依赖于彼此的状态 - 尝试在线程池上运行。

但问题是:我怎么知道我的所有任务都已完成?

AsyncTask 中没有内置方法,因此您应该做一些解决方法。

在我的例子中,我在我的 Asynctask 类中添加了一个静态 Hashmap 字段,以跟踪所有已启动和已完成的任务。作为地图的奖励,我总是可以知道当前正在进行哪个任务。

    private static HashMap<Uri, Boolean> mapOfAttachmentTasks = new HashMap<>();

并提供三种访问此地图的简单方法。 重要提示:它们应该同步

public static synchronized void addTask(Uri uri)
    mapOfAttachmentTasks.put(uri, true);


public static synchronized void removeTask(Uri uri)
    mapOfAttachmentTasks.remove(uri);


public static synchronized boolean isTasksEmpty()
    return mapOfAttachmentTasks.isEmpty();

您想在 AsyncTask 构造函数中将新项目添加到跟踪地图并在 onPostExecute() 中将其删除:

public AttachmentTask(Uri uri) 
    this.uri = uri;
    addTask(uri);


@Override
protected void onPostExecute(Attachment attachment) 
    removeTask(uri);

    if(isTasksEmpty())
        EventBus.getDefault().post(new AttachmentsTaskFinishedEvent(attachment));

每次任务完成时,它都会调用 onPostEexecute 并检查它是否是最后一个任务。如果没有剩余任务 - 发送信号表明您已完成。

现在,我在这里使用 EventBus 将事件发送到我的 Fragment,但您可以使用回调。在这种情况下,您应该使用 callbackMethod 创建一个接口,您的 Fragment(任何等待事件的 UI 组件)应该实现该接口并具有该方法。然后在 AsyncTask 构造函数中获取 Fragment 作为参数并保留对它的引用,以便在一切完成后调用它的回调方法。

但我不喜欢这样的做法。首先,您需要将 Fragment(或任何其他 UI)的引用保存在 WeakReference 包装器中,因为当您的 Fragment 死了(但仍保留在内存中,因为您的 AsyncTask 有它的引用)时,您会发生内存泄漏。 此外,您需要进行大量检查,它看起来像这样:

private boolean isAlive() 
    return mFragmentWeakReference != null
            && mFragmentWeakReference.get() != null
            && mFragmentWeakReference.get().isAdded()
            && mFragmentWeakReference.get().getActivity() != null
            && !mFragmentWeakReference.get().getActivity().isFinishing();

是的,在生产中你应该有点偏执并做所有这些检查:)

这就是你可以使用 EventBus 的原因,如果你的 UI 死了 - 无论如何。

【讨论】:

【参考方案10】:

试试这个,也许能帮到你……

            final ImageUploader _upload = new ImageUploader();
            _upload.setValue(getApplicationContext(), _imagepath, _urlphp);
            _upload.execute();
            Runnable _run;
            Handler _h2;
            _run = new Runnable() 
                public void run() 
                    _h2 = new Handler();
                    _h2.postDelayed(this, 1000);
                    Toast.makeText(getApplicationContext(), "not finished", Toast.LENGTH_LONG).show();

                    if (_upload.getStatus() == AsyncTask.Status.FINISHED) 
                        Toast.makeText(getApplicationContext(), "finished", Toast.LENGTH_LONG).show();
                        _h2.removeCallbacks(_run);

                    
                
            ;
            _h2 = new Handler();
            _h2.postDelayed(_run, 1);

【讨论】:

以上是关于检查所有 AsyncTask 是不是已完成的主要内容,如果未能解决你的问题,请参考以下文章

如何检查改造请求调用是不是完成?

ProgressDialog.show() - Activity 已泄露窗口

如何检查Retrofit请求呼叫是否完成?

在 Asynctask 中将视图作为参数发送

使用 Kotlin 显示来自 AsyncTask 的 Toast 消息

检查 Elasticsearch 是不是已完成索引