- public class Concurrency extends Activity implements TaskListener<String> {
- private static final int TASK1 = 0;
- private static final int TASK2 = 1;
- private Task<String> task1, task2;
- private Callable<String> callable1 = new Callable<String>() {
- public String call() throws Exception {
- try {
- System.out.println(“task1 starting”);
- Thread.sleep(3000);
- } catch (InterruptedException e) {
- System.out.println(“task1 finished”);
- }
- return “task1 result”;
- };
- };
- private Callable<String> callable2 = new Callable<String>() {
- public String call() throws Exception {
- try {
- System.out.println(“task2 starting”);
- Thread.sleep(6000);
- } catch (InterruptedException e) {
- System.out.println(“task2 finished”);
- }
- return “task2 result”;
- };
- };
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- }
- @Override
- protected void onPause() {
- super.onPause();
- task1.unregisterCallback();
- task2.unregisterCallback();
- }
- @Override
- protected void onResume() {
- super.onResume();
- task1 = Task.getOrCreate(this, TASK1);
- task2 = Task.getOrCreate(this, TASK2);
- switch (task1.state()) {
- case NOT_STARTED:
- task1.run(this, callable1);
- break;
- case RUNNING:
- System.out.println(“task1 still running”);
- break;
- case COMPLETED:
- System.out.println(“task1 completed in background, result: “
- + task1.getResult());
- break;
- }
- switch (task2.state()) {
- case NOT_STARTED:
- task2.run(this, callable2);
- break;
- case RUNNING:
- System.out.println(“task2 still running”);
- break;
- case COMPLETED:
- System.out.println(“task2 completed in background, result: “
- + task2.getResult());
- break;
- }
- }
- @Override
- public void onTaskFinished(Task<String> task) {
- if (task.failed()) {
- System.err.println(“task” + task.getTaskId() + ” failed. Reason: “
- + task.getError().getMessage());
- } else {
- System.out.println(“task” + task.getTaskId() + ” finish handler: “
- + task.getResult());
- }
- }
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK) {
- Task.cancelAll(this);
- }
- return super.onKeyDown(keyCode, event);
- }
- }
Let’s walk through that code bit by bit. First, our activity defines two task objects, task1 and task2, with IDs TASK1 and TASK2. What those tasks are supposed to do is defined using two Callable objects, callable1 and callable2. Everything that happens inside the call() methods of those objects will be executed in a separate thread. We also have to tell those tasks what will happen should they complete. We do this by implementing the TaskListener interface, which currently only defines a single method: onTaskFinished(Task). We can check in that handler whether the task succeeded or not by calling its failed() method. If any exception was thrown during the execution of call(), this method will return true and the exception can be retrieved by calling its getError() method. Otherwise, the return value of getResult() is guranteed to be whatever you return in the callable. The Task class is generic: You instantiate it using the return type of the callable you pass to it. This ensures type safety when working with the result object. The same holds for TaskListener.
A closer look to onResume() reveals that everytime our activity is resumed, those task objects are either already in memory or will be created for us by calling Task.getOrCreate(). If we already started that task during a previous life-cycle of our activity, we can poll its status to check whether it has already completed or if it’s still running. The former is the case if the thread had terminated while our activity was paused or even completely destroyed; in that case, we can simply pick up whatever result the task came up with in the meantime. We also call Task.unregisterCallback() in onPause() in order to avoid being called back by the task when the activity goes poof (not doing so may result in memory leaks, as described above).
Right now, the activity will pick up any results of a task with a certain ID when being restarted, even when explicitly restarted by the user. If that’s not what you want, you can make a call to Task.cancelAll() in the key handler for the ‘back’ key. That way you can ensure that all tasks (or their results) are discarded when explicitly exiting the activity. You can also cancel a single task using task.cancel(). A canceled task will never post any result or error data back to the caller.
How it works
Internally, task state is maintained in a static hash mapping callers to their list of tasks. This assures that tasks are kept in memory as long as the Task class itself (or until they terminate of course). The Task class does all the locking, state updates and callback invocations for us; we can even create a ProgressDialog and assign it to a Task using Task.setProgressDialog(). The dialog will then automatically be displayed when the task starts running, and will close when the task finishes. A task (or more precisely: a list of tasks) is bound to its caller (the calling activity or service) by the caller’s ComponentName. That means, you can think of tasks being associated to a calling class, rather than a calling object. The task class will take care of removing tasks that finished and which have been posted back to the caller, but it will preserve all completed (uncanceled) tasks until the caller claims its results, in case the caller wasn’t reachable while the task was finishing.
You can download the Task module for free here.
Taken from brainflush blog
Leave a Reply:
You must be logged in to post a comment.