【问题标题】:Pattern for reusing Android AsnycTask over several Activities?在多个活动上重用 Android AsyncTask 的模式?
【发布时间】:2012-06-20 07:04:38
【问题描述】:

我的项目中有几个 Activity 子类,每个子类都调用一个基于 SOAP 的 Web 服务,处理并显示结果。 SOAP 序列化、调用处理和结果解析成各种 POJO 对象都封装在 MyWebService 类中。此类通过 AsyncTask 执行实际的 Web 服务调用。

为了能够将结果传递回调用 Activity 子类,我认为我强制所有这些活动都应实现 WebServiceResultProcessor 接口,定义一个函数 (processWebServiceResults) 作为 AsyncTask 的回调,从 onPostExecute 调用。

我还想在 Web 服务调用期间显示 ProgressDialog。我的问题来了。为了能够显示 ProgressDialog(来自 MyWebService 或它的 AsyncTask),我需要将引用传递给调用者 Activity 的 Context。为了能够从 AsyncTask 执行回调函数,我需要传递相同的对象引用,但这次是作为 WebServiceResultProcessor。在我看来,这似乎是一种代码味道,两次传递同一个对象,但看不到任何解决方法。我可以创建一个新的基类,而不是接口,扩展 Activity 类并强制从扩展类继承,但这意味着我将排除 ListActivity 之类的东西使用这个 MyWebService 类。

有没有更好的方法来做到这一点?

【问题讨论】:

  • 您的 WebServiceResultProcessor 是否扩展了上下文/活动?
  • @HeikoRupp 不,我可能更愿意保持这种状态。

标签: android design-patterns android-activity android-asynctask reusability


【解决方案1】:

+1,一个很好的问题!

这不是对您问题的直接回答。但是让我说我​​认为AsyncTask 不是此类东西的正确选择。我是这么认为的,因为在这种情况下,AsyncTask 持有对Activity 的引用(通过ProgressDialog 实例或要从onPostExecute() 调用的回调)。

想象一下:在 Android 中,操作系统可能会在 AsyncTask 执行其 doInBackground() 之前杀死 Activity。当然,这是某种极端情况,但这并非不可能。考虑一个场景:用户接到来电,您的活动变得不可见,操作系统需要更多 RAM,因此它决定终止您的活动。至少是内存泄漏案例。

我不知道为什么 Google 从字面上隐藏了有关如何将 UI 与后台任务正确分离的信息。是的,他们说“使用服务”。但这不是一项微不足道的工作。遗憾的是,谷歌几乎为每个开发主题都提供了很好的指南,但没有在这个主题上提供。尽管如此,我还是建议您查看“Google I/O 2010 - Android REST client applications”演示文稿以获得灵感。看起来他们给出了如何在 Android 中完成这些事情的关键。

【讨论】:

  • 感谢您指出使用 AsyncTask 中的上下文引用的危险。我发现这个stackoverflow.com/questions/3357477/… 问题和答案相当广泛地涵盖了这个主题。
  • @András Szepesházi:谢谢,这是一次很好的讨论。但是,他们主要讨论如何解决配置更改(例如,活动在设备轮换时重新启动)。但是,如果在我描述的场景(来电)中活动被杀死,而有一个在远程服务器上创建新用户帐户的异步任务呢? :) 将创建帐户,但是当用户返回活动时,它永远不会知道异步任务的结果。所以用户会重试并得到“这样的用户名已被使用”错误? :) 好的,如果 asycntask 进行了支付交易怎么办? :)
  • 哇,这太邪恶了。我非常想避免使用服务和处理程序,但似乎这是唯一的方法。
  • @András Szepesházi:嗯,实际上这取决于重要性/安全性/保证的级别。如果这只是在活动开始时加载一些数据以呈现在 UI 上的操作,那么 AsycnTask 是一种可行的方法(实现起来很简单,如果正确处理配置更改也不会产生不良后果)。但是,如果您想创建一个可用于明智操作(付款等)的通用解决方案,那么它应该涉及服务 + 一些引擎来保持任务状态/进度,因此它与活动/UI 分离(这是一种方法Google IO 演示文稿推出)。
【解决方案2】:

您可以查看这篇博客文章(part 1part 2),它使用 AsyncTaskLoader 实现了一个 Web 服务,并使用一个 Service 组件实现了相同的 Web 服务。此外,它还显示了两种方法之间的差异,并且本文也有一些有趣的 cmets。

【讨论】:

    【解决方案3】:

    尽管有 Arhimed 的警告,我最终还是使用了 AsyncTask,因为它仍然符合我的目的。我只是确保所有调用 Web 服务的活动,在他们的onDestroy() 上,将cancel() 发送到调用的 AsyncTask。 AsyncTask 实现本身通过在必要时检查 isCancelled() 来优雅地处理取消请求。

    至于最初的问题,我一定有失误-解决方案非常简单。我将 Activity 子类实例作为对象传递给 AsyncTask,并根据需要将其转换为 Context 或 WebServiceResultProcessor。显示其工作原理的片段:

    if (callerActivity instanceof Context) {
        ProgressDialog dialog = new ProgressDialog((Context)callerActivity);
    }
    

    ...

    if (callerActivity instanceof WebServiceResultProcessor) {
        ((WebServiceResultProcessor)callerActivity).processWebServiceResults(soapObject);
    }
    

    【讨论】:

    • 我认为if (callerActivity instanceof Context) { .. } 是多余的检查。除非我遗漏任何东西 callerActivity 在您的情况下将始终是 Context 的实例,因此您可以直接调用进度创建代码。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多