Skip to content

Commit

Permalink
Merge pull request #277 from spoonconsulting/revert-226-one-running-w…
Browse files Browse the repository at this point in the history
…orker

Revert "Manage WorkManager: One Running Worker to upload a Batch of Files"
  • Loading branch information
dinitri authored Apr 30, 2024
2 parents b27c663 + 1605cd8 commit 9ab0c91
Show file tree
Hide file tree
Showing 9 changed files with 322 additions and 357 deletions.
11 changes: 8 additions & 3 deletions plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,20 @@
<source-file src="src/android/AckDatabase.java" target-dir="src/com/spoon/backgroundfileupload" />
<source-file src="src/android/UploadEventDao.java" target-dir="src/com/spoon/backgroundfileupload" />
<source-file src="src/android/UploadEvent.java" target-dir="src/com/spoon/backgroundfileupload" />
<source-file src="src/android/PendingUploadDao.java" target-dir="src/com/spoon/backgroundfileupload" />
<source-file src="src/android/PendingUpload.java" target-dir="src/com/spoon/backgroundfileupload" />
<source-file src="src/android/ProgressRequestBody.java" target-dir="src/com/spoon/backgroundfileupload" />
<source-file src="src/android/UploadForegroundNotification.java" target-dir="src/com/spoon/backgroundfileupload" />
<source-file src="src/android/UploadNotification.java" target-dir="src/com/spoon/backgroundfileupload" />
<resource-file src="src/android/res/ic_upload.png" target="res/drawable/ic_upload.png" />
</platform>
<platform name="ios">
<framework src="AFNetworking" type="podspec" spec="~> 4.0.1" />
<podspec>
<config>
<source url="https://cdn.cocoapods.org/"/>
</config>
<pods use-frameworks="true">
<pod name="AFNetworking" spec="~> 4.0.1" />
</pods>
</podspec>
<config-file target="config.xml" parent="/*">
<feature name="FileTransferBackground">
<param name="ios-package" value="FileTransferBackground" />
Expand Down
4 changes: 1 addition & 3 deletions src/android/AckDatabase.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import androidx.room.TypeConverters;
import androidx.work.Data;

@Database(entities = {UploadEvent.class, PendingUpload.class}, version = 4)
@Database(entities = {UploadEvent.class}, version = 5)
@TypeConverters(value = {Data.class})
public abstract class AckDatabase extends RoomDatabase {
private static AckDatabase instance;
Expand All @@ -30,6 +30,4 @@ public static void closeInstance() {
}

public abstract UploadEventDao uploadEventDao();

public abstract PendingUploadDao pendingUploadDao();
}
179 changes: 102 additions & 77 deletions src/android/FileTransferBackground.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import androidx.work.OutOfQuotaPolicy;
import androidx.work.WorkInfo;
import androidx.work.WorkManager;
import androidx.work.WorkQuery;

import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
Expand All @@ -29,29 +30,33 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class FileTransferBackground extends CordovaPlugin {

private static final String TAG = "FileTransferBackground";
public static final String WORK_TAG_UPLOAD = "work_tag_upload";

private CallbackContext uploadCallback;
private boolean ready = false;

private Data httpClientBaseConfig = Data.EMPTY;

public static boolean workerIsStarted;
private static String currentTag;
private static long currentTagFetchedAt;

private ScheduledExecutorService executorService = null;

private int ccUpload;

public void sendCallback(JSONObject obj) {
/* we check the webview has been initialized */
if (ready) {
Expand Down Expand Up @@ -158,7 +163,8 @@ private void initManager(String options, final CallbackContext callbackContext)

try {
final JSONObject settings = new JSONObject(options);
ccUpload = settings.getInt("parallelUploadsLimit");
int ccUpload = settings.getInt("parallelUploadsLimit");

// Rebuild base HTTP config
httpClientBaseConfig = new Data.Builder()
.putInt(UploadTask.KEY_INPUT_CONFIG_CONCURRENT_DOWNLOADS, ccUpload)
Expand Down Expand Up @@ -200,32 +206,31 @@ private void initManager(String options, final CallbackContext callbackContext)
.observeForever((tasks) -> {
int completedTasks = 0;
for (WorkInfo info : tasks) {
// No db in main thread
executorService.schedule(() -> {
final List<UploadEvent> uploadEventsList = ackDatabase
.uploadEventDao()
.getAll();
for (UploadEvent ack : uploadEventsList) {
handleAck(ack.getOutputData());
}
}, 0, TimeUnit.MILLISECONDS);
switch (info.getState()) {
// If the upload in not finished, publish its progress
case RUNNING:
if (info.getProgress() != Data.EMPTY) {
String id = info.getProgress().getString(UploadTask.KEY_PROGRESS_ID);
int progress = info.getProgress().getInt(UploadTask.KEY_PROGRESS_PERCENT, 0);

logMessage("initManager: " + info.getId() + " (" + info.getState() + ") Progress: " + progress);
Log.d(TAG, "initManager: " + info.getId() + " (" + info.getState() + ") Progress: " + progress);
sendProgress(id, progress);
}
break;
case CANCELLED:
case BLOCKED:
case ENQUEUED:
case SUCCEEDED:
logMessage("Task succeeded: " + info.getId());
completedTasks++;
// No db in main thread
executorService.schedule(() -> {
// The corresponding ACK is already in the DB, if it not, the task is just a leftover
String id = info.getOutputData().getString(UploadTask.KEY_OUTPUT_ID);
if (ackDatabase.uploadEventDao().exists(id)) {
handleAck(info.getOutputData());
}
}, 0, TimeUnit.MILLISECONDS);
break;
case FAILED:
// The task can't fail completely so something really bad has happened.
logMessage("eventLabel='Uploader failed inexplicably' error='" + info.getOutputData() + "'");
Expand Down Expand Up @@ -295,67 +300,59 @@ private void addUpload(JSONObject jsonPayload) {
} catch(PackageManager.NameNotFoundException e) {
e.printStackTrace();
}

AckDatabase.getInstance(cordova.getContext()).pendingUploadDao().insert(
new PendingUpload(
uploadId,
new Data.Builder()
// Put base info
.putString(UploadTask.KEY_INPUT_ID, uploadId)
.putString(UploadTask.KEY_INPUT_URL, (String) payload.get("serverUrl"))
.putString(UploadTask.KEY_INPUT_FILEPATH, (String) payload.get("filePath"))
.putString(UploadTask.KEY_INPUT_FILE_KEY, (String) payload.get("fileKey"))
.putString(UploadTask.KEY_INPUT_HTTP_METHOD, (String) payload.get("requestMethod"))
// Put headers
.putInt(UploadTask.KEY_INPUT_HEADERS_COUNT, headersNames.size())
.putStringArray(UploadTask.KEY_INPUT_HEADERS_NAMES, headersNames.toArray(new String[0]))
.putAll(headerValues)
// Put query parameters
.putInt(UploadTask.KEY_INPUT_PARAMETERS_COUNT, parameterNames.size())
.putStringArray(UploadTask.KEY_INPUT_PARAMETERS_NAMES, parameterNames.toArray(new String[0]))
.putAll(parameterValues)
// Put notification stuff
.putString(UploadTask.KEY_INPUT_NOTIFICATION_TITLE, (String) payload.get("notificationTitle"))
.putString(UploadTask.KEY_INPUT_NOTIFICATION_ICON, cordova.getActivity().getPackageName() + ":drawable/ic_upload")
.putString(UploadTask.KEY_INPUT_CONFIG_INTENT_ACTIVITY, intentActivity)

// Put config stuff
.putAll(httpClientBaseConfig)
.build()
)
startUpload(uploadId, new Data.Builder()
// Put base info
.putString(UploadTask.KEY_INPUT_ID, uploadId)
.putString(UploadTask.KEY_INPUT_URL, (String) payload.get("serverUrl"))
.putString(UploadTask.KEY_INPUT_FILEPATH, (String) payload.get("filePath"))
.putString(UploadTask.KEY_INPUT_FILE_KEY, (String) payload.get("fileKey"))
.putString(UploadTask.KEY_INPUT_HTTP_METHOD, (String) payload.get("requestMethod"))

// Put headers
.putInt(UploadTask.KEY_INPUT_HEADERS_COUNT, headersNames.size())
.putStringArray(UploadTask.KEY_INPUT_HEADERS_NAMES, headersNames.toArray(new String[0]))
.putAll(headerValues)

// Put query parameters
.putInt(UploadTask.KEY_INPUT_PARAMETERS_COUNT, parameterNames.size())
.putStringArray(UploadTask.KEY_INPUT_PARAMETERS_NAMES, parameterNames.toArray(new String[0]))
.putAll(parameterValues)

// Put notification stuff
.putString(UploadTask.KEY_INPUT_NOTIFICATION_TITLE, (String) payload.get("notificationTitle"))
.putString(UploadTask.KEY_INPUT_NOTIFICATION_ICON, cordova.getActivity().getPackageName() + ":drawable/ic_upload")
.putString(UploadTask.KEY_INPUT_CONFIG_INTENT_ACTIVITY, intentActivity)

// Put config stuff
.putAll(httpClientBaseConfig)
.build()
);

if (!workerIsStarted) {
startWorkers();
workerIsStarted = true;
}
}

private void startWorkers() {
logMessage("startUpload: Starting worker via work manager");

for (int i = 0; i < ccUpload; i++) {
OneTimeWorkRequest.Builder workRequestBuilder = new OneTimeWorkRequest.Builder(UploadTask.class)
.setConstraints(new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
)
.keepResultsForAtLeast(0, TimeUnit.MILLISECONDS)
.setBackoffCriteria(BackoffPolicy.LINEAR, 30, TimeUnit.SECONDS)
.addTag(FileTransferBackground.WORK_TAG_UPLOAD);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
workRequestBuilder.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST);
}

OneTimeWorkRequest workRequest = workRequestBuilder.build();
private void startUpload(final String uploadId, final Data payload) {
Log.d(TAG, "startUpload: Starting work via work manager");

OneTimeWorkRequest.Builder workRequestBuilder = new OneTimeWorkRequest.Builder(UploadTask.class)
.setConstraints(new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
)
.keepResultsForAtLeast(0, TimeUnit.MILLISECONDS)
.setBackoffCriteria(BackoffPolicy.LINEAR, 30, TimeUnit.SECONDS)
.addTag(FileTransferBackground.WORK_TAG_UPLOAD)
.addTag(getCurrentTag(cordova.getContext()))
.setInputData(payload);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
workRequestBuilder.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST);
}

WorkManager.getInstance(cordova.getContext())
.enqueueUniqueWork(FileTransferBackground.WORK_TAG_UPLOAD + "_" + i, ExistingWorkPolicy.KEEP, workRequest);
OneTimeWorkRequest workRequest = workRequestBuilder.build();

logMessage("eventLabel=Uploader starting uploads via worker" + i);
}
WorkManager.getInstance(cordova.getContext())
.enqueueUniqueWork(uploadId, ExistingWorkPolicy.APPEND, workRequest);

logMessage("eventLabel='Uploader starting upload' uploadId='" + uploadId + "'");
}

private void sendAddingUploadError(String uploadId, Exception error) {
Expand Down Expand Up @@ -433,7 +430,6 @@ private void handleAck(final Data ackData) {
*/
private void cleanupUpload(final String uploadId) {
final UploadEvent ack = AckDatabase.getInstance(cordova.getContext()).uploadEventDao().getById(uploadId);

// If the upload is done there is an ACK of it, so get file name from there
if (ack != null) {
if (ack.getOutputData().getString(UploadTask.KEY_OUTPUT_RESPONSE_FILE) != null) {
Expand Down Expand Up @@ -505,15 +501,44 @@ public static HashMap<String, Object> convertToHashMap(JSONObject jsonObject) th
return hashMap;
}

public static void logMessage(String message) {
Log.d("CordovaBackgroundUpload", message);
public static String getCurrentTag(Context context) {
final long now = System.currentTimeMillis();
if (currentTag != null && now - currentTagFetchedAt <= 5000) {
return currentTag;
}
currentTagFetchedAt = now;
currentTag = fetchCurrentTag(context);
return currentTag;
}

public static void logMessageInfo(String message) {
Log.i("CordovaBackgroundUpload", message);
public static String fetchCurrentTag(Context context) {
WorkQuery workQuery = WorkQuery.Builder
.fromTags(Arrays.asList(FileTransferBackground.WORK_TAG_UPLOAD))
.addStates(Arrays.asList(WorkInfo.State.RUNNING, WorkInfo.State.ENQUEUED))
.build();
List<WorkInfo> workInfo;
try {
workInfo = WorkManager.getInstance(context)
.getWorkInfos(workQuery)
.get();
} catch (ExecutionException | InterruptedException e) {
Log.w(TAG, "getForegroundInfo: Problem while retrieving task list:", e);
workInfo = Collections.emptyList();
}
String prefix = "packet_";
for (WorkInfo info : workInfo) {
if (!info.getState().isFinished()) {
for (String tag : info.getTags()) {
if (tag.startsWith(prefix)) {
return tag;
}
}
}
}
return prefix + UUID.randomUUID().toString();
}

public static void logMessageError(String message, Exception exception) {
Log.e("CordovaBackgroundUpload", message, exception);
public static void logMessage(String message) {
Log.d("CordovaBackgroundUpload", message);
}
}
48 changes: 0 additions & 48 deletions src/android/PendingUpload.java

This file was deleted.

Loading

0 comments on commit 9ab0c91

Please sign in to comment.