Android Callable Future FutureTask學習

在上篇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 運行截圖
我們可以看到,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.

  1. FutureTask是一個可以取消的移步計算任務,其實現了Future,可以開始或取消一個任務、查看任務是否完成和得到任務的返回值。當任務沒有完成的時候,get方法會被阻塞。一旦任務完成,該任務不能重新啓動或取消(除非調用runAndReset)
  2. 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內容看出來,見下圖。

FutureTaskDemo

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章