在如何使用Future Pattern中用Java的方式來實作Future Pattern, 其實Android也有提供這個機制。
Android的Future是被實作在Executor內部, 因此只要透過ExecutorService就可以使用,
而Future有兩個參數, 一個是InvokeAll另外一個是InvokeAny,
在如何使用Future Pattern內有說明,
當InvokeAll會等待全部任務完成後, 將結果回傳。
而InvokeAny則只有要一個任務完成以後, 則會結束其他任務, 只回傳完成的該任務。
宣告兩個Button跟一個TextView, 一個處理InvokeAll另外一個處理InvokeAny,
將結果顯示在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/invoke_all"
android:text="Invoke All"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:layout_toRightOf="@id/invoke_all"
android:id="@+id/invoke_any"
android:text="Invoke Any"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:layout_below="@id/invoke_all"
android:id="@+id/result"
android:text="Hello!"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
主要程式
public class MainActivity extends AppCompatActivity {
private Button sendInvokeAll;
private Button sendInvokeAny;
private TextView result;
private List<Callable<String>> tasks;
private StringBuffer strBuffer;
private ExecutorService executorService;
private final static int INVOTE_ALL = 0;
private final static int INVOTE_ANY = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
initView();
}
private void initView(){
sendInvokeAll = (Button) findViewById(R.id.invoke_all);
sendInvokeAny = (Button) findViewById(R.id.invoke_any);
result = (TextView) findViewById(R.id.result);
sendInvokeAll.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
executeTask(INVOTE_ALL);
}
});
sendInvokeAny.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
executeTask(INVOTE_ANY);
}
});
}
private void initData(){
tasks = new ArrayList<>();
strBuffer = new StringBuffer();
executorService = Executors.newCachedThreadPool();
}
private String emulateGetStringFromServer(int index){
String string = "task ";
try {
int r = (int)(Math.random() * 3 + 1);
Thread.sleep(r * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return string + index + " is done.";
}
private void executeTask(final int flag){
new Thread(new Runnable() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
tasks.clear();
strBuffer.delete(0, strBuffer.length());
strBuffer.append("Loding..." + "\n");
result.setText(strBuffer.toString());
}
});
tasks.add(new Callable<String>() {
@Override
public String call() throws Exception {
return emulateGetStringFromServer(1);
}
});
tasks.add(new Callable<String>() {
@Override
public String call() throws Exception {
return emulateGetStringFromServer(2);
}
});
try {
if(flag == INVOTE_ALL) {
List<Future<String>> futures = executorService.invokeAll(tasks);
for(int i = 0; i < futures.size(); i++){
strBuffer.append(futures.get(i).get() + "\n");
}
} else {
String taskStr = executorService.invokeAny(tasks);
strBuffer.append(taskStr);
}
runOnUiThread(new Runnable() {
@Override
public void run() {
result.setText(strBuffer.toString());
}
});
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}).start();
}
}
在上面可以看到, 我們透過一個List內包含了Future的物件,
List<Future<String>> futures = executorService.invokeAll(tasks);
然後實作Callable的任務, 透過傳入的不同參數, 來代表不同任務的id,
tasks.add(new Callable<String>() {
@Override
public String call() throws Exception {
return emulateGetStringFromServer(1);
}
});
這邊可以看到, 其實我們是用Thread.sleep的方式來模擬執行長任務的效果,
時間長短會根據Random後的結果。
private String emulateGetStringFromServer(int index){
String string = "task ";
try {
int r = (int)(Math.random() * 3 + 1);
Thread.sleep(r * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return string + index + " is done.";
}
當我們執行第一個按鈕, 則會把全部任務跑完才顯示,
sendInvokeAll.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
executeTask(INVOTE_ALL);
}
});
第二個按鈕由於是隨機的, 因此只要有任務完成, 就會回傳字串,
可以看到有時候是任務1完成, 有時候是任務2完成。
sendInvokeAny.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
executeTask(INVOTE_ANY);
}
});
執行結果如下
程式碼