在如何使用Future Pattern-2中, 我們示範了Future Pattern,
並且使用了InvokeAll跟InvokeAny兩種方式,
但是會想說怎麼沒有一種方式可以任務處理完就先回來,
其實有! 你可以利用ExecutorCompletionService來達成這樣的實做。
首先建立一個Button跟一個TextView,
用來模擬傳送Task到遠端, 然後結果回來顯示在TextView上面。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<Button
android:id="@+id/start_task"
android:text="start task"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<TextView
android:layout_below="@id/start_task"
android:id="@+id/result"
android:text="Hello World!"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
接著我們在Java檔案內進行操作
public class MainActivity extends AppCompatActivity {
private Button startTask;
private TextView result;
private CompletionService<String> completionService;
private ExecutorService executorService;
private StringBuffer strBuffer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
initView();
}
private void initData(){
executorService = Executors.newCachedThreadPool();
completionService = new ExecutorCompletionService<String>(executorService);
strBuffer = new StringBuffer();
}
private void initView(){
startTask = (Button) findViewById(R.id.start_task);
result = (TextView) findViewById(R.id.result);
startTask.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ExecutorService singleThread = Executors.newSingleThreadExecutor();
singleThread.submit(new Runnable() {
@Override
public void run() {
strBuffer.delete(0, strBuffer.length());
runOnUiThread(new Runnable() {
@Override
public void run() {
result.setText(strBuffer.toString());
}
});
for(int i = 0; i < 10; i++) {
completionService.submit(getTask(i));
}
for(int i = 0; i < 10; i++) {
try {
String s = completionService.take().get();
strBuffer.append(s + "\n");
runOnUiThread(new Runnable() {
@Override
public void run() {
result.setText(strBuffer.toString());
}
});
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
});
}
});
}
private Callable<String> getTask(final int index){
return new Callable<String>() {
@Override
public String call() throws Exception {
int r = (int)(Math.random() * 3 + 1);
Thread.sleep(r * 1000);
return "task " + index;
}
};
}
}
由上面可以看到, 我們宣告了一個newCachedThreadPool,
接著把它塞入到ExecutorCompletionService,
這樣就可以批次處理多個Task。
executorService = Executors.newCachedThreadPool();
completionService = new ExecutorCompletionService<String>(executorService);
接著我們要讓每個Task都有自己的編號,
因此在呼叫Callable的時候, 會帶入一個index,
讓它隨著String回來, 而利用Thread.sleep()方法,
隨機睡個幾秒, 模擬長任務的操作。
private Callable<String> getTask(final int index){
return new Callable<String>() {
@Override
public String call() throws Exception {
int r = (int)(Math.random() * 3 + 1);
Thread.sleep(r * 1000);
return "task " + index;
}
};
}
最後透過completionService將任務送出去,
透過take()的方式把每個完成的任務拿回來,
呈現在TextView上面。
for(int i = 0; i < 10; i++) {
completionService.submit(getTask(i));
}
for(int i = 0; i < 10; i++) {
try {
String s = completionService.take().get();
strBuffer.append(s + "\n");
runOnUiThread(new Runnable() {
@Override
public void run() {
result.setText(strBuffer.toString());
}
});
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
操作結果如下
程式碼