线程
由于Android的Activity中默认所有代码都在主线程(UI线程)中执行,如果在这里面执行耗时任务(例如下载),界面就会无反应且不可操作,直到耗时任务执行完毕。
如果想在执行耗时任务的同时又想让界面不会没有反应,就需要新开一个线程(Thread)。系统会在UI线程和新开的线程之间不断切换,由于切换速度极快且可以操作界面,就会给人一种没有在执行耗时任务的感觉。
JAVA中的线程
在JAVA中,有两个跟线程关系最紧密的类或接口:
-
Runnable接口:只有一个抽象方法run()。这是线程实际执行的方法,应该实现这个方法并把要在线程中执行的代码放到这里。
-
Tread类:实现了Runnable接口。通过执行start()来开启线程,并在新的线程中执行run()的代码。
能否直接执行Tread的run()方法呢?答案是不行。如果直接在Thead执行run(),它仍然会在当前线程(例如UI线程)里执行run()的代码,而不是在新的线程中运行。
以下是线程的一种写法:
new Thread(new Runnable() {
@Override
public void run() {
// 在线程中运行的代码
}
}).start();
Android中的线程
任务(task):执行一个特定操作的一个Runnable对象或者Runnable对象集。
在Android中,可以使用上面介绍的方法开启线程。不过那样的写法会使得一旦线程的任务结束,不会再次运行这个线程(也就是一次性线程)。
当你想为不同数据集而重复执行某个任务时,可以使用IntentService。不过要注意,同一段时间只处理一个数据集。
如果上面两者都不符合你的要求,那么可以试试 ThreadPoolExecutor ,它可以实现以下功能:
- 当资源准备好时自动执行任务
- 多个任务同时执行
当 ThreadPoolExecutor 的线程池中有一个线程为空闲时, ThreadPoolExecutor 会从一个队列(queue)中取出一个任务来执行。
线程间数据交换
因为新开的线程和处理界面的线程是分开的,于是在Android中使用线程会遇到一个问题:线程处理完的数据如何更新到界面上?
JAVA
你可以实现 BlockingQueue 接口。将其实例传入线程中,在线程里面使用put(Object)将数据放入队列,使用take()从队列中取出数据。
以下是官方的例子:
class Producer implements Runnable {
private final BlockingQueue queue;
Producer(BlockingQueue q) { queue = q; }
public void run() {
try {
while (true) { queue.put(produce()); }
} catch (InterruptedException ex) { ... handle ...}
}
Object produce() { ... }
}
class Consumer implements Runnable {
private final BlockingQueue queue;
Consumer(BlockingQueue q) { queue = q; }
public void run() {
try {
while (true) { consume(queue.take()); }
} catch (InterruptedException ex) { ... handle ...}
}
void consume(Object x) { ... }
}
class Setup {
void main() {
BlockingQueue q = new SomeQueueImplementation();
Producer p = new Producer(q);
Consumer c1 = new Consumer(q);
Consumer c2 = new Consumer(q);
new Thread(p).start();
new Thread(c1).start();
new Thread(c2).start();
}
}
Android
Android添加了几个类,用来处理数据交换。
- Handler : 线程之间交换数据的通道,用于接收和发送消息。如果在UI线程里创建Handler,则该Handler里的handleMessage(Message)方法会在UI线程里执行。
- Message : 线程A发送消息给线程B时,需要将消息封装到Message里面。通过在线程A内部执行线程B的Handler.sendMessage(Message)将Message传给线程B,此时会执行Handler的handleMessage(Message)方法。
- MessageQueue : 当发送多个Message时,为了不造成混乱,将这些Message组成一个队列,逐个处理。每个线程中只能有一个MessageQueue。这个队列由Looper管理。
- Looper : 管理MessageQueue。每次从队列里面取出一个Message,交给Handler处理。Handler处理完毕后再去队列中取出Message。
这些类太多了,有时候写起来麻烦。为了简化线程的写法,Android将上面那些封装起来,于是就有了AsyncTask。
Android对线程类的封装
AsyncTask有四个重要的方法:
- onPreExecute() :(UI线程)后台任务执行前在UI线程上做某些初始化操作
- doInBackground(Params ...) :(子线程)相当于Runnable的run()方法。可以在这个方法内计算进度,并调用publishProgress(Progress ...)传递给onProgressUpdate(Progress ...)方法
- onProgressUpdate(Progress ...) : (UI线程)用于更新界面上的进度信息
- onPostExecute(Result) :(UI线程)用于子线程处理完毕后对结果进行处理
AsyncTask是一个抽象类,需要创建一个类去继承它。AsyncTask有三个泛型参数,它们用途依次为:
- 执行任务所需要的参数
- 当前进度的单位
- 任务的结果
执行AsyncTask的execute()方法以开始任务。