-
Notifications
You must be signed in to change notification settings - Fork 38
/
Async.java
161 lines (124 loc) · 5.24 KB
/
Async.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package io.particle.android.sdk.utils;
import android.app.Activity;
import android.os.AsyncTask;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.concurrent.RejectedExecutionException;
import javax.annotation.ParametersAreNonnullByDefault;
import io.particle.android.sdk.cloud.ParticleCloud;
import io.particle.android.sdk.cloud.ParticleDevice;
import io.particle.android.sdk.cloud.exceptions.ParticleCloudException;
/**
* Analgesic AsyncTask wrapper for making Particle cloud API calls
*/
@ParametersAreNonnullByDefault
public class Async {
private static final TLog log = TLog.get(Async.class);
public abstract static class ApiWork<ApiCaller, Result> {
public abstract Result callApi(ApiCaller apiCaller) throws ParticleCloudException, IOException;
public abstract void onSuccess(Result result);
public abstract void onFailure(ParticleCloudException exception);
/**
* Called at the end of the async task execution, before
* onSuccess(), onFailure(), or onCancel()
*/
public void onTaskFinished() {
// default: no-op
}
public void onCancelled() {
// default: no-op
}
}
/**
* For when you don't care about the return value (or there isn't one), you just want to make
* the REST call
*/
public abstract static class ApiProcedure<ApiCaller> extends ApiWork<ApiCaller, Void> {
@Override
public void onSuccess(Void voyd) {
// no-op, because that's the whole point of this class.
}
}
public static <T> AsyncApiWorker<ParticleCloud, T> executeAsync(ParticleCloud particleCloud,
ApiWork<ParticleCloud, T> work) {
return (AsyncApiWorker<ParticleCloud, T>) new AsyncApiWorker<>(particleCloud, work)
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
public static <T> AsyncApiWorker<ParticleDevice, T> executeAsync(ParticleDevice particleDevice,
ApiWork<ParticleDevice, T> work) throws ParticleCloudException {
try {
return (AsyncApiWorker<ParticleDevice, T>) new AsyncApiWorker<>(particleDevice, work)
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} catch (RejectedExecutionException ex) {
throw new ParticleCloudException(ex);
}
}
public static class AsyncApiWorker<ApiCaller, Result> extends AsyncTask<Void, Void, Result> {
private final ApiCaller caller;
private final ApiWork<ApiCaller, Result> work;
private WeakReference<Activity> activityReference;
private volatile ParticleCloudException exception;
// FIXME: this is Bad and Wrong, but this needs to SHIP, so I'm leaving it for now.
public volatile IOException ioException;
private AsyncApiWorker(ApiCaller caller, ApiWork<ApiCaller, Result> work) {
this.caller = caller;
this.work = work;
}
// This method name looks weird on its own, but looks fine in use.
/**
* Prevent all callbacks (onTaskFinished(), onCancelled(), onSuccess(), and onFailure())
* from being called if the supplied Activity is finishing (i.e.: you don't necessarily
* care about the )
*/
public AsyncApiWorker<ApiCaller, Result> andIgnoreCallbacksIfActivityIsFinishing(Activity activity) {
this.activityReference = new WeakReference<>(activity);
return this;
}
@Override
protected Result doInBackground(Void... voids) {
try {
return work.callApi(caller);
} catch (ParticleCloudException e) {
exception = e;
return null;
} catch (IOException e) {
ioException = e;
return null;
}
}
@Override
protected void onCancelled() {
if (shouldCallCallbacks()) {
work.onTaskFinished();
work.onCancelled();
}
}
@Override
protected void onPostExecute(Result result) {
if (!shouldCallCallbacks()) {
return;
}
work.onTaskFinished();
if (exception == null && ioException == null) {
work.onSuccess(result);
} else {
// FIXME: this error handling isn't quite right; fix it.
if (exception == null) {
exception = new ParticleCloudException(ioException);
}
log.e("Error calling API: " + exception.getBestMessage(), exception);
work.onFailure(exception);
}
}
private boolean shouldCallCallbacks() {
if (activityReference == null || activityReference.get() == null) {
return true;
}
boolean shouldCall = !activityReference.get().isFinishing();
if (!shouldCall) {
log.d("Refusing to call callbacks, was told to ignore them if the activity was finishing");
}
return shouldCall;
}
}
}