如何使用Future Pattern-2

如何使用Future Pattern-2

如何使用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);
    }
});

執行結果如下

程式碼