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

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

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

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

图。

【问题讨论】:

  • 试试我在下面提供的解决方案,它就像一个魅力

标签: android multithreading android-asynctask android-2.2-froyo


【解决方案1】:

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

  1. 保留对所有 3 个任务的引用。当任务完成时,检查其他两个任务是否也完成,如果是,则关闭进度对话框,如果没有,则等待其他任务完成并再次检查。确保在完成后释放参考。
  2. 如果您不想保留参考存储,请使用计数器。当任务完成时,增加计数器并检查它是否等于 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);
                  

                  【讨论】:

                    猜你喜欢
                    • 2012-06-12
                    • 2016-08-24
                    • 1970-01-01
                    • 2014-07-22
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2017-12-10
                    相关资源
                    最近更新 更多