Skip to content
This repository was archived by the owner on Dec 17, 2020. It is now read-only.

Commit c22813a

Browse files
author
Rolf Smit
committed
Updated readme.
1 parent 05f34dc commit c22813a

File tree

1 file changed

+148
-34
lines changed

1 file changed

+148
-34
lines changed

README.md

Lines changed: 148 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,37 @@
11
# Android-Retainable-Tasks
2-
An easy to use micro-library for easy asynchronous background tasking with callbacks to the UI. This library is based on the Android AsyncTask implementation but with support for configuration changes (orientation) and callbacks to the UI. It also support custom Executors.
2+
Android-Retainable-Tasks is an easy to use mini-library for easy asynchronous background tasking with callback support to the UI. This library is based on the Android `AsyncTask` implementation but with support for retaining tasks and therefor surviving configuration changes (orientation).
33

4-
Usage
5-
--------
6-
Extend the `Task` class to build a custom task. The `Task` class is heavily based on the default Android `AsyncTask` class and you will need to override the `doInBackground` method. Note that the `Task` class doesn't come with a generic type for input parameters, you should provide input when constructing the `Task` instance (using the constructor for example).
4+
1. [Basic usage](#1-basic-usage)
5+
2. [How it works](#2-how-it-works)
6+
3. [Advanced usage](#3-advanced-usage)
7+
4. [FAQ](#4-faq)
8+
9+
## 1. Basic usage
10+
---
11+
In order to, execute a task which modifies the user-interface and to retain it across configuration changes you will need to do three things:
12+
13+
1. Create an implementation of the `Task` class;
14+
2. Execute the task using the `TaskHandler` in an`Activity` which implements the `Callback` interface;
15+
3. Retain the task when configuration changes by overriding the `onStart()` method and calling the `TaskHandler.attachListener()` method;
16+
17+
The Activity's `TaskHandler` makes sure that tasks can be retained across configuration changes and is responsible for removing the `Callback` listener when the Activity's user-interface is no longer valid. You are however responsible for re-attaching a new `Callback` listener when the Activity 's user-interface is restarted.
18+
19+
### 1.1 Creating the task
20+
21+
You will need to extend the `Task` class to create a custom task. The `Task` class is heavily based on the default Android `AsyncTask` class and you will need to override at least the `doInBackground` method and provide an appropriate constructor.
22+
23+
>**Note:**
24+
>A`Task` doesn't come with a generic type for input parameters like the Android `AsyncTask`, instead you should provide input when constructing the `Task` (using the constructor for example).
25+
26+
**Example:**
727

828
```java
929
private class ExampleTask extends Task<Integer, String> {
1030

31+
public ExampleTask(String tag){
32+
super(tag);
33+
}
34+
1135
protected String doInBackground() {
1236
for(int i = 0; i < 100; i++) {
1337
if(isCancelled()){
@@ -21,56 +45,146 @@ private class ExampleTask extends Task<Integer, String> {
2145
}
2246
```
2347

24-
Then execute the task using the TaskExecutor. You can also execute tasks using a custom Executor.
48+
49+
### 1.2 Executing the task
50+
Before you can execute the `ExampleTask` you first need to get the current Activity's `TaskHandler`. A `TaskHandler` keeps references to tasks and executes them. You can obtain the current Activity's `TaskHandler` using the `TaskHandler.getActivityTaskHandler()` method:
2551

2652
```java
27-
TaskExecutor.executeParallel(new ExampleTask());
53+
public TaskHandler getTaskHandler(){
54+
return TaskHandler.getActivityTaskHandler(getSupportFragmentManager());
55+
}
2856
```
2957

58+
Then you can execute the task using the `TaskHandler.execute()` method. This method needs two arguments, the task to execute and a `Callback` listener to send feedback to. Preferably your activity implements the `Callback` interface, but this isn't necessarily needed.
59+
3060
```java
31-
TaskExecutor.executeSerial(new ExampleTask());
61+
public class Main extends AppCompatActivity implements Task.Callback {
62+
63+
@Override
64+
public void onClick(View view){
65+
ExampleTask task = new ExampleTask("activity-unique-tag");
66+
getTaskHandler().execute(task, this);
67+
}
68+
69+
@Override
70+
public void onPreExecute(Task<?, ?> task) {
71+
//Task started
72+
}
73+
74+
@Override
75+
public void onPostExecute(Task<?, ?> task) {
76+
//Task finished
77+
Toast.makeText(this, "Task finished", Toast.LENGTH_SHORT).show();
78+
}
79+
}
3280
```
3381

82+
### 1.2 Retaining the task
83+
When the configuration changes (device rotates) the Activity's `TaskHandler` will automatically remove the `Callback` listeners from all active tasks. This is needed otherwise the tasks could leak Activity, Fragment or other references.
84+
85+
> **In-depth:**
86+
> The `TaskHandler` will automatically remove the `Callback` listeners when the Activity is stopping (`onStop()`). At this moment the user-interface has become *"unstable"*, for example, when this happens the FragmentManager refuses to add new Fragments because the Activity's `onSaveInstanceState()` method has already been called. If the `Callback` listener is not removed by the `TaskHandler` before this point, then a `DialogFragment.show()` call will throw an exception when called in the `onPostExecute()` method. This is why the `Callback` listeners are removed when the Activity stops.
87+
88+
To retain the task when your Activity is recreated you will need to re-attach a (new) `Callback` listener using the `TaskHandler.attachListener()` method. The best place to do this is in the `onStart()` method right after the user-interface has been created.
89+
90+
3491
```java
35-
//Alias for calling executeParallel
36-
TaskExecutor.execute(new ExampleTask());
92+
@Override
93+
public void onStart(){
94+
super.onStart();
95+
//Re-attach the this Activity as listener for task
96+
getTaskHandler().attachListener("activity-unique-tag", this);
97+
}
3798
```
3899

39-
Using the TaskExecutor directly (like in the above example) means that you won't get any feedback to the UI.
100+
## 2. How it works
101+
---
102+
**Retaining tasks**
103+
Tasks retained using the described method are stored in a *"no-ui-fragment"* this fragment retained across configuration changes and is added to your Activity's `FragmentManager` the first time you call `TaskHandler.getActivityTaskHandler()`. This fragment is from that point on bound to the Activity's life-cycle and holds an internal `TaskHandler`. The fragment makes sure that the internal `TaskHandler` removes all `Callback` listeners as soon as the Activity is stopping (`onStop()`).
104+
105+
**Task without callback finishes**
106+
When a Task doesn't have a `Callback` listener to deliver it's results to it will skip the delivery and redeliver the results as soon as a new listener is attached. If you call the `TaskHandler.attachListener()` method in the `onStart()` method, then the listener will be fired and you need to be sure that the user-interface is ready.
107+
108+
Only the `onPostExecute()` and `onCanceled()` methods will be redelivered, other methodes won't be redelivered.
40109

41-
The preferred way to use a the `Task` class is by using the `TaskHandler.getActivityTaskHandler(FragmentManger)` method which returns a TaskHandler. The TaskHandler which is returned is loosely coupled to the Activity life-cycle using a internal Fragment which is retained across configuration changes. The `TaskHandler` makes sure you can retain your tasks across configuration changes.
110+
**Task and Callback life-cycle**
111+
A `Task` basically has four life-cycle methods:
112+
113+
* `onPreExecute()` *[ui-thread]*
114+
* `doInBackground()` *[executor-thread]*
115+
* `onProgressUpdate()` *[ui-thread]*
116+
* `onPostExecute()` or `onCanceled()` *[ui-thread]*
117+
118+
A `Callback` listener has the same life-cycle methods as the`Task` and reflects those methods to, for example, an Activity. All `Callback` methods are executed on the user interface thread. When a `Callback` listener is attached to the task, both the `Callback` and the `Task` methods will be called. But when the listener is detached from the task only the tasks methods will be called. With the exception of the `onPostExecute()` and `onCanceled()` methods which can be redelivered.
119+
120+
121+
## 3. Advanced usage
122+
---
123+
Besides the basics there are some more advanced API's you will probably need.
124+
125+
**Getting task results**
126+
Unlike the default Android `AsyncTask` implementation you don't get `Task` results as a parameter, instead you will need to call the `Task.getResult()` method, which returns the tasks result.
127+
128+
**Getting the tasks current state**
129+
The Android `AsyncTask` API provides the `AsyncTask.getStatus()` method which returns an enum value which can be used to determinate the tasks current state. Instead of using that method combined with an enum you can use on of the following methods:
130+
131+
* `isFinished()`
132+
* `isRunning()`
133+
* `isReady()`
134+
* `isResultDelivered()`
135+
* `isCanceled()`
136+
137+
**Getting the tasks last progress update**
138+
To get the tasks most recent progress update use the `getLastKnownProgress()` method.
139+
140+
**AdvancedCallback**
141+
If you need the `onProgressUpdated` and `onCanceled` callback methods you can implement the `AdvancedCallback` interface, which is an extension of the `Callback` interface.
142+
143+
**TaskExecutor & Executor**
144+
You can also execute tasks without using a `TaskHandler` this means that you are responsible for removing and setting the `Callback` listener. Executing tasks without using the `TaskHandler` is handy when you don't need to get any feedback to the Activity's user-interface.
42145

43146
```java
44-
public class Main extends Activity implements Task.Callback, View.OnClickListener {
45-
private static final String TASK_DEMO = "demo-task";
147+
TaskExecutor.executeParallel(new ExampleTask());
148+
```
149+
```java
150+
TaskExecutor.executeSerial(new ExampleTask());
151+
```
152+
```java
153+
//Alias for calling executeParallel
154+
TaskExecutor.execute(new ExampleTask());
155+
```
46156

47-
//... onCreate etc.
157+
You can also use a custom java `Executor` to execute tasks with:
48158

49-
public TaskHandler getTaskHandler(){
50-
return TaskHandler.getActivityTaskHandler(getSupportFragmentManager());
51-
}
159+
```java
160+
TaskExecutor.executeOnExecutor(new ExampleTask(), yourExecutor);
161+
```
52162

53-
protected void onStart() {
54-
super.onStart();
55-
//Attach this activity as listener for the Task identified by tag TASK_DEMO
56-
getTaskHandler().attachListener(TASK_DEMO, this);
57-
}
163+
## 4. FAQ
58164

59-
public void onClick(View v) {
60-
//Create a new task and execute it through the TaskHandler,
61-
//making this activity instance the tasks listener.
62-
ExampleTask task = new ExampleTask(TASK_DEMO);
63-
getTaskHandler().execute(task, this);
64-
}
165+
**Why does the Task class still have the onPostExecute and onPreExecute etc. methods?**
166+
Although the `Callback` interface provides these methods sometimes you don't need any callback to the Activity's user-interface, at that moment the Task methods come in handy.
167+
```java
168+
private class VerySimpleTask extends Task<Void, Boolean> {
65169

66-
public void onPreExecute(Task<?, ?> task) {
67-
//Task started
170+
private final File fileToRemove;
171+
private final Context context;
172+
173+
public ExampleTask(String tag, Context context, File fileToRemove){
174+
super(tag);
175+
this.fileToRemove = fileToRemove;
176+
this.context = context.getApplicationContext();
68177
}
69178

70-
public void onPostExecute(Task<?, ?> task) {
71-
//Task finished
72-
//This method will be called even after a rotation change, but within the new Activity instance)
73-
Toast.makeText(this, "Task finished", Toast.LENGTH_SHORT).show();
179+
@Override
180+
protected String doInBackground() {
181+
return fileToRemove.delete();
74182
}
183+
184+
@Override
185+
protected void onPostExecute(){
186+
Toast.makeText(context, "Removed file: " + getResult(), Toast.LENGTH_SHORT).show();
187+
}
188+
75189
}
76190
```

0 commit comments

Comments
 (0)