五、Android中的多线程
编写代码时首先要牢记的是:确保永远不要阻塞主线程!
Android中所有应用程序都是从主线程(也称为UI线程)开始运行,除非启动另外一个线程或者通过隐式调用函数来启动一个线程,否则所有在Android应用中的操作都会运行在主线程中。这意味着,如果在主线程执行很耗时的操作(比如在 onResume 中运行代码),所有的绘制以及输入事件将被阻塞,直到该操作完成。
1. Thread类
每次更新UI都需要创建一个新的 Runnable 对象。这使得代码变得很乱,而且垃圾回收器还会进行不必要的对象回收,这些都是开发者要避免的。为了在主线程中使用 runOnUiThread 方法更新UI,必须使用 Runnable 。这种方案还有一个问题:因为只能对 Thread 实例调用一次 start 方法,所以每次执行操作都需要创建一个新的 Thread 对象。不断创建新的线程是非常昂贵的,这不是一个非常灵活的方法,开发者应避免直接使用 Thread 类。
2. AsyncTask
AsyncTask 是Android中比较流行的几个类中的一个,因为它很容易使用。它允许开发者定义一个运行在单独线程中的任务,还能在任务的不同阶段提供回调函数。这些回调函数被设计成无需使用runOnUiThread 方法即可更新UI,这非常适合表示长时间运行的操作的进度。
通过 AsyncTask 类,开发者可以很容易在其他线程中执行耗时的任务,也可以在需要时很方便地和主线程通信。使用 AsyncTask 唯一的问题是该类的实例只能使用一次,这意味着每次执行操作都要新建一个MyAsyncTask对象 。虽然是个轻量级的类(实际的线程是由ExecutorService 管理的),但它不适合那些频繁的操作,因为这会快速聚集需要垃圾回收的对象,并最终导致应用程序界面卡顿。
此外, AsyncTask 不能对操作设置执行时间,也无法间隔一段时间执行操作。它适合文件下载,以及不会频繁发生或通过用户交互等类似情况的操作。然而,由于容易实现, AsyncTask很可能是开发时首选的类。
3. Handler类
当需要更细粒度地控制在一个单独的线程中执行操作时, Handler 类会是一个很有用的工具。该类允许开发者准确地控制操作的执行时间,还可以重复多次使用它。执行操作的线程会一直运行,直到被显式地终止。 Looper 类会处理幕后的事情,但开发者很少需要直接和它打交道,相反可以通过包装类 HandlerThread 创建它。
4. 选择合适的线程
前面显示了三种在Android上创建和使用线程的方式。API中和线程相关的类还有ExecutorService 和 Loader 。 ExecutorService 适合处理并行运行的多个任务,这非常适合编写响应多客户端的服务器应用。 AsyncTask 内部同样使用 ExecutorService 处理多线程。如果希望能够并行执行多个 AsyncTask ,也可以通过使用正确的 ExecutorService 来完成。
不建议直接使用Thread 类,除非是要完全控制线程的执行。大多数情况下推荐使用 AsyncTask 和 Handler 类,具体使用哪一个取决于具体的需求。如果不是很频繁地执行操作,比如超过每分钟一次,那么AsyncTask 可能是个不错的选择。如果需要安排操作的时间或者需要快速间隔地执行操作,Handler 会是更好的选择。从长远来看,使用 Handler 生成的代码更少,不过 AsyncTask 更容易使用。