在上篇Android 线程与线程池 Thread&ThreadPool,我们介绍了线程Thread及线程池的使用。我们知道,当线程执行完毕的时候,该线程会自动销毁。但是,其却不会通知任务的执行者。比如,我们新建一个线程执行任务,当任务执行完毕的时候,我们是不知道的,而且,该任务可能会有返回值。那么,我们应该怎么办呢?Callable就登场啦。
1. Callable
Callable是什么呢?Develop是这样介绍的,
A task that returns a result and may throw an exception. Implementors define a single method with no arguments called call.
The Callable interface is similar to Runnable, in that both are designed for classes whose instances are potentially executed by another thread. A Runnable, however, does not return a result and cannot throw a checked exception.
简单翻译一下,Callable可以执行一个有返回值的任务,使用的时候,必须实现其call方法。Callable接口和Runnable接口类似,其都是给线程设计(Callable的call方法和Runnable的run方法会在线程中执行),然而,一个Runnable却没有返回值,而且不会抛出异常。
Callable的代码是这样的,其定义了一个函数接口call,该函数的返回类型是V,也就是泛型,我们实现的时候,必须指定其类型。
public interface Callable<V> {
V call() throws Exception;
}
比如,我们可以这样使用 ,
class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int result = 1;
Log.i(TAG, "Callable Task begin! Thread id: " + Thread.currentThread().getId());
Thread.sleep(1000);
Log.i(TAG, "Callable Task finish! Thread id: " + Thread.currentThread().getId());
return result;
}
}
在执行任务的时候,我们将Callable的实例加入到线程池中,在下面的例子中,我们新建了一个单一线程池。
ExecutorService threadPool = Executors.newSingleThreadExecutor();
Future<Integer> future = threadPool.submit(new MyCallable());
try {
int result = future.get();
Log.i(TAG, "callable result: " + result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
运行截图如下,
我们可以看到,Callable实例的确在新的线程中运行,而且,我们在任务执行完的时候,可以得到任务的返回值。线程池的submit的返回类型是Future。我们可以通过submit返回的Future实例得到任务的返回值。
那么Future是什么类型?
2. Future
Future是一个接口,其在Developer上是这样描述的,
A Future represents the result of an asynchronous computation. Methods are provided to check if the computation is complete, to wait for its completion, and to retrieve the result of the computation. The result can only be retrieved using method get when the computation has completed, blocking if necessary until it is ready. Cancellation is performed by the cancel method. Additional methods are provided to determine if the task completed normally or was cancelled. Once a computation has completed, the computation cannot be cancelled. If you would like to use a Future for the sake of cancellability but not provide a usable result, you can declare types of the form Future and return null as a result of the underlying task.
简单翻译下,Future反映了异步计算的结果。其提供了get方法来检查该异步计算是否完成、等待完成和得到返回值。可以通过get方法得到返回值,当任务没有结束的时候,该方法会阻塞线程知道任务结束。
所以我们不能在主线程中执行get方法,因为其可能阻塞主线程,导致ANR。
cancel方法可以取消该任务,如果你想提供取消的功能,而不需要有效的返回值,那么,你可以用Future, 不需要声明返回类型,并且在任务中返回null。
例如下面的代码,
ExecutorService threadPool = Executors.newSingleThreadExecutor();
Future future = threadPool.submit(new NoRetCallable());
class NoRetCallable implements Callable<Void> {
@Override
public Void call() throws Exception {
Log.i(TAG, "Callable Task begin! Thread id: " + Thread.currentThread().getId());
Thread.sleep(1000);
Log.i(TAG, "Callable Task finish! Thread id: " + Thread.currentThread().getId());
return null;
}
}
cancel使用的时候,是有参数的,参数是用来判断,是否取消正在执行的任务,这样会中断正在执行的任务。其返回值是是否取消成功。
Future还提供了以下接口函数,
boolean isCancelled(); 判断该任务是否取消
boolean isDone();判断该任务是否完成
V get(long timeout, TimeUnit unit);得到返回值,不过会等待,最长等待时间由参数给出。若超出最长等待时间任务还未完成,则会抛出TimeOut异常。
3. RunnableFuture
其是一个接口,同时继承了Runnable和Future,使得其可以被执行,也可以得到其返回值。
A Future that is Runnable. Successful execution of the run method causes completion of the Future and allows access to its results.
4. FutureTask
FutureTask实现了RunnableFuture接口,Developer这样描述,
A cancellable asynchronous computation. This class provides a base implementation of Future, with methods to start and cancel a computation, query to see if the computation is complete, and retrieve the result of the computation. The result can only be retrieved when the computation has completed; the get methods will block if the computation has not yet completed. Once the computation has completed, the computation cannot be restarted or cancelled (unless the computation is invoked using runAndReset()).
A FutureTask can be used to wrap a Callable or Runnable object. Because FutureTask implements Runnable, a FutureTask can be submitted to an Executor for execution.
- FutureTask是一个可以取消的移步计算任务,其实现了Future,可以开始或取消一个任务、查看任务是否完成和得到任务的返回值。当任务没有完成的时候,get方法会被阻塞。一旦任务完成,该任务不能重新启动或取消(除非调用runAndReset)
- FutureTask可以封装Callable或者Runnable对象,由于FutureTask实现了Runnable接口,一个FutureTask可以提交给Exectutor来执行。
一个简单的例子如下,
ExecutorService threadPool = Executors.newSingleThreadExecutor();
MyCallable mCallable = new MyCallable();
futureTask = new FutureTask<Integer>(mCallable) {
@Override
protected void done() {
Log.i(TAG, "FutureTask is done!");
try {
Integer result = futureTask.get();
Log.i(TAG, "result: " + result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
Log.i(TAG, "done in current Thread Id :" + Thread.currentThread().getId());
}
};
threadPool.execute(futureTask);
当FutureTask结束的时候,会调用done方法,所以我们可以重写done方法。在该方法中,我们可以得到返回值,然后进行一些其他操作,但是要注意,done方法和FutureTask在一个线程中,所以我们不能在done方法访问UI。这一点我们可以通过Log内容看出来,见下图。