How do I run a ListenableWorker work on a background thread?

前端 未结 2 690
盖世英雄少女心
盖世英雄少女心 2021-01-03 04:52

Since I need to perform work asynchronously in WorkManager, I need to use the ListenableWorker, which by default runs on the main (UI) thread. Since this work c

相关标签:
2条回答
  • 2021-01-03 05:34

    If you want to continuously (i.e., less than every 60 seconds), you absolutely should be using a foreground service and not WorkManager, which is for, as per the documentation:

    deferrable, asynchronous tasks

    And not something that needs to run near continously.

    However, if you do proceed to incorrectly use WorkManager, you'd want to keep the following in mind:

    Your custom doWork method runs on the main thread because as per the setExecutor() documentation:

    An Executor for running Workers

    Specifically, only the Worker subclass of ListenableWorker runs on a background thread provided by the Executor - not your ListenableWorker implementation.

    As per the ListenableWorker.startWork() documentation:

    This method is called on the main thread.

    Because you're using ListenableWorker, your startWork method is being called on the main thread, as expected. Since you call your own doWork() method on the same thread, you'll still be on the main thread.

    In your case, you don't need to care about what thread you're on and you don't need any Executor since it doesn't matter what thread you call getLastLocation() on.

    Instead, you need to only call set on your ResolvableFuture when you actually have a result - i.e., in the onSuccess() or onFailure callbacks. This is the signal to WorkManager that you're actually done with your work:

    public class LocationWorker extends ListenableWorker {
    
        static final String UNIQUE_WORK_NAME = "LocationWorker";
        static final String KEY_NEW_LOCATION = "new_location";
        private static final String TAG = "LocationWorker";
        private ResolvableFuture<Result> mFuture;
        private LocationCallback mLocationCallback;
    
        public LocationWorker(@NonNull final Context appContext, @NonNull WorkerParameters workerParams) {
            super(appContext, workerParams);
        }
    
        @NonNull
        @Override
        public ListenableFuture<Result> startWork() {
            Log.d(TAG, "Starting work " + getId());
            mFuture = ResolvableFuture.create();
            Utils.setRequestingLocationUpdates(getApplicationContext(), true);
            mLocationCallback = new LocationCallback() {
                @Override
                public void onLocationResult(LocationResult locationResult) {
                    LocationUtils.getInstance(getApplicationContext()).removeLocationUpdates(this);
                    Location location = locationResult.getLastLocation();
                    Log.d(TAG, "Work " + getId() + " returned: " + location);
                    // Rescheduling work
                    OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(LocationWorker.class).setInitialDelay(10, TimeUnit.SECONDS).build();
                    WorkManager.getInstance().enqueueUniqueWork(LocationWorker.UNIQUE_WORK_NAME, ExistingWorkPolicy.KEEP, request);
                    Log.d(TAG, "Rescheduling work. New ID: " + request.getId());
    
                    // Always set the result as the last operation
                    mFuture.set(Result.success(Utils.getOutputData(location)));
                }
            };
            LocationUtils.getInstance(getApplicationContext()).requestSingleUpdate(mLocationCallback, new OnFailureListener() {
                @Override
                public void onFailure(@NonNull Exception e) {
                    LocationUtils.getInstance(getApplicationContext()).removeLocationUpdates(mLocationCallback);
                    Utils.setRequestingLocationUpdates(getApplicationContext(), false);
                    WorkManager.getInstance().cancelUniqueWork(UNIQUE_WORK_NAME);
                    mFuture.set(Result.failure());
                }
            });
            return mFuture;
        }
    }
    
    0 讨论(0)
  • 2021-01-03 05:52

    Well - As the other answers mentioned, if you want to do frequent work (like every 60 sec) you should use foreground service.

    Regardless, I'd use coroutines to get out of the main thread. Like:

            runBlocking(Dispatchers.Default) {
               //If you want to run blocking your code
            }
    

    Or using the GlobalScope Launch

            GlobalScope.launch {
             //Work in a separated thread (running not blocking)
            }
    

    Here is a practical example of getting a location using ListenableWorker and setting up the listeners out of the main thread.

    https://github.com/febaisi/ListenableWorkerExample/blob/master/app/src/main/java/com/febaisi/listenableworkerexample/data/LocationListenableWorker.kt#L28

    0 讨论(0)
提交回复
热议问题