18

Android 在不同的线程上同时执行代码有很多不同的便利。但我不确定何时应该使用每一个,或者这些不同方式的最佳实践是什么。

  1. 何时/为什么应该使用处理程序?

  2. 何时/为什么应该使用 Loaders?

  3. 何时/为什么应该使用 AsyncTasks?

  4. 何时/为什么应该使用 FutureTask?

  5. 何时/为什么应该使用 Executor?

  6. 何时/为什么应该使用线程/可运行对象?

  7. 何时/为什么应该使用 Volley?

我错过了吗?

非常感谢您的帮助!

4

1 回答 1

23

你有一个相当大的组件列表,每个组件处理略有不同的事情。根据我的经验,两个主要冲突实际上是在HandlersExecutors以及TasksThreads之间。

概述

线程

线程是用于促进并发的底层系统机制。线程存在于 Windows、Unix 等中,并且可以以系统认为合适的任何方式实现。创建线程通常非常昂贵(创建全新的进程更是如此)。 我不知道 Dalvik VM 是依赖 Linux 线程还是提供了自己的 Thread 实现。 根据这个Dalvik 虚拟机架构文档,Dalvik VM 简单地使用 Linux 线程模型来实现其线程。但是,Dalvik VM 不符合 Android 4.0 之前的 Java 语言规范的内存模型。

您可能知道,由于创建线程的成本很高,安排大量异步计算的更好方法是使用托管线程池(大小取决于底层硬件的能力)并将任务提供给管理器,反过来,它会在线程可用时将任务安排在线程上。

Android 处理程序

据我了解,历史的一个简短概述是,Android 开发始于 Java Executor库/api 固化之前的日子。为了提供比旧的ThreadsSynchronization方法更容易混淆的并发接口,并以一种很好的方式促进线程之间的消息传递,Android 引入了Handlers。但是,除非我理解错误,否则处理程序基本上与执行程序相同。您将消息(包含 Runnables)或简单的 Runables 传递给处理程序,这些消息将排队并尽快执行,处理程序的线程可以很好地处理它们。

Java 执行器

在 Android 中,线程之间的所有通信都(或应该)通过线程的处理程序完成(据我所知,不包括您直接访问另一个线程内存和可能需要同步的情况)。在新的 Java 中,Executors 在功能上等同于 Handlers,FutureTasks 等同于 Messages。Runnables 仍然是 Runnables。当只需要执行代码时使用 Runnable。当需要异步执行计算并且需要从计算中检索结果时,使用 FutureTask。结果可以是任何类型的对象。因为 FutureTask 实现了 Future 接口,你也可以轻松地在一个线程中等待 Future 解析并得到结果。

装载机

加载器只是尝试提供一种可重用机制来加载数据(由活动消耗)。尽管有文档,但 Loader 类不是抽象的,也不需要单独用于异步获取数据(虽然我想不出你会使用它进行同步加载的情况,但我没有探索过) . AsyncTaskLoader只是使用AsyncTask 来执行 Loader 负责加载数据的方法。装载机根本不需要。

例子

线程

线程应该用于支持 Executors 或 Handlers,或者,在其他情况下,您将知道何时需要线程(例如支持 Service)。对于以自由方式创建、执行和销毁的小任务,您通常应该避免使用线程。

处理程序与执行程序

根据我的经验,在处理 UI 线程(运行 Activity 和/或 View 层次结构)和其他线程之间的消息传递时,最好坚持使用 Handlers。但是,在实现自己的异步功能时,使用 Executors 或 Handlers 似乎是完全安全的。例如,我可能有一个类来管理要从服务器获取的图像队列。我可以简单地创建自己的 executor 并将任务提供给我认为合适的 executor。但是,当将一条消息传递回 Activity 时,其中一个图像已加载并且应使用结果数据设置视图,我使用 Activity 的处理程序(通过runOnUiThread()方法)而不是创建一个在 Activity 的线程上运行的执行程序并将 Runnables 发布到它。目前尚不清楚为什么后者不能一直工作(即从文档中),但我怀疑这样做会导致类似于 HandlerrunWithScissors()方法的行为。 (编辑,我现在知道在一个线程上运行多个并发的“looper”是非常糟糕的做法——这就解释了这一点。)

我想一个好的经验法则可能是:

  • 在 Android 组件之间传递消息时使用处理程序。
  • 在实现您自己的库或实用程序功能时使用执行器或处理程序。

Runnables vs FutureTasks vs AsyncTasks

如前所述,Runnables 是一个简单的接口,用于在对象之间传递计算。只有在涉及线程/执行程序/处理程序时才需要使用 Runnables——它们可用于捕获状态和重用代码。

  • Runnables 和 FutureTasks 与 Executors 一起使用。
  • AsyncTasks(隐式)与处理程序一起使用。当您构造 AsyncTask 时,您提供了对活动的引用,并且 AsyncTask 类有助于在工作线程池中的一个线程上执行 doInBackground 方法(这在内部由 Executor 管理)。为此,处理程序用于在活动线程和任务线程之间传递消息。但是,任务本身由 Executor 管理,并在可用时移交给工作线程。

装载机

我不使用装载机。但是,当您需要加载数据以便 Activity 使用它时,可能会使用它们。如果您在处理数据方面遵循 ContentProvider 习惯用法,则可以将 CursorLoader 与 ContentProvider 一起使用。

排球

我从未使用过 volley,所以我怀疑我对该频道有任何有用的见解。


免责声明:我不是 Android 历史的权威。无论如何,我希望概述有助于增加清晰度。

于 2013-09-05T23:11:06.070 回答