Android 中线程可分为主线程和子线程两类,其中主线程也就是UI线程,它的主要这作用就是运行四大组件、处理界面交互。子线程则主要是处理耗时任务,也是我们要重点分析的。
首先 Java 中的各种线程在 Android 里是通用的,Android 特有的线程形态也是基于 Java 的实现的,所以有必要先简单的了解下 Java 中的线程,本文主要包括以下内容:
Thread、Runnable
Callable、Future
线程池
IntentService、HandlerThread
AsyncTask
一、Thread、Runnable
在 Java 中要创建子线程可以直接继承Thread类,重写run()方法:
public class MyThread extends Thread {
@Override
public void run() {
}
}
// 启动线程
new MyThread().start();
或者实现Runnable接口,然后用Thread执行Runnable,这种方式比较常用:
public class MyRunnable implements Runnable {
@Override
public void run() {
}
}
// 启动线程
new Thread(new MyRunnable()).start();
简单的总结下:
Runnable 可以实现多个线程共享资源,可以参考网上卖票的例子
Runnable 可以避免 Java 中的单继承的限制
无法直接得到任务的执行结果
二、Callable、Future
Callable和Runnable类似,都可以用来处理具体的耗时任务逻辑的,但是但具体的差别在哪里呢?看一个小例子:
定义 MyCallable 实现了 Callable接口,和之前Runnable的run()方法对比下,call()方法是有返回值的哦,泛型就是返回值的类型:
public class MyCallable implements Callable {
@Override
public String call() throws Exception {
Log.e("call", "task start");
Thread.sleep(2000);
Log.e("call", "task finish");
return "hello thread";
}
}
一般会通过线程池来执行Callable(线程池相关内容后边会讲到),执行结果就是一个Future对象:
// 创建一个线程池
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
// 执行任务
Future result = cachedThreadPool.submit(new MyCallable());
try {
// 获取执行结果
Log.e("result", result.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
可以看到,通过线程池执行 MyCallable 对象返回了一个 Future 对象,取出执行结果。
Future是一个接口,从其内部的方法可以看出它提供了取消任务(有坑!!!)、判断任务是否完成、获取任务结果的功能:
public interface Future {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
Future接口有一个FutureTask实现类,同时FutureTask也实现了Runnable接口,并提供了两个构造函数:
public FutureTask(Callable callable) {
}
public FutureTask(Runnable runnable, V result) {
}
用FutureTask一个参数的构造函数来改造下上边的例子:
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
FutureTask futureTask = new FutureTask<>(new MyCallable());
cachedThreadPool.submit(futureTask);
try {
Log.e("result", futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
FutureTask内部有一个done()方法,代表Callable中的任务已经结束,可以用来获取执行结果:
FutureTask futureTask = new FutureTask(new MyCallable()){
@Override
protected void done() {
super.done();
try {
Log.e("result", get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
};
所以Future + Callable的组合可以更方便的获取子线程任务的执行结果,更好的控制任务的执行,主要的用法先说这么多了,其实AsyncTask内部也是类似的实现!
注意,Future并不能取消掉运行中的任务,这点在后边的AsyncTask解析中有提到。
三、线程池
Java 中线程池的具体的实现类是ThreadPoolExecutor,继承了Executor接口,这些线程池在 Android 中也是通用的。使用线程池的好处:
方便对线程进行管理
线程复用,避免大量创建、销毁线程带来的性能开销
可控制线程的最大并发数,避免线程之间抢占资源造成阻塞
常用的构造函数如下:
public ThreadPoolExecutor(
// 线程池的核心线程数,如果设置allowCoreThreadTimeOut属性为true,当闲置时间大于keepAliveTime会被终止掉,否则会一直存活不受keepAliveTime影响
int corePoolSize,
// 线程池能容纳的最大线程数,超过该数量的将会被阻塞
int maximum