Skip to content

Commit 92e512b

Browse files
authored
Update AndroidJobStrategy to cancel requests
1 parent 8a72157 commit 92e512b

File tree

2 files changed

+116
-8
lines changed

2 files changed

+116
-8
lines changed

core/src/androidTest/java/com/cloudinary/android/AndroidJobStrategyTest.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,21 @@
33

44
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
55
import androidx.test.internal.util.ReflectionUtil;
6+
import androidx.test.platform.app.InstrumentationRegistry;
67
import androidx.work.BackoffPolicy;
78
import androidx.work.Constraints;
89
import androidx.work.OneTimeWorkRequest;
910
import androidx.work.WorkManager;
1011
import androidx.work.WorkRequest;
1112
import androidx.work.impl.model.WorkSpec;
1213

14+
import com.cloudinary.Cloudinary;
15+
import com.cloudinary.android.callback.ErrorInfo;
16+
import com.cloudinary.android.callback.UploadCallback;
1317
import com.cloudinary.android.payload.FilePayload;
18+
import com.cloudinary.utils.ObjectUtils;
1419

20+
import org.cloudinary.json.JSONObject;
1521
import org.junit.Assert;
1622
import org.junit.Test;
1723
import org.junit.runner.RunWith;
@@ -20,10 +26,13 @@
2026
import java.io.IOException;
2127
import java.lang.reflect.Field;
2228
import java.lang.reflect.ParameterizedType;
29+
import java.util.Map;
2330

2431
@RunWith(AndroidJUnit4ClassRunner.class)
2532
public class AndroidJobStrategyTest extends AbstractTest {
2633

34+
int success = 0;
35+
int errors = 0;
2736
private File payloadFile;
2837

2938
@Override
@@ -55,4 +64,39 @@ public void testAdapter() throws InterruptedException, IOException, NoSuchFieldE
5564
Assert.assertEquals(BackoffPolicy.LINEAR, adapted.getWorkSpec().backoffPolicy);
5665

5766
}
67+
@Test
68+
public void testCancelRequest() throws InterruptedException, IOException, NoSuchFieldException, IllegalAccessException {
69+
FilePayload payload = buildPayload();
70+
String requestId = MediaManager.get().upload(payload)
71+
.unsigned(TEST_PRESET).callback(new UploadCallback() {
72+
@Override
73+
public void onStart(String requestId) {
74+
}
75+
76+
@Override
77+
public void onProgress(String requestId, long bytes, long totalBytes) {
78+
}
79+
80+
@Override
81+
public void onSuccess(String requestId, Map resultData) {
82+
success++;
83+
}
84+
85+
@Override
86+
public void onError(String requestId, ErrorInfo error) {
87+
errors++;
88+
}
89+
90+
@Override
91+
public void onReschedule(String requestId, ErrorInfo error) {
92+
93+
}
94+
})
95+
.dispatch();
96+
Thread.sleep(1000);
97+
MediaManager.get().cancelRequest(requestId);
98+
Thread.sleep(3000);
99+
Assert.assertTrue(success == 0);
100+
Assert.assertTrue(errors == 1);
101+
}
58102
}

core/src/main/java/com/cloudinary/android/AndroidJobStrategy.java

Lines changed: 72 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import android.content.Context;
44
import android.os.Bundle;
5-
65
import androidx.annotation.NonNull;
76
import androidx.work.BackoffPolicy;
87
import androidx.work.Constraints;
@@ -23,13 +22,19 @@
2322
import java.io.FileInputStream;
2423
import java.io.IOException;
2524
import java.io.ObjectInputStream;
25+
import java.lang.ref.WeakReference;
2626
import java.util.List;
27+
import java.util.Map;
28+
import java.util.concurrent.ConcurrentHashMap;
2729
import java.util.concurrent.TimeUnit;
2830

2931
public class AndroidJobStrategy implements BackgroundRequestStrategy {
3032

3133
private static final String JOB_TAG = "CLD";
3234

35+
private static final Map<String, WeakReference<Thread>> threads = new ConcurrentHashMap<>();
36+
private static final Object threadsMapLockObject = new Object();
37+
3338
private Context context;
3439

3540
public static OneTimeWorkRequest adapt(UploadRequest<?> request, File payloadFile) {
@@ -43,7 +48,7 @@ public static OneTimeWorkRequest adapt(UploadRequest<?> request, File payloadFil
4348

4449
Data inputData = request.buildPayload(payloadFile);
4550

46-
return new OneTimeWorkRequest.Builder(UploadJob.class).setBackoffCriteria(adaptBackoffPolicy(policy.getBackoffPolicy()), policy.getBackoffMillis(), TimeUnit.MILLISECONDS).setInputData(inputData).setConstraints(constraints).addTag(JOB_TAG).build();
51+
return new OneTimeWorkRequest.Builder(UploadJob.class).setBackoffCriteria(adaptBackoffPolicy(policy.getBackoffPolicy()), policy.getBackoffMillis(), TimeUnit.MILLISECONDS).setInputData(inputData).setConstraints(constraints).addTag(request.getRequestId()).build();
4752
}
4853

4954
private static BackoffPolicy adaptBackoffPolicy(UploadPolicy.BackoffPolicy backoffPolicy) {
@@ -99,15 +104,48 @@ public void executeRequestsNow(int howMany) {
99104
@Override
100105
public boolean cancelRequest(String requestId) {
101106
Operation operation = WorkManager.getInstance(context).cancelAllWorkByTag(requestId);
107+
killThread(requestId);
102108
return operation.getResult().isCancelled();
103109
}
104110

105111
@Override
106112
public int cancelAllRequests() {
107113
WorkManager.getInstance(context).cancelAllWork();
114+
killAllThreads();
108115
return 0;
109116
}
110117

118+
private void killThread(String requestId) {
119+
synchronized (threadsMapLockObject) {
120+
WeakReference<Thread> ref = threads.remove(requestId);
121+
if (ref != null) {
122+
Thread thread = ref.get();
123+
if (thread != null) {
124+
thread.interrupt();
125+
}
126+
127+
ref.clear();
128+
}
129+
}
130+
}
131+
132+
private void killAllThreads() {
133+
synchronized (threadsMapLockObject) {
134+
for (String requestId : threads.keySet()) {
135+
WeakReference<Thread> ref = threads.get(requestId);
136+
Thread thread = ref.get();
137+
138+
if (thread != null) {
139+
thread.interrupt();
140+
}
141+
142+
ref.clear();
143+
}
144+
145+
threads.clear();
146+
}
147+
}
148+
111149
@Override
112150
public int getPendingImmediateJobsCount() {
113151
return getJobCountByState(WorkInfo.State.ENQUEUED);
@@ -137,36 +175,62 @@ public static final class UploadJob extends Worker {
137175
private final Context context;
138176
private final WorkerParameters workParams;
139177

178+
private String requestId;
179+
140180
public UploadJob(@NonNull Context context, @NonNull WorkerParameters workerParams) {
141181
super(context, workerParams);
142182
this.context = context;
143183
this.workParams = workerParams;
144184
}
145185

186+
@Override
187+
public void onStopped() {
188+
super.onStopped();
189+
unregisterThread(requestId);
190+
}
191+
192+
private void registerThread(String requestId, Thread thread) {
193+
synchronized (threadsMapLockObject) {
194+
threads.put(requestId, new WeakReference<>(thread));
195+
}
196+
}
197+
198+
private void unregisterThread(String requestId) {
199+
synchronized (threadsMapLockObject) {
200+
if(requestId != null) {
201+
WeakReference<Thread> removed = threads.remove(requestId);
202+
if (removed != null) {
203+
removed.clear();
204+
}
205+
}
206+
}
207+
}
208+
146209
@NonNull
147210
@Override
148211
public Result doWork() {
149-
// Removed Wakelock logic as it causes RuntimeException ("WakeLock under-locked")
150212

151213
// Prepare extract payload data from temporary file.
152214
String payloadFilePath = workParams.getInputData().getString(UploadRequest.PayloadData.KEY);
153215
if (payloadFilePath == null) {
154216
// NO Payload input file created prior to request.
155217
return Result.failure();
156218
}
219+
157220
File payloadFile = new File(payloadFilePath);
158221
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(payloadFile))) {
159222
UploadRequest.PayloadData payloadData = (UploadRequest.PayloadData) ois.readObject();
160223
AndroidJobStrategy.AndroidJobRequestParams jobInputData = new AndroidJobStrategy.AndroidJobRequestParams(payloadData);
161-
162-
// call the generic processor:
163-
UploadStatus result = MediaManager.get().processRequest(context, jobInputData);
224+
requestId = payloadData.getRequestId();
225+
registerThread(requestId, Thread.currentThread());
226+
UploadStatus result = MediaManager.get().processRequest(context, jobInputData); // Replace this with your actual upload logic
164227
return adaptResult(result);
165-
166-
} catch (NullPointerException | IOException | ClassNotFoundException e) {
228+
} catch (NullPointerException | IOException | ClassNotFoundException e ) {
167229
// Unable to deserialize payload data from file.
168230
e.printStackTrace();
169231
return Result.failure();
232+
} finally {
233+
unregisterThread(requestId);
170234
}
171235
}
172236

0 commit comments

Comments
 (0)