如何报告任务的进度

时间:2015-04-07 17:14:53

标签: java android multithreading concurrency android-asynctask

我想报告 Android独立计算函数在进行不间断计算时的进度。

通过Android-Independant,我的意思是包的代码不能依赖于任何Android包。

理想情况下,我希望能够监控任何需要时间才能完成的功能的进度。 我使用AsyncTask定期向UI线程报告。 AsyncTask启动一个运行该函数的Thread。 因此,每一秒,AsyncTask都会询问Thread的进度并将其发布到UI Thread。

所以我有三个主题:

  1. 负责用户交互和显示的UI线程
  2. 运行计算并设置其进度的计算线程 快速
  3. AsyncTask负责定期询问计算线程是否已完成,如果尚未完成则完成了多少工作,然后将答案发布到UI线程
  4. 它几乎可以工作,但我对结果不满意,我认为我的实现很麻烦而且坏了。

    我发现了两个问题:

    1. 当我启动线程时,它可能无法立即启动。 (更多 确切地说,我看到它的输出可能需要几秒钟, 那不一样。)
    2. 如果计算线程持续时间超过预定义的持续时间,我 没有检测功能就无法杀死它。
    3. 使用此日志跟踪(在连接的设备上执行)可以看到

      1-:

      04-07 15:34:31.416  18706-18834/com.example.android.elevationdrag D/yo? loop(99)0.0 0.0 0.0 0
      04-07 15:34:32.416  18706-18834/com.example.android.elevationdrag D/yo? loop(98)0.0 0.0 0.0 0
      04-07 15:34:33.416  18706-18834/com.example.android.elevationdrag D/yo? loop(97)0.0 0.0 0.0 0
      04-07 15:34:34.416  18706-18834/com.example.android.elevationdrag D/yo? loop(96)0.0 0.0 0.0 0
      04-07 15:34:34.826  18706-18835/com.example.android.elevationdrag D/yoSVC? 0.0
      04-07 15:34:35.006  18706-18835/com.example.android.elevationdrag D/dalvikvm? GC_FOR_ALLOC freed 5547K, 50% free 5614K/11228K, paused 29ms, total 30ms
      04-07 15:34:35.416  18706-18834/com.example.android.elevationdrag D/yo? loop(95)0.16882964936278524 0.0 0.0 0
      04-07 15:34:35.456  18706-18835/com.example.android.elevationdrag D/dalvikvm? GC_FOR_ALLOC freed 1341K, 44% free 6321K/11228K, paused 24ms, total 24ms
      04-07 15:34:35.916  18706-18835/com.example.android.elevationdrag D/dalvikvm? GC_FOR_ALLOC freed 812K, 33% free 7556K/11228K, paused 31ms, total 31ms
      04-07 15:34:36.356  18706-18835/com.example.android.elevationdrag D/dalvikvm? GC_FOR_ALLOC freed 835K, 22% free 8769K/11228K, paused 40ms, total 40ms
      04-07 15:34:36.426  18706-18834/com.example.android.elevationdrag D/yo? loop(94)0.461804305565497 0.16957664338683295 1696.0 1696
      04-07 15:34:36.776  18706-18835/com.example.android.elevationdrag D/dalvikvm? GC_FOR_ALLOC freed 870K, 12% free 9946K/11228K, paused 47ms, total 47ms
      04-07 15:34:37.286  18706-18835/com.example.android.elevationdrag D/dalvikvm? GC_FOR_ALLOC freed 854K, 8% free 11370K/12340K, paused 61ms, total 61ms
      04-07 15:34:37.426  18706-18834/com.example.android.elevationdrag D/yo? loop(93)0.7725448196430245 0.461804305565497 4618.0 4618
      04-07 15:34:37.796  18706-18835/com.example.android.elevationdrag D/dalvikvm? GC_FOR_ALLOC freed 1149K, 10% free 12877K/14236K, paused 68ms, total 68ms
      04-07 15:34:38.126  18706-18835/com.example.android.elevationdrag D/yo? Released from DoCalculatePlan.run()
      04-07 15:34:38.436  18706-18834/com.example.android.elevationdrag D/yo? loop(92)100.0 0.7728058175550427 7728.0 7728
      

      yo行来自AsyncTask,我在其中放置以下行:

      Log.d("yo", "loop("+loops+")" +String.valueOf(cpt_plan.getPercentProgress())+" "+String.valueOf(progressr)+" "+String.valueOf(progressd)+" "+String.valueOf(progress));
      

      yoSVC行来自函数本身,我在其中放置以下行:

      Log.d("yoSVC", String.valueOf(this.percentProgress));
      

      从服务类进行更多日志记录。可以观察到在15:53:45.096发生的上下文切换,在调用Thread.sleep()后恰好一秒钟。

      04-07 15:53:43.096  24348-24421/com.example.android.elevationdrag D/yo? loop(99)0.0 0.0 0.0 0
      04-07 15:53:44.096  24348-24421/com.example.android.elevationdrag D/yo? loop(98)0.0 0.0 0.0 0
      04-07 15:53:44.486  24348-24422/com.example.android.elevationdrag D/yoSVC? 0.011000550888307417
      04-07 15:53:44.506  24348-24422/com.example.android.elevationdrag D/yoSVC? 0.022001101776615132
      04-07 15:53:44.546  24348-24422/com.example.android.elevationdrag D/yoSVC? 0.04500068861038525
      04-07 15:53:44.566  24348-24422/com.example.android.elevationdrag D/yoSVC? 0.05600123949869297
      04-07 15:53:44.606  24348-24422/com.example.android.elevationdrag D/yoSVC? 0.0790008263324631
      04-07 15:53:44.626  24348-24422/com.example.android.elevationdrag D/yoSVC? 0.09000137722077081
      04-07 15:53:44.666  24348-24422/com.example.android.elevationdrag D/yoSVC? 0.11300096405454094
      04-07 15:53:44.706  24348-24422/com.example.android.elevationdrag D/yoSVC? 0.13600055088830218
      04-07 15:53:44.726  24348-24422/com.example.android.elevationdrag D/yoSVC? 0.14700110177660103
      04-07 15:53:44.756  24348-24422/com.example.android.elevationdrag D/yoSVC? 0.1700006886103526
      04-07 15:53:44.776  24348-24422/com.example.android.elevationdrag D/yoSVC? 0.18100123949865146
      04-07 15:53:44.816  24348-24422/com.example.android.elevationdrag D/yoSVC? 0.20400082633240305
      04-07 15:53:44.836  24348-24422/com.example.android.elevationdrag D/yoSVC? 0.2150013772207019
      04-07 15:53:44.866  24348-24422/com.example.android.elevationdrag D/yoSVC? 0.23800096405445348
      04-07 15:53:44.906  24348-24422/com.example.android.elevationdrag D/yoSVC? 0.26100055088822277
      04-07 15:53:44.926  24348-24422/com.example.android.elevationdrag D/yoSVC? 0.27200110177653936
      04-07 15:53:44.956  24348-24422/com.example.android.elevationdrag D/yoSVC? 0.295000688610328
      04-07 15:53:44.976  24348-24422/com.example.android.elevationdrag D/yoSVC? 0.3060012394986446
      04-07 15:53:45.016  24348-24422/com.example.android.elevationdrag D/yoSVC? 0.32900082633243327
      04-07 15:53:45.026  24348-24422/com.example.android.elevationdrag D/yoSVC? 0.34000137722074986
      04-07 15:53:45.066  24348-24422/com.example.android.elevationdrag D/yoSVC? 0.3630009640545385
      04-07 15:53:45.096  24348-24421/com.example.android.elevationdrag D/yo? loop(97)0.38391750447598555 0.0 0.0 0
      04-07 15:53:45.106  24348-24422/com.example.android.elevationdrag D/yoSVC? 0.3860005508883272
      

      “service”类使用double进行检测,以便发布进度:

      class Plan
      {
        //...
      
        // In order to inform users of the calculation progress, the service itself has
        // to publish its progress.
        private volatile double percentProgress;
      
        //...
      }
      

      从Activity中提取的Android应用程序代码:

      private class CalculatePlanTask extends AsyncTask<BigDecimal, Integer, Plan>
      {
          volatile boolean released = false;
      
          volatile Plan cpt_plan;
      
      
          CalculatePlanTask()
          {
              cpt_plan = new Plan();
          }
      
          private class DoCalculatePlan implements Runnable
          {
              final BigDecimal period;
              final BigDecimal principal;
              final BigDecimal rate;
      
              private DoCalculatePlan(BigDecimal period, BigDecimal principal, BigDecimal rate)
              {
                  this.period = period;
                  this.principal = principal;
                  this.rate = rate;
              }
              /**
               * Starts executing the active part of the class' code. This method is
               * called when a thread is started that has been created with a class which
               * implements {@code Runnable}.
               */
              @Override
              public void run()
              {
                  android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_LOWEST);
      
                  Situation situation = new Situation(principal, period.intValue());
                  cpt_plan.setSituationDeDepart(situation);
                  cpt_plan.setUniqueEvent(new RateChange(0, rate.movePointLeft(2), RateChange.RateChangeMode.KEEP_DURATION));
                  cpt_plan.computeSituation();
      
                  released = true;
                  //Log.d("yo", "Released from DoCalculatePlan.run()");
              }
          }
      
          /** The system calls this to perform work in a worker thread and
           * delivers it the parameters given to AsyncTask.execute() */
          protected Plan doInBackground(BigDecimal... bds) {
      
              publishProgress(0);
              BigDecimal bdPeriods = bds[0];
              BigDecimal bdPrincipal = bds[1];
              BigDecimal bdRate = bds[2];
      
              android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
              Thread calculator = new Thread(new DoCalculatePlan(bdPeriods, bdPrincipal, bdRate));
              calculator.start();
      
              final int timeoutMilli = 100 * 1000;
              final int sleeptime = 1000;
              final int maxLoops = timeoutMilli / sleeptime;
              int loops = maxLoops;
              int progress = 0;
              boolean thrown = false;
              while (!released && !thrown && --loops >= 0)
              {
                  //DO NOT use this variable : progress = (int) Math.round(cpt_plan.getPercentProgress()*100.0);
                  //double progressr = cpt_plan.getPercentProgress(); //cpt_plan.percentProgress;
                  //double progressd = Math.round(cpt_plan.percentProgress*10000);
                  progress = (int) Math.round(cpt_plan.percentProgress*10000);
                  //DONE:
                  try
                  {
                      Thread.sleep(sleeptime);
                      publishProgress((int) Math.round(cpt_plan.getPercentProgress()*10000)); // Use directly the volatile variable which value is copied.
                      //Log.d("yo", "loop("+loops+")" +String.valueOf(cpt_plan.getPercentProgress())+" "+String.valueOf(progressr)+" "+String.valueOf(progressd)+" "+String.valueOf(progress));
                  }
                  catch (InterruptedException e)
                  {
                      released = true;
                      thrown = true;
                  }
              }
      
              //DONE : put the thread in an interrupted state.
              // we cannot kill it :(
              calculator.interrupt();
      
              if (0 == loops || thrown)
                  return null;
      
              return cpt_plan;
          }
      
          protected void onProgressUpdate(Integer... progress)
          {
              int p = progress[0];
              EditText principal = (EditText) findViewById(R.id.editPrincipal);
              principal.setText(progress[0].toString());
          }
      
      
          /** The system calls this to perform work in the UI thread and delivers
           * the result from doInBackground() */
          protected void onPostExecute(Plan p_plan)
          {
              if (null == p_plan)
                  plan = new Plan();
              else
                  plan = p_plan;
      
              postCalculateImpl();
          }
      }
      

      代码已被修改很多次以进行实验,所以可能看起来有点奇怪,然而,建设性意见表示赞赏。

      在进一步讨论之前,我想知道我是否误解了某些内容,而且到目前为止我发现的大多数问题或博客条目都是在任务中或使用Android代码实现的。

      我想知道为什么我的计算线程在AsyncTask中调用sleep之后不会立即开始工作(可能是这样且日志记录容易出错)。

      我也想知道如何在不将丑陋的线程代码放入计算功能的情况下中断我的线程。每次设置percentProgress时调用Thread.isInterrupted()都是我最容易接受的。

      我必须注意这里的文档: http://developer.android.com/reference/android/os/AsyncTask.html

        

      首次引入时,AsyncTasks在单个上串行执行   背景线程。从DONUT开始,这被改成了一个池   允许多个任务并行运行的线程。从...开始   HONEYCOMB,任务在单个线程上执行以避免常见   并行执行导致的应用程序错误。

           

      如果您真的想要并行执行,可以调用   executeOnExecutor(java.util.concurrent.Executor,Object [])with   THREAD_POOL_EXECUTOR

      因此,当我测试executeOnExecutor()时,我期望并发执行,但只看到顺序执行。

      此答案Running multiple AsyncTasks at the same time -- not possible?可能会解释此行为,因为我的设备在Android 4.4下运行。但是我身上的错误更有可能发生。

0 个答案:

没有答案