Search code examples
javaandroidandroid-workmanager

Android WorkManager returns multiple Results


I am fairly new to WorkManager (and still new to Java as well) and seem to be having some issues. Basically, we have an app where the user is able to upload some records to a server.

Getting them uploaded isn't an issue. However, we want to be able to continue the upload in the event the network gets disconnected. After doing some research I stumbled across WorkManager. After watching multiple videos, and several tutorials, we decided to give it a shot.

I got it working (sort of) but am having an issue where its returning multiple results instead of just one. What I'd like to do is just have it return the Result(Success or Failure) once AFTER all the records have been updated and then use an AlertDialog to show whether or not it succeeded. I think that it's returning a result for each Data item in the work request instead of just the one time. But I'm not sure why, or even how to fix it. When I run it 3, AlertDialogs will appear, and the first 2 won't have the correct information, but the last does.

The breakdown: I call an UpLoadBatchRecords method when the upload button is clicked. The UploadBatchRecords method sets the Data, Constraints, and enqueues the WorkRequest:

public void UploadBatchRecords() {
        BatchMaster batchMaster = batchList.get(curBatchPosition);
        final Data data = new Data.Builder()
                .putInt(String.valueOf(NetworkConnected.CUR_POSITION), curBatchPosition)
                .putString(NetworkConnected.currentModule, batchMaster.ModuleName)
                .putString(NetworkConnected.currentBatch, batchMaster.BatchID)
                .build();

        Constraints constraints = new Constraints.Builder()
                .setRequiredNetworkType(NetworkType.CONNECTED)
                .build();


        final OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(NetworkConnected.class)
                .setInputData(data)
                .setConstraints(constraints)
                .build();

        WorkManager.getInstance().enqueue(workRequest);

        WorkManager.getInstance().getWorkInfoByIdLiveData(workRequest.getId())
                   .observe(this, new Observer<WorkInfo>() {
                               @Override
                               public void onChanged(WorkInfo workInfo) {
                                   if (workInfo.getState().isFinished() && workInfo.getState() == WorkInfo.State.SUCCEEDED) {
                                       message = workInfo.getOutputData().getString(NetworkConnected.KEY_SUCCESS);
                                   }
                                   else {
                                       message = workInfo.getOutputData().getString(NetworkConnected.KEY_FAILURE);
                                   }

                                   alert(message);
                               }
                           });
    }

WorkerClass:

public class NetworkConnected extends Worker {
    public NetworkConnected(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
    }

    private ArrayList<BatchMaster> batchList;
    public static final int CUR_POSITION = 0;
    public static final String currentModule = "Module Name";
    public static final String currentBatch = "Batch ID";
    public static final String KEY_SUCCESS = "Success";
    public static final String KEY_FAILURE = "Failure";

        @NonNull
    @Override
    public Result doWork() {
            int curPosition = getInputData().getInt(String.valueOf(CUR_POSITION), 0);
            String moduleName = getInputData().getString(currentModule);
            String batchID = getInputData().getString(currentBatch);
            LoadBatches();

            try {

                UploadBatchRecords(curPosition, moduleName, batchID);

                Data success = new Data.Builder()
                        .putString(KEY_SUCCESS, " records have been updated \n\n")
                        .build();

                return Result.success(success);
            }
            catch (Exception e) {
                Data failure = new Data.Builder()
                        .putString(KEY_FAILURE, " records failed to upload")
                        .build();
                return Result.failure(failure);
            }
        }

    public void UploadBatchRecords(Integer curPosition, String modName, String batchID) {
        int seqNum;
        String recordData;

        Cursor cur = Global.dbManager.fetchBatchRecords(companyID, modName, batchID);
        while (cur.moveToNext()) {
            seqNum = cur.getInt(cur.getColumnIndex("SequenceNumber"));
            recordData = cur.getString(cur.getColumnIndex("RecordData"));

            BatchRecordWebService(curPosition, seqNum, recordData);
        }
        cur.close();
    }

    public void BatchRecordWebService(Integer position, Integer seqNum, String recordData) {
        BatchMaster batchMaster = batchList.get(position);

        final WebService webservice = new WebService();

        webservice.setMethodName("UploadRecord");
        ArrayList<PropertyInfo> props = new ArrayList<>();
        AddWebServiceProperty(props, "CompanyID", companyID, String.class);
        AddWebServiceProperty(props, "ModuleName", batchMaster.ModuleName, String.class);
        AddWebServiceProperty(props, "BatchID", batchMaster.BatchID, String.class);
        AddWebServiceProperty(props, "SequenceNumber", seqNum.toString(), String.class);
        AddWebServiceProperty(props, "RecordData", recordData, String.class);
        AddWebServiceProperty(props, "CreatedBy", batchMaster.CreatedBy, String.class);
        AddWebServiceProperty(props, "CreatedDate", batchMaster.CreatedDate, String.class);
        AddWebServiceProperty(props, "UploadedBy", batchMaster.UpdatedBy, String.class);
        AddWebServiceProperty(props, "UploadDate", batchMaster.UpdatedDate, String.class);

        webservice.setSoapProperties(props);

        try {
            webservice.execute().get();

            if (webservice.getSuccess()) {
                Global.count++;
            }
            else{
                Global.failedCount++;
            }
        } catch (Exception ignored) {
        }
    }

    public void LoadBatches() {
        batchList = new ArrayList<>();

        Cursor cur = Global.dbManager.fetchBatches();
        while (cur.moveToNext()) {
            BatchMaster view = new BatchMaster(cur.getString(cur.getColumnIndex("ModuleName")),
                    cur.getString(cur.getColumnIndex("BatchID")),
                    cur.getString(cur.getColumnIndex("FormatName")),
                    cur.getInt(cur.getColumnIndex("RecordCount")),
                    cur.getString(cur.getColumnIndex("CreatedBy")),
                    cur.getString(cur.getColumnIndex("CreateDate")),
                    cur.getString(cur.getColumnIndex("UpdatedBy")),
                    cur.getString(cur.getColumnIndex("UpdateDate")));
            batchList.add(view);
        }
        cur.close();
   }

I'm not sure what I'm missing and I'm pretty sure that it's something stupid. Any help you can give would be great! Thanks!


Solution

  • where its returning multiple results instead of just one.

    This is expected because The onChanged() method gets called on any change of the state regardless that the work is finished or not.

        /**
         * Returns {@code true} if this State is considered finished.
         *
         * @return {@code true} for {@link #SUCCEEDED}, {@link #FAILED}, and * {@link #CANCELLED}
         *         states
         */
        public boolean isFinished() {
            return (this == SUCCEEDED || this == FAILED || this == CANCELLED);
        }
    

    WorkInfo isFinished() is called when your work is in any of (SUCCEEDED | FAILED | CANCELLED) states.

    But there are other states like ENQUEUED | RUNNING | BLOCKED.

    So, onChanged() method gets called on any of these states,

    What I'd like to do is just have it return the Result(Success or Failure) once AFTER all the records have been updated and then use an AlertDialog to show whether or not it succeeded.

    But as you need it once the entire work returns a result, then you need to wrap your code in if (workInfo.getState().isFinished()), but that is negated for all other states in the else branch.

    To do that:

    WorkManager.getInstance().getWorkInfoByIdLiveData(workRequest.getId())
               .observe(this, new Observer<WorkInfo>() {
                           @Override
                           public void onChanged(WorkInfo workInfo) {
                               if (workInfo.getState().isFinished() {
                                   if (workInfo.getState() == WorkInfo.State.SUCCEEDED) {
                                       message = workInfo.getOutputData().getString(NetworkConnected.KEY_SUCCESS);
                                   }
                                   else {
                                       message = workInfo.getOutputData().getString(NetworkConnected.KEY_FAILURE);
                                   }
    
                                   alert(message);
                               }
                           }
                       });