Skip to content
This repository was archived by the owner on Sep 2, 2025. It is now read-only.

Commit 2189526

Browse files
committed
Merge pull request #19 from nhaarman/master
Simpler & Testable API
2 parents b1c06ce + 3d34e82 commit 2189526

30 files changed

Lines changed: 1269 additions & 1754 deletions

File tree

README.md

Lines changed: 100 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# AsyncTask-Android
1+
# AsyncTask-Android [ ![Download](https://api.bintray.com/packages/label305/Label305/AsyncTask-Android/images/download.svg) ](https://bintray.com/label305/Label305/AsyncTask-Android/_latestVersion)
22

33
AsyncTask made simpler.
44

@@ -18,7 +18,7 @@ Use a `SimpleAsyncTask` when doing work that will not throw a checked `Exception
1818
public class MyAsyncTask extends SimpleAsyncTask<String> {
1919

2020
@Override
21-
protected String doInBackgroundSimple() {
21+
protected String doInBackground() {
2222
return "Test";
2323
}
2424

@@ -50,7 +50,105 @@ public class MyAsyncTask extends AsyncTask<String, IOException> {
5050
protected void onException(final IOException e) {
5151
// Handle IOException
5252
}
53+
}
54+
```
55+
56+
## Executing
57+
58+
Like Android's `AsyncTask`, you can call `execute()` on the `AsyncTask` instance to start it:
59+
60+
```java
61+
new MyAsyncTask().execute();
62+
```
63+
64+
Alternatively, you can use an [`AsyncTaskExecutor`](https://github.com/Label305/AsyncTask-Android/blob/master/asynctask/src/main/java/com/label305/asynctask/AsyncTaskExecutor.java)
65+
to delegate the execution. A default implementation is available as `AsyncTaskExecutor.DEFAULT_EXECUTOR`, which is used by `AsyncTask.execute()`.
66+
67+
```java
68+
AsyncTaskExecutor.DEFAULT_EXECUTOR.execute(new MyAsyncTask());
69+
```
70+
71+
## Testing
72+
73+
The `AsyncTaskExecutor` can come in handy while testing. When practicing dependency injection, you can inject a different implementation that executes the
74+
`AsyncTask` synchronously. [TestAsyncTaskExecutor](https://github.com/Label305/AsyncTask-Android/blob/master/asynctask-test/src/main/java/com/label305/asynctask/TestAsyncTaskExecutor.java)
75+
can be used for such tests. To access this class, add the following to your relevant `build.gradle` section:
76+
77+
```groovy
78+
testCompile 'com.label305:asynctask-test:x.x.x' // For unit tests
79+
androidTestCompile 'com.label305:asynctask-test:x.x.x' // For instrumentation tests
80+
```
81+
82+
For example, suppose we have the following class for asynchronously retrieving a `User` from some web service:
83+
84+
```java
85+
public class UserRetriever {
86+
87+
private final WebService mWebService;
88+
89+
private final AsyncTaskExecutor mExecutor;
90+
91+
public UserRetriever(final WebService webService, final AsyncTaskExecutor executor) {
92+
mWebService = webService;
93+
mExecutor = executor;
94+
}
5395

96+
/**
97+
* Retrieves the User asynchronously, and notifies given callback when the User is retrieved.
98+
*/
99+
public void retrieveUser(final UserCallback callback) {
100+
mExecutor.execute(new RetrieveUserTask(callback));
101+
}
102+
103+
public interface UserCallback {
104+
105+
void onUserRetrieved(User user);
106+
}
107+
108+
private class RetrieveUserTask extends SimpleAsyncTask<User> {
109+
110+
private final UserCallback mCallback;
111+
112+
private RetrieveUserTask(final UserCallback callback) {
113+
mCallback = callback;
114+
}
115+
116+
@Override
117+
public User doInBackground() {
118+
return mWebService.retrieveUser();
119+
}
120+
121+
@Override
122+
public void onSuccess(final User user) {
123+
mCallback.onUserRetrieved(user);
124+
}
125+
}
126+
}
127+
```
128+
129+
We can test this class using mock objects, in this case Mockito:
130+
131+
```java
132+
public class UserRetrieverTest {
133+
134+
@Test
135+
public void retrieveUser_retrievesUser_andNotifiesCallback() {
136+
/* Given */
137+
User user = new User();
138+
139+
WebService webService = mock(WebService.class);
140+
when(webService.retrieveUser()).thenReturn(user);
141+
142+
UserCallback callback = mock(UserCallback.class);
143+
144+
UserRetriever userRetriever = new UserRetriever(webService, TestAsyncTaskExecutor.instance());
145+
146+
/* When */
147+
userRetriever.retrieveUser(callback);
148+
149+
/* Then */
150+
verify(callback).onUserRetrieved(user);
151+
}
54152
}
55153
```
56154

RELEASING.md

Lines changed: 0 additions & 16 deletions
This file was deleted.

asynctask-example/build.gradle

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
apply plugin: 'com.android.library'
2+
3+
apply from: rootProject.projectDir.absolutePath + '/gradle/compile.gradle'
4+
apply from: rootProject.projectDir.absolutePath + '/gradle/publishing.gradle'
5+
apply from: rootProject.projectDir.absolutePath + '/gradle/bintray.gradle'
6+
apply from: rootProject.projectDir.absolutePath + '/gradle/artifactory.gradle'
7+
8+
dependencies {
9+
compile project(':asynctask')
10+
11+
provided 'com.android.support:support-annotations:22.2.1'
12+
13+
testCompile project(':asynctask-test')
14+
testCompile 'junit:junit:4.12'
15+
testCompile 'org.mockito:mockito-core:2.0.31-beta'
16+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
POM_NAME=AsyncTask Example
2+
POM_ARTIFACT_ID=asynctask-example
3+
POM_PACKAGING=aar
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<manifest package="com.label305.asynctask.example" />
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2015 Label305
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.label305.asynctask.example;
18+
19+
public class User {
20+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright 2015 Label305
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.label305.asynctask.example;
18+
19+
import android.support.annotation.NonNull;
20+
import com.label305.asynctask.AsyncTask;
21+
import com.label305.asynctask.AsyncTaskExecutor;
22+
import java.io.IOException;
23+
24+
public class UserRetriever {
25+
26+
private final WebService mWebService;
27+
28+
private final AsyncTaskExecutor mExecutor;
29+
30+
public UserRetriever(final WebService webService, final AsyncTaskExecutor executor) {
31+
mWebService = webService;
32+
mExecutor = executor;
33+
}
34+
35+
/**
36+
* Retrieves the User asynchronously, and notifies given callback when the User is retrieved.
37+
*/
38+
public void retrieveUser(final UserCallback callback) {
39+
mExecutor.execute(new RetrieveUserTask(callback));
40+
}
41+
42+
public interface UserCallback {
43+
44+
void onUserRetrieved(User user);
45+
46+
void onException(IOException e);
47+
}
48+
49+
private class RetrieveUserTask extends AsyncTask<User, IOException> {
50+
51+
private final UserCallback mCallback;
52+
53+
private RetrieveUserTask(final UserCallback callback) {
54+
mCallback = callback;
55+
}
56+
57+
@Override
58+
public User doInBackground() throws IOException {
59+
return mWebService.retrieveUser();
60+
}
61+
62+
@Override
63+
public void onSuccess(final User user) {
64+
mCallback.onUserRetrieved(user);
65+
}
66+
67+
@Override
68+
protected void onException(@NonNull final IOException e) {
69+
mCallback.onException(e);
70+
}
71+
}
72+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright 2015 Label305
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.label305.asynctask.example;
18+
19+
import java.io.IOException;
20+
21+
public interface WebService {
22+
23+
User retrieveUser() throws IOException;
24+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright 2015 Label305
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.label305.asynctask.example;
18+
19+
import com.label305.asynctask.TestAsyncTaskExecutor;
20+
import java.io.IOException;
21+
import org.junit.Test;
22+
23+
import static org.mockito.Mockito.mock;
24+
import static org.mockito.Mockito.verify;
25+
import static org.mockito.Mockito.when;
26+
27+
@SuppressWarnings("NewExceptionWithoutArguments")
28+
public class UserRetrieverTest {
29+
30+
@Test
31+
public void retrieveUser_retrievesUser_andNotifiesCallback() throws IOException {
32+
/* Given */
33+
User user = new User();
34+
35+
WebService webService = mock(WebService.class);
36+
when(webService.retrieveUser()).thenReturn(user);
37+
38+
UserRetriever.UserCallback callback = mock(UserRetriever.UserCallback.class);
39+
40+
UserRetriever userRetriever = new UserRetriever(webService, TestAsyncTaskExecutor.instance());
41+
42+
/* When */
43+
userRetriever.retrieveUser(callback);
44+
45+
/* Then */
46+
verify(callback).onUserRetrieved(user);
47+
}
48+
49+
@Test
50+
public void retrieveUser_retrievesUser_withException_notifiesCallback() throws IOException {
51+
/* Given */
52+
IOException e = new IOException();
53+
54+
WebService webService = mock(WebService.class);
55+
when(webService.retrieveUser()).thenThrow(e);
56+
57+
UserRetriever.UserCallback callback = mock(UserRetriever.UserCallback.class);
58+
59+
UserRetriever userRetriever = new UserRetriever(webService, TestAsyncTaskExecutor.instance());
60+
61+
/* When */
62+
userRetriever.retrieveUser(callback);
63+
64+
/* Then */
65+
verify(callback).onException(e);
66+
}
67+
}

asynctask-test/build.gradle

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
apply plugin: 'com.android.library'
2+
3+
android {
4+
lintOptions {
5+
disable 'InvalidPackage'
6+
}
7+
}
8+
9+
apply from: rootProject.projectDir.absolutePath + '/gradle/compile.gradle'
10+
apply from: rootProject.projectDir.absolutePath + '/gradle/publishing.gradle'
11+
apply from: rootProject.projectDir.absolutePath + '/gradle/bintray.gradle'
12+
apply from: rootProject.projectDir.absolutePath + '/gradle/artifactory.gradle'
13+
14+
dependencies {
15+
compile project(':asynctask')
16+
compile 'org.mockito:mockito-core:2.0.31-beta'
17+
18+
provided 'com.android.support:support-annotations:22.2.1'
19+
}

0 commit comments

Comments
 (0)