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

Commit e82d179

Browse files
committed
Started on writing a new README with seperated documentation.
1 parent 0c6480f commit e82d179

File tree

5 files changed

+357
-262
lines changed

5 files changed

+357
-262
lines changed

DOCUMENTATION.md

Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
# Android-Retainable-Tasks
2+
[ ![Download](https://api.bintray.com/packages/rolf-smit/maven/android-retainable-tasks/images/download.svg) ](https://bintray.com/rolf-smit/maven/android-retainable-tasks/_latestVersion)
3+
[![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-Android%20Retainable%20Tasks-brightgreen.svg?style=flat)](http://android-arsenal.com/details/1/3236)
4+
5+
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 therefore surviving configuration changes (orientation).
6+
7+
*Key features:*
8+
9+
- Light weight & Simple API
10+
- Annotation support
11+
- Same Task API on all Android versions, based on the Nougat AsyncTask implementation.
12+
- Supports the [LifecycleObserver](https://developer.android.com/reference/android/arch/lifecycle/LifecycleObserver.html) class from the [Android Architecture Lifecycle](https://developer.android.com/reference/android/arch/lifecycle/Lifecycle.html) library;
13+
- Supports API 14+ <sub>(with or without the Android support libraries)</sub>
14+
15+
**Add it to your project**
16+
17+
Android-Retainable-Tasks is available on jCenter, just add the following compile dependency to your modules build.gradle file.
18+
19+
```groovy
20+
dependencies {
21+
// The library dependency
22+
compile 'org.neotech.library:android-retainable-tasks:1.0.0-alpha-1'
23+
24+
// Only needed if you want to use annotations
25+
annotationProcessor 'org.neotech.library:android-retainable-tasks-compiler:1.0.0-alpha-1'
26+
}
27+
```
28+
29+
**Index**
30+
31+
1. [Basic usage](#1-basic-usage)
32+
2. [How it works](#2-how-it-works)
33+
3. [Advanced usage](#3-advanced-usage)
34+
4. [FAQ](#4-faq)
35+
5. [ToDo](#5-to-do)
36+
37+
>**Why use this library?**
38+
>*Always! its awesome!*
39+
>No, obviously you should not always use this library. This library is useful when 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, all those things are quit tightly bound to the user-interface. You need to use an additional library if you need: task scheduling, automatic retries, task persistence across reboots, task constrains (network availability) etc.
40+
41+
42+
## 1. Basic usage
43+
---
44+
In order to, execute a task which modifies the user-interface and to retain it across configuration changes you will need to do four things:
45+
46+
1. Create an implementation of the `Task` class;
47+
2. Make your Activity extend the `TaskActivityCompat` class (or for Fragments the `TaskFragmentCompat` class);
48+
3. Implement the `Callback` interface somewhere and execute the task using the `TaskManager`;
49+
4. Retain the task using the `TaskActivityCompat.onPreAttach()` method.
50+
51+
### 1.1 Creating the task
52+
53+
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.
54+
55+
>**Note:**
56+
>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).
57+
58+
**Example:**
59+
60+
```java
61+
private class ExampleTask extends Task<Integer, String> {
62+
63+
public ExampleTask(String tag){
64+
super(tag);
65+
}
66+
67+
protected String doInBackground() {
68+
for(int i = 0; i < 100; i++) {
69+
if(isCancelled()){
70+
break;
71+
}
72+
SystemClock.sleep(50);
73+
publishProgress(i);
74+
}
75+
return "Result";
76+
}
77+
}
78+
```
79+
80+
### 1.2 Extending from the TaskActivityCompat class
81+
The `TaskActivityCompat` 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`.
82+
83+
```java
84+
public class Main extends TaskActivityCompat {
85+
86+
}
87+
```
88+
89+
>**Help, I already extend some custom Activity implementation!**
90+
>Don't worry, you can easily add the `TaskActivityCompat` behaviour to any Activity or Fragment by using the `TaskManagerLifeCycleProxy` class . Check out [this sample](#using-the-taskmanagerlifecycleproxy-to-mimic-the-taskactivity).
91+
92+
### 1.3 Execute the Task and receive callback
93+
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 `TaskActivityCompat.getTaskManager()` method.
94+
95+
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)*.
96+
97+
98+
```java
99+
public class Main extends TaskActivityCompat implements Task.Callback {
100+
101+
@Override
102+
public void onClick(View view){
103+
ExampleTask task = new ExampleTask("activity-unique-tag");
104+
getTaskManager().execute(task, this);
105+
}
106+
107+
@Override
108+
public void onPreExecute(Task<?, ?> task) {
109+
//Task started
110+
}
111+
112+
@Override
113+
public void onPostExecute(Task<?, ?> task) {
114+
//Task finished
115+
Toast.makeText(this, "Task finished", Toast.LENGTH_SHORT).show();
116+
}
117+
}
118+
```
119+
120+
121+
>**Tip:**
122+
>You can also make your Fragment extend the `TaskFragmentCompat` 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()`).
123+
124+
125+
### 1.4 Retaining the task
126+
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.
127+
128+
> **In-depth:**
129+
> 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.
130+
131+
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 `TaskActivityCompat` (or `TaskFragmentCompat`) `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).
132+
133+
```java
134+
public class Main extends TaskActivityCompat implements Task.Callback {
135+
136+
@Override
137+
public Task.Callback onPreAttach(Task<?, ?> task) {
138+
//Restore the user-interface based on the tasks state
139+
return this; //This Activity implements Task.Callback
140+
}
141+
}
142+
```
143+
144+
145+
## 2. How it works
146+
---
147+
####**How are tasks retained?**
148+
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:
149+
150+
- `getTaskManager()` in the following classes:
151+
- `TaskActivity` & `TaskActivityCompat`
152+
- `TaskFragment` & `TaskFragmentCompat`
153+
- `TaskManagerLifeCycleProxy.getTaskManager()`
154+
- `TaskManager.getActivityTaskManager()` <sub>(super-advanced usage)</sub>
155+
- `TaskManager.getFragmentTaskManager()` <sub>(super-advanced usage)</sub>
156+
157+
Essentially any time you request a `TaskManager`.
158+
159+
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.
160+
161+
####**What happens when a Task without callback finishes?**
162+
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).
163+
164+
**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.
165+
166+
####**What does the Task and Callback life-cycle look like?**
167+
A `Task` basically has four life-cycle methods *(its heavily based on Android's AsyncTask)*:
168+
169+
* `onPreExecute()` *[ui-thread]*
170+
* `doInBackground()` *[executor-thread]*
171+
* `onProgressUpdate()` *[ui-thread]*
172+
* `onPostExecute()` or `onCanceled()` *[ui-thread]*
173+
174+
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.
175+
176+
177+
## 3. Advanced usage
178+
---
179+
Besides the basics there are some more advanced API's you will probably need.
180+
181+
####**Getting task results**
182+
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.
183+
184+
####**Getting the tasks current state**
185+
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:
186+
187+
* `isFinished()`
188+
* `isRunning()`
189+
* `isReady()`
190+
* `isResultDelivered()`
191+
* `isCanceled()`
192+
193+
####**Getting the tasks last progress update**
194+
To get the tasks most recent progress update use the `getLastKnownProgress()` method.
195+
196+
####**AdvancedCallback**
197+
If you need the `onProgressUpdated` and `onCanceled` callback methods you can implement the `AdvancedCallback` interface, which is an extension of the `Callback` interface.
198+
199+
####**TaskExecutor & Executor**
200+
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.
201+
202+
```java
203+
TaskExecutor.executeParallel(new ExampleTask());
204+
```
205+
```java
206+
TaskExecutor.executeSerial(new ExampleTask());
207+
```
208+
```java
209+
//Alias for calling executeParallel
210+
TaskExecutor.execute(new ExampleTask());
211+
```
212+
213+
You can also use a custom java `Executor` to execute tasks with:
214+
215+
```java
216+
TaskExecutor.executeOnExecutor(new ExampleTask(), yourExecutor);
217+
```
218+
219+
####**Using the TaskManagerLifeCycleProxy to mimic the TaskActivity**
220+
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.
221+
222+
Create a new `TaskManagerLifeCycleProxy` instance and let your Activity (or Fragment) implement the `TaskManagerOwner` interface. Override the`onStart()` and `onStop()` methods and proxy those together with the `getTaskManager()` method to the `TaskManagerLifeCycleProxy` instance.
223+
224+
```java
225+
public class MyBaseActivity extends SomeActivity implements TaskManagerOwner {
226+
227+
private TaskManagerLifeCycleProxy proxy = new TaskManagerLifeCycleProxy(this);
228+
229+
@Override
230+
protected void onStart() {
231+
super.onStart();
232+
proxy.onStart();
233+
}
234+
235+
@Override
236+
protected void onStop() {
237+
proxy.onStop();
238+
super.onStop();
239+
}
240+
241+
@Override
242+
public TaskManager getTaskManager() {
243+
return proxy.getTaskManager();
244+
}
245+
246+
@Override
247+
public Task.Callback onPreAttach(@NonNull Task<?, ?> task) {
248+
return null;
249+
}
250+
}
251+
```
252+
253+
## 4. FAQ
254+
255+
####**Why does the Task class still have the onPostExecute and onPreExecute etc. methods?**
256+
257+
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:
258+
```java
259+
private class VerySimpleTask extends Task<Integer, Integer> {
260+
261+
private final ArrayList<Integer> progressValues = new ArrayList<>();
262+
263+
public ExampleTask(String tag){
264+
super(tag);
265+
}
266+
267+
@Override
268+
protected Boolean doInBackground() {
269+
for(int i = 0; i < 10; i++){
270+
publishProgress(i);
271+
SystemClock.sleep(500);
272+
}
273+
return 10;
274+
}
275+
276+
@Override
277+
protected void onProgressUpdate(Integer value) {
278+
progressValues.add(value);
279+
}
280+
281+
public List<Integer> getProgressCache(){
282+
// This method is safe to call on the ui-thread because the
283+
// onProgressUpdate method is executed on the same thread.
284+
return progressValues;
285+
}
286+
}
287+
```
288+
289+
## 5. To-Do
290+
291+
*“As long as I am breathing, in my eyes, I am just beginning.”*
292+
293+
- Add custom Executors to the TaskManager;
294+
- Finish documentation;
295+
- Write real tests for the library besides having only a demo app;

0 commit comments

Comments
 (0)