【问题标题】:Executing task asynchronously from a timer freezes UI when resuming from background从后台恢复时从计时器异步执行任务会冻结 UI
【发布时间】:2016-01-06 10:21:49
【问题描述】:

我正在开发的应用程序正在显示当地电车的时间表。数据是从网站获取的,应该每 30 秒刷新一次。为了在后台执行 fetch,我以更适合 scala 的方式重新实现了 AsyncTask。

摘自class RichFuture[T](future: Future[U]),它通过隐式转换丰富了Future:

def mapUI[U](func: (T) ⇒ U)(implicit activity: Activity, ec: ExecutionContext): Future[U] = {
  val promise = Promise[U]
  future andThen {
    case Success(value) ⇒
      activity.runOnUiThread(new Runnable {
        override def run(): Unit = {
          try {
            promise.success(func(value))
          } catch {
            case t: Throwable ⇒ promise.failure(t)
          }
        }
      })
    case Failure(fail) ⇒
      promise.failure(fail)
  }
  promise.future
}

这基本上取代了 AsyncTask which does not work in scala。完整的课程可以在github 看到。不过我认为这无关紧要。

fetch 函数函数如下所示:

def fetchSchedule(): Unit = {
  implicit val ec: ExecutionContext = ExecutionContext.fromExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
  Schedule.fetch(station) mapUI { schedules ⇒
    Log.w("Jenastop", "Schedules are fetched")
    /* Display results in UI */
  } recoverUI {
    case t: Throwable ⇒
      /* Display error in UI */
  }
}

调用fetchSchedule的地方是这样的:

protected override def onResume(): Unit = {
  super.onResume()

  Log.w("Jenastop", "Resume started")

  // Setup timer that refreshes the data every 30
  // seconds.
  timer = new Timer
  timer.scheduleAtFixedRate(new TimerTask {
    override def run(): Unit = fetchSchedule()
  }, 0, 30000)

  Log.w("Jenastop", "Resume finished")
}

现在的问题是:每当我将 Activity 置于后台并尝试恢复它时,它都会冻结,直到获取时间表为止。当我第一次启动活动时不会发生这种情况,但只有当我尝试从后台恢复它时才会发生。

从后台恢复时的日志输出是这样的:

01-06 11:19:06.694  5280  5280 W Jenastop: Resume started
01-06 11:19:06.694  5280  5280 W Jenastop: Resume finished
01-06 11:19:07.974  5280  5280 I Choreographer: Skipped 71 frames!  The application may be doing too much work on its main thread.
01-06 11:19:07.974  5280  5280 W Jenastop: Schedules are fetched
/* UI unfreezes */

在 THREAD_POOL_EXECUTOR 中的任务完成之前,UI 似乎一直处于阻塞状态。我该如何规避呢?

【问题讨论】:

  • 一个小细节。而不是try { promise.successful(...) } catch ... },你应该简单地运行promise.complete(Try(func(value)))。
  • 不是 Android 专家。所以Schedule.fetch 拥有未来,对吧?您是否有理由必须使用AsyncTask.THREAD_POOL_EXECUTOR?根据我的经验,您可以使用自定义池轻松阻止期货。如果您使用默认的执行上下文,您可以在未来的正文中使用blocking { } 来避免此类情况。
  • 因为您使用 insideSchedule.fetch 的 Jsoup 可能会阻塞。使用异步 http 库可能会更好。同样,我对此一无所知,但也许this blog 有助于这样做。
  • 在 android 上避免使用 scala 池的主要原因是与某些设备的兼容性。它并不总是有效。 blog.scaloid.org/2013/11/…
  • Jsoup 预计会被阻止。出于这个原因,我在线程池中而不是在 UI 线程中运行它。 UI 线程也被阻塞了,我不知道为什么。

标签: android scala android-asynctask


【解决方案1】:

该错误与模拟器有关。在真实设备上运行代码时,根本不会发生延迟。

然而,在模拟器上,似乎只有在线程池耗尽时才会恢复活动。我通过向网络连接添加恒定延迟来测试这一点。

如果有人对这里到底发生了什么有更好的答案,我会很高兴接受它,但由于这只是调试过程中的一个问题,我没有动力进一步研究它

【讨论】:

    猜你喜欢
    • 2017-01-12
    • 1970-01-01
    • 2015-06-07
    • 2020-06-21
    • 2020-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-11
    相关资源
    最近更新 更多