如何在 Android 上安排长时间(耗时)的操作?

Posted

技术标签:

【中文标题】如何在 Android 上安排长时间(耗时)的操作?【英文标题】:How to arrange long (time consuming) actions on Android? 【发布时间】:2011-01-15 12:15:58 【问题描述】:

例如,我们在 SomeActivity 中,该活动有一个按钮,用于调用将文件从一个目录移动到另一个目录(我们称之为作业)。

在黑莓上我会:

    推送一个不可取消的弹出窗口(对话屏幕)说“请稍候...” 启动一个完成任务的线程 在线程完成时关闭弹出窗口

这种方法 99.99% 可以保证我们在任务结束后保持在同一个屏幕上,用户只是看到弹出窗口并等待任务完成。设备轮换或任何不会破坏所需工作流程的方式。

android 上,情况发生了变化。我知道可能会提供 AsyncTask 来解决我的问题。甚至还有一个很好的example 应该如何使用它。但由于无法保证 Activity 实例将存活多长时间,因此应在 onSaveInstanceState 上取消 AsyncTask(并在 onRestoreInstanceState 上重新启动)。这意味着使用 AsyncTask 并不能保证我们一旦开始就能够完全完成工作。在某些情况下,作为发送创建用户的 http 发布请求,我不想在重新运行 AsyncTask 时遇到“用户已存在此登录”的麻烦。这是可能的,因为 AsyncTask 可以在请求已经发送时被中断(并且服务器实际上正在做它的工作 - 创建一个新用户),但是 AsyncTask 在我们得到响应之前被取消。

在 Android 上是否有任何解决方案来获得上述类似 BB 的行为?

【问题讨论】:

【参考方案1】:

但由于无法保证如何 一个 Activity 实例会存在多久 AsyncTask 应该在 onSaveInstanceState (并重新启动 onRestoreInstanceState)。

或者让它由Service管理。

【讨论】:

我应该研究这个组件,因为已经有人建议我使用它两次。 嗨,CommonsWare。在您的任何书籍中,是否详细解释了应如何在 Android 上对长期运行的作业进行架构/编码? 这在一定程度上取决于您将什么定义为“长时间运行的作业”。我在一本书(commonsware.com/Android)中介绍了使用 AsyncTask 的服务——请参阅github.com/commonsguy/cw-android/tree/master/Service/… 了解相关项目。我在另一本书 (commonsware.com/AdvAndroid) 中介绍了使用 AlarmManager 来避免永久服务 - 请参阅 github.com/commonsguy/cw-advandroid/tree/master/SystemServices/… 了解该项目。 现在才看到你的回复(我以为会有关于新评论的某种电子邮件通知,但没有)。 我正在寻找一种解决方案,用于在进度弹出窗口下正确处理长时间运行的作业线程,假设用户从 Activity 调用它。问题是如何将作业线程与 Activity 的生命周期“同步”。那是我原来的帖子。【参考方案2】:

如果您的 Activity 想要停留在屏幕上,您可以像这样简单地启动一个线程:

final File fromFile = ...;
final File toFile = ...;

new Thread() 
    @Override
    public void run() 
      // do something with fromFile, toFile
    
.start();

这样,GUI线程就可以做其他的事情了,比如显示一个

  android.app.ProgressDialog

另外,考虑使用

使对话框不可取消
  ProgressDialog.setCancelable(false);

这样用户只能通过 HOME-Key 离开,您会在何时收到通知

  Activity.onPause()

被调用。此外,您可能想要查看 Wakelocks,以阻止屏幕变黑,并将您的应用程序推到可能被杀死的后台。你会在线程中这样做:

  PowerManager pm = (PowerManager) ivContext.getSystemService(Context.POWER_SERVICE);
  Wakelock wakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "MyApp");
  wakeLock.acquire();

  // ... copy stuff ...

  wakeLock.release();

当然,当用户通过 HOME 键离开时,您也必须释放唤醒锁。

最后,如果您想从后台线程调用 GUI-Elements,这仅在线程是 GUI-Event-Loop 的一部分时才有效,就像您正在运行的正常线程一样,当被调用时...-方法。为了实现这一点,您的后台线程必须通过处理程序回调到 GUI 线程。像这样:

  private Handler mHandler = new Handler()  
      @Override
      public void handleMessage(Message msg) 
          Log.v(TAG, "Got Message "+msg.what); // prints: Got Message 77
      // ... do GUI actions ...
          
  ;

  // ... in Thread ...

  int lvInfo = 77;
  mHandler.sendEmptyMessage(lvInfo);

你甚至可以像这样在消息中放置对象:

  Message txtMsg = Message.obtain();
  textMsg.obj = "Hello World";
  mHandler.sendMessage(lvTextMsg);

【讨论】:

rflexor,感谢您的回答。专门用于唤醒锁点。【参考方案3】:

2010 年 5 月,Google 发布了一个名为 Developing Android REST client applications 的新 IO 会话,它解释了如何实现我所要求的。

原来这个问题相当复杂,所以没有简单快捷的解决方案。该解决方案需要深入了解 Android 平台/API。这是应用程序流程/活动生命周期的灵活性造成的代价。

我觉得有些奇怪,为什么在 Android 的第一个版本中没有提供此信息。看起来 Google 知道如何编写 100% 无错误的应用程序,但出于某种营销原因并没有分享这种方法。试想一下,到 2010 年 5 月,有多少错误的应用程序被编写出来。

无论如何,我很高兴我现在有了我们称之为最佳实践方法的东西。

【讨论】:

以上是关于如何在 Android 上安排长时间(耗时)的操作?的主要内容,如果未能解决你的问题,请参考以下文章

Android Service的启动过程

Android核心组件 Service

简单的 MySQL 查询需要很长时间来计算

Android AsyncTask 用于长时间运行的操作

winform批量更新数据_长时间的执行会导致界面卡死

在 Android 上测量长时间的运行时间(重新启动)