【问题标题】:How to let Asynctask update UI in different ways如何让 Asynctask 以不同的方式更新 UI
【发布时间】:2011-04-14 20:53:21
【问题描述】:

这是我在使用 Asyntask 时经常遇到的问题。联系 UI 线程的方法是调用 publishProgress() 并且此方法接受 只有一个 TYPE 参数的数组。

在后台运行的执行复杂计算的线程可能需要使用不同类型的对象在不同点更新 UI。

让我用一个例子来说明:

...do some processing...
// Send UI thread the integer values of the width & height of the image 
...do some more processing...
// Send UI thread a String with custom message.
...do some more processing...
// Send UI thread an instance of MyObject so it can extract & display certain values 
...do some cleanup job & finish...

但是,onProgressUpdate() 只接受一种类型的数组。

那么我是否将其设为一个无所不包的 Object 类型?由于可以从第 1、2 或 3 行调用此方法,所以我怎么知道如何向下转换它,那么现在是什么时候?

肯定有什么好方法可以做到这一点吗?

编辑:如果在 Android 中可能的话,我真的很想看到定义 publishProgress1(user-defined args1), publishProgress2(user-defined args2), publishProgress3(用户定义的args3) ...

【问题讨论】:

  • 我相信您要求的构造类似于 VARIANT en.wikipedia.org/wiki/Variant_type
  • @JAL:感谢分享有趣的链接。 Variant_type 及其运行时类型确定让我想起了 Java 中的 Object 类型,它具有运行时向下转换。我真的很想看到,如果它在 Android 中是可能的,将是某种定义 publishProgress1(user-defined args1), publishProgress2(user-defined args2), publishProgress3(用户定义的args3) ...
  • @OceanBlue VisualBasisVARIANT 是一个旧的解决方案,但我宁愿看到一个更安全的解决方案,您可以使用消息和一个打开 message.what 的处理程序来解决启动三个顺序线程的问题。所以启动线程返回带有返回类型 A 的消息(0),处理程序中的陷阱消息,带有消息(1)返回类型 B 的启动线程,带有消息(2)的陷阱和启动线程等等。
  • @JAL:将任务分解为更小的任务/不同线程的问题是,如果您的工作需要串行完成,逻辑就会崩溃。除非......你进入整个线程锁定,等待/通知业务,我宁愿避免,潜在的死锁和东西。
  • @OceanBlue 我认为您可以使用我概述的消息按顺序执行此操作。仅当第一个线程返回时才启动下一个线程。如果数据不能通过消息传递,这可能不起作用。无需使用与共享内存并发完全不同的消息传递并发进行线程锁定、等待或通知。 en.wikipedia.org/wiki/Concurrent_computing

标签: android design-patterns


【解决方案1】:

在你的第三种情况下......

// 向 UI 线程发送 MyObject 的实例

...有一个论点说您会在 onPostExecute() 中执行此操作,尽管这取决于您在插图中的意思。

你可以很容易地按照你的建议传递一个包罗万象的对象。对象可以有各种字段(整数、字符串、对象)加上一个“操作”来描述哪些字段是有效的并且需要处理。

您可以简单地传递一个 int 枚举,例如 PROCESS_INT_WIDTH_AND_HEIGHTPROCESS_STRING_MESSAGEPROCESS_OBJECT 等。没有什么可以阻止您这样做...

private static class MyAsyncTask extends AsyncTask<String, int, Void> {
    private int width;
    private int height;
    private String customMessage;
    private MyObject myObject;

    @Override
    protected Void doInBackground(String... params) {

        width = 10;
        height = 10;
        publishProgress(PROCESS_INT_WIDTH_AND_HEIGHT);
    }

    protected void onProgressUpdate(int... progress) {
        if (progress == PROCESS_INT_WIDTH_AND_HEIGHT)
            // Process width and height
    }
}

换句话说,onProgressUpdate() 方法只是响应“命令”并相应地处理相关的私有字段。

【讨论】:

  • 这里的一个危险是publishProgress() 可能在onProgressUpdate() 成功之前被多次调用。如果您修改您的方法,让publishProgress() 采用Object,第一个参数是您的Integer 命令ID,其他参数是其余数据,您将避免此问题。
  • @CommonsWare:有效点 - 根据所涉及任务的复杂性,它可能会导致问题。我的偏好实际上是 OP 暗示的“包罗万象”对象,其中嵌入了要执行的操作/命令(如您所说)。从某种意义上说,这模拟了 Intent 的使用方式——可以在不同的情况下提供相同的数据参数,但“操作”最终决定了结果。
  • 谢谢,我想这可能是最好的方法,在限制下。请参阅编辑。
【解决方案2】:

如果您想坚持使用AsyncTask,另一种选择是在不同时间使用不同的类,并使用 instanceof 测试类型。

但是,这听起来像是一项相对复杂的任务,所以我建议使用Handler 并将其从常规Thread 发布[Runnable]s2,或使用runOnUiThread

您可能还想阅读Painless Threading

希望这会有所帮助,

菲尔·莱洛

【讨论】:

  • 链接+1。这些是很棒的链接,我以前读过它们。我认为创建 AsnycTask 基本上是为了避免处理程序和线程的复杂性(和潜在的陷阱),所以我想尽可能坚持下去。
【解决方案3】:

我认为没有其他方法可以做到这一点。使用 getType 方法创建一个基类,然后将其用作您的类型以进行适当的转换。

【讨论】:

    【解决方案4】:

    在这种情况下泛型类型会起作用吗?您可以根据需要对值进行类型转换。示例AsyncTask原型:

    new AsyncTask<String, Object, List<?>>(){ 
    ...
    

    我还记得读过AsyncTask 最适合仅用于短期任务。考虑其他线程模型用于复杂和长时间运行的操作。

    【讨论】:

    • 服务并不意味着是长时间运行的任务,而只是一种在后台运行的方式。对于长时间运行的任务,即使在服务中也应该使用线程。
    • 不是很长时间运行。只需几秒钟。但是对于盯着固定屏幕的用户来说,这些看起来也很长。一路说你想做一些事情......显示一条消息,然后进行一些处理,更新整数值等等。希望我的问题有意义!
    • @OceanBlue 您仍然可以考虑将您的任务分成更小的部分或使用较低级别的模型。 AsyncTask 似乎并不适合所有情况。还可以考虑使用泛型类型来满足您想要的行为。
    【解决方案5】:

    如果您返回的值在AsyncTask 处理中具有相应的状态,您可以在AsyncTask 内为您需要的每个数据/类型创建一个私有成员,然后为每个状态分配一个整数并在@ 987654323@ 放置一个 switch(state),它根据从 publishProgress(state) 获得的 int 状态来完成这项工作。

    (这可能不是我给出的最清楚的答案)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-04-04
      • 1970-01-01
      • 2016-07-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多