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

Commit dde4cb9

Browse files
committed
Updated the readme to make it up-to-date with the latest development.
1 parent 99175a5 commit dde4cb9

File tree

1 file changed

+124
-41
lines changed

1 file changed

+124
-41
lines changed

README.md

Lines changed: 124 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,25 @@ dependencies {
1919
2. [How it works](#2-how-it-works)
2020
3. [Advanced usage](#3-advanced-usage)
2121
4. [FAQ](#4-faq)
22+
5. [ToDo](#5-to-do)
23+
24+
>**Why use this library?**
25+
26+
>*Always! its awesome!!!*
27+
28+
>This library is useful if you need to do stuff in the background which is heavily bound to the user-interface, like: Refreshing large lists from a database, loading an article from a network source or decoding an image. You need to use an additional library if you need: task scheduling, automatic retries, task persistence across reboots, task constrains (network availability) etc.
29+
30+
31+
2232

2333
## 1. Basic usage
2434
---
2535
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:
2636

2737
1. Create an implementation of the `Task` class;
28-
2. Execute the task using the `TaskManager` in an`Activity` which implements the `Callback` interface;
29-
3. Retain the task when configuration changes by overriding the `onStart()` method and calling the `TaskManager.attachListener()` method;
38+
2. Make your Activity extend the `TaskActivity` class (or for Fragments the `TaskFragment` class);
39+
3. Implement the `Callback` interface somewhere and execute the task using the `TaskManager`;
3040

31-
The Activity's `TaskManager` 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.
3241

3342
### 1.1 Creating the task
3443

@@ -59,17 +68,23 @@ private class ExampleTask extends Task<Integer, String> {
5968
}
6069
```
6170

62-
63-
### 1.2 Executing the task
64-
Before you can execute the `ExampleTask` you first need to get the current Activity's `TaskManager`. A `TaskManager` keeps references to tasks and executes them. You can obtain the current Activity's `TaskManager` using the `TaskManager.getActivityTaskManager()` method:
71+
### 1.2 Extending from the TaskActivity class
72+
The `TaskActivity` class is the most easy way to use this library, make sure your Activity extends from it and it wil take care of retaining all `Tasks` started by the Activity's `TaskManager`.
6573

6674
```java
67-
public TaskManager getTaskManager(){
68-
return TaskManager.getActivityTaskManager(getSupportFragmentManager());
75+
public class Main extends TaskActivity {
76+
6977
}
7078
```
7179

72-
Then you can execute the task using the `TaskManager.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.
80+
>**Help, I already extend some custom Activity implementation!**
81+
>Don't worry, you can easily add the `TaskActivity` behaviour to any Activity or Fragment by using the `TaskManagerLifeCycleProxy` class . Check out [this sample](#using-the-taskmanagerlifecycleproxy-to-mimic-the-taskactivity).
82+
83+
### 1.3 Execute the Task and receive callback
84+
Before you can execute the `ExampleTask` you first need to get the current Activity's `TaskManager`. A `TaskManager` keeps references to tasks and executes them. You can obtain the current Activity's `TaskManager` using the `TaskActivity.getTaskManager()` method.
85+
86+
Then you can execute the task using the `TaskManager.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. The `TaskManager` currently always executes tasks in parallel *(work in progress to make it a option)*.
87+
7388

7489
```java
7590
public class Main extends AppCompatActivity implements Task.Callback {
@@ -93,43 +108,57 @@ public class Main extends AppCompatActivity implements Task.Callback {
93108
}
94109
```
95110

96-
### 1.2 Retaining the task
97-
When the configuration changes (device rotates) the Activity's `TaskManager` will automatically remove the `Callback` listeners from all active tasks. This is needed otherwise the tasks could leak Activity, Fragment or other references.
98111

99-
> **In-depth:**
100-
> The `TaskManager` 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 `TaskManager` 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.
112+
>**Tip:**
113+
>You can also make your Fragment extend the `TaskFragment` class and use a Fragment to execute and retain your task in. It works exactly the same, but keep in mind that the `Callback` listeners are removed as soon as the Fragments stops (`onStop()`).
114+
101115

102-
To retain the task when your Activity is recreated you will need to re-attach a (new) `Callback` listener using the `TaskManager.attachListener()` method. The best place to do this is in the `onStart()` method right after the user-interface has been created.
116+
### 1.4 Retaining the task
117+
When the configuration changes (device rotates) the `TaskManager` will automatically remove the `Callback` listeners from all active tasks and retain all `Tasks`. Removing the `Callback` is needed otherwise the tasks could leak Activity, Fragment or other references.
103118

119+
> **In-depth:**
120+
> The `TaskManger` (or actually a internal class) will detect when the Activity stops (`onStop()`). and will automatically remove all `Callback` listeners when this happens. At this moment the user-interface has become *"unstable"*, this means that some user-interface functionality stops working. For example, the `FragmentManager` refuses at this point to add new Fragments because the Activity's `onSaveInstanceState()` method already has been called. If the `Callback` listener is not removed by the `TaskManager` 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.
121+
122+
Although tasks are automatically retained, you will still need to provide a new `Callback` listener for each `Task`. You can easily do this by implementing (overriding) the `TaskActivity` (or `TaskFragment`) `onPreAttachTask(Task)` method and return a `Callback` instance. At this point you can also use the `onPreAttachTask(Task)` method to restore the user-interface state according to the `Tasks` state. The `onPreAttachTask(Task)` method will be called for each task that isn't finished (didn't deliver).
104123

105124
```java
106-
@Override
107-
public void onStart(){
108-
super.onStart();
109-
//Re-attach the this Activity as listener for task
110-
getTaskManager().attachListener("activity-unique-tag", this);
125+
public class Main extends AppCompatActivity implements Task.Callback {
126+
127+
@Override
128+
public Task.Callback onPreAttach(Task<?, ?> task) {
129+
//Restore the user-interface based on the tasks state
130+
return this; //This Activity implements Task.Callback
131+
}
111132
}
112133
```
113134

135+
114136
## 2. How it works
115137
---
116-
####**Retaining tasks**
117-
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 `TaskManager.getActivityTaskManager()`. This fragment is from that point on bound to the Activity's life-cycle and holds an internal `TaskManager`. The fragment makes sure that the internal `TaskManager` removes all `Callback` listeners as soon as the Activity is stopping (`onStop()`).
138+
####**How are tasks retained?**
139+
Tasks are are stored in `FragmentManagers` which 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:
140+
141+
- `TaskActivity.getTaskManager()`;
142+
- `TaskFragment.getTaskManager()`;
143+
- `TaskManager.getActivityTaskManager()` (super-advanced usage);
144+
- `TaskManager.getFragmentTaskManager()` (super-advanced usage);
118145

119-
####**Task without callback finishes**
120-
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 `TaskManager.attachListener()` method in the `onStart()` method, then the listener will be fired and you need to be sure that the user-interface is ready.
146+
The *"no-ui-fragment"* is from that point on bound to the Activity's life-cycle and keeps track of all `TaskManager` instances. It also makes sure that all internal `TaskManagers` remove all `Callback` listeners as soon as the Activity is stopping (`onStop()`). It might also throw an exception if a `Fragment` `TaskManger` did not remove the `Callback` listeners, so that you (the developer) know you've messed up.
121147

122-
Only the `onPostExecute()` and `onCanceled()` methods will be redelivered, other methodes won't be redelivered.
148+
####**What happens when a Task without callback finishes?**
149+
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. This happens for example when the `onPreAttachTask(Task)` method returns, the newly provided `Callback` listener will be fired and you need to be sure that the user-interface is ready. It might also happen if you manually call one of the `TaskManager.attach()` methods (advanced usage).
123150

124-
####**Task and Callback life-cycle**
125-
A `Task` basically has four life-cycle methods:
151+
**Important:** Only the `onPostExecute()` and `onCanceled()` methods will be redelivered, other methodes won't be redelivered. You can restore a tasks progress using the `Task.getLastKnownProgress()` method.
152+
153+
####**What does the Task and Callback life-cycle look like?**
154+
A `Task` basically has four life-cycle methods *(its heavily based on Android's AsyncTask)*:
126155

127156
* `onPreExecute()` *[ui-thread]*
128157
* `doInBackground()` *[executor-thread]*
129158
* `onProgressUpdate()` *[ui-thread]*
130159
* `onPostExecute()` or `onCanceled()` *[ui-thread]*
131160

132-
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.
161+
A `Callback` listener has the same life-cycle methods as the`Task`. 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 exception of the `onPostExecute()` and `onCanceled()` methods which can be redelivered.
133162

134163

135164
## 3. Advanced usage
@@ -155,7 +184,7 @@ To get the tasks most recent progress update use the `getLastKnownProgress()` me
155184
If you need the `onProgressUpdated` and `onCanceled` callback methods you can implement the `AdvancedCallback` interface, which is an extension of the `Callback` interface.
156185

157186
####**TaskExecutor & Executor**
158-
You can also execute tasks without using a `TaskManager` this means that you are responsible for removing and setting the `Callback` listener. Executing tasks without using the `TaskManager` is handy when you don't need to get any feedback to the Activity's user-interface.
187+
You can also execute tasks without using a `TaskManager` this means that you are responsible for removing and setting the `Callback` listener. Executing tasks without using the `TaskManager` is handy when you don't perse need to get any feedback to the user-interface.
159188

160189
```java
161190
TaskExecutor.executeParallel(new ExampleTask());
@@ -174,32 +203,86 @@ You can also use a custom java `Executor` to execute tasks with:
174203
TaskExecutor.executeOnExecutor(new ExampleTask(), yourExecutor);
175204
```
176205

206+
####**Using the TaskManagerLifeCycleProxy to mimic the TaskActivity**
207+
If you already use some custom Activity or Fragment implementation you might not be able to use the `TaskActivity` or `TaskFragment` class. To overcome this problem you can implement the behaviour of the `TaskActivity` yourself using the `TaskManagerLifeCycleProxy` class.
208+
209+
Create a new `TaskManagerLifeCycleProxy` instance and let your Activity (or Fragment) implement the `TaskManagerProvider` interface. Override the`onStart()` and `onStop()` methods and proxy those together with the `getTaskManager()` method to the `TaskManagerLifeCycleProxy` instance.
210+
211+
```java
212+
public class MyBaseActivity extends SomeActivity implements TaskManagerProvider {
213+
214+
private TaskManagerLifeCycleProxy proxy = new TaskManagerLifeCycleProxy(this);
215+
216+
@Override
217+
protected void onStart() {
218+
super.onStart();
219+
proxy.onStart();
220+
}
221+
222+
@Override
223+
protected void onStop() {
224+
proxy.onStop();
225+
super.onStop();
226+
}
227+
228+
@Override
229+
public TaskManager getTaskManager() {
230+
return proxy.getTaskManager();
231+
}
232+
233+
@Override
234+
public Task.Callback onPreAttach(@NonNull Task<?, ?> task) {
235+
return null;
236+
}
237+
}
238+
```
239+
240+
241+
242+
243+
177244
## 4. FAQ
178245

179246
####**Why does the Task class still have the onPostExecute and onPreExecute etc. methods?**
180247

181-
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.
248+
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. It also gives a `Task` the change to modify it's state or store it's progress values, for example:
182249
```java
183-
private class VerySimpleTask extends Task<Void, Boolean> {
250+
private class VerySimpleTask extends Task<Integer, Integer> {
184251

185-
private final File fileToRemove;
186-
private final Context context;
252+
private final ArrayList<Integer> progressValues = new ArrayList<>();
187253

188-
public ExampleTask(String tag, Context context, File fileToRemove){
254+
public ExampleTask(String tag){
189255
super(tag);
190-
this.fileToRemove = fileToRemove;
191-
this.context = context.getApplicationContext();
192256
}
193257

194258
@Override
195-
protected String doInBackground() {
196-
return fileToRemove.delete();
259+
protected Boolean doInBackground() {
260+
for(int i = 0; i < 10; i++){
261+
publishProgress(i);
262+
SystemClock.sleep(500);
263+
}
264+
return 10;
197265
}
198266

199267
@Override
200-
protected void onPostExecute(){
201-
Toast.makeText(context, "Removed file: " + getResult(), Toast.LENGTH_SHORT).show();
268+
protected void onProgressUpdate(Integer value) {
269+
progressValues.add(value);
270+
}
271+
272+
public List<Integer> getProgressCache(){
273+
/**
274+
* This method is safe to call on the ui-thread because the
275+
* onProgressUpdate method is executed on the same thread.
276+
*/
277+
return progressValues;
202278
}
203-
204279
}
205-
```
280+
```
281+
282+
## 5. To-Do
283+
284+
*“As long as I am breathing, in my eyes, I am just beginning.”*
285+
286+
- Add custom Executors to the TaskManager;
287+
- Finish documentation;
288+
- Write real tests for the library besides having only a demo app;

0 commit comments

Comments
 (0)