Migrating from JobScheduler to WorkManager seems like a massive undertaking. Any advice+ - android

I've used JobScheduler for a while now. A typical example would be:
JobScheduler triggers the onStartJob() method. This kicks off some sort of task that might include several other background processes (getting device position, making a network call, etc). I then use an interface to call back to the JobService once the task is complete or if it fails.
However, with WorkManager it seems like there is basically no way to run work asynchronously. I know WorkManager will run Workers in a separate thread but it seems like a single worker must run synchronously? If so I'll have to rework a lot of the logic in my app it seems unless I'm missing something.
Say I had a weather app and it would use the JobScheduler to run a JobService that did the following:
onStartJob() --> get device position --> make API request for local weather data -> write to database -> call jobFinished()
With WorkManager, is the intended implementation of something like this to chain multiple workers together? And if so, how do you pass data from one Worker to another? Again, seems like it would be a massive undertaking for any slightly complex app to migrate to the WorkManager API.

I think what you are looking for is a ListenableWorker. It has exactly that interface. Rather than you having to call jobFinished() in the end, we do the work for you when you resolve the ListenableFuture.
https://developer.android.com/reference/androidx/work/ListenableWorker.html#startWork() is analogous to onStartJob in your case.

Related

Android Jetpack: lifecycle-aware recurrent periodic task execution with LiveData and ViewModels

My app already uses some recent Android patterns for network calls:
LiveData class
MVVM architecture with ViewModel class
Kotlin coroutines for Repository classes
Retrofit interface etc.
Now I want to implement common feature which is automatic fetching current data from API every few minutes.
I read about WorkManager and give it a shot - I implemented it but I then saw that WorkManager (JobScheduler) keeps running after closing app which is not what I want. I also felt like WorkManager API is too much overkill for that simple task.
Then I read a guide on Codepath that suggests Handler class for main thread repetitive jobs and ScheduledThreadPoolExecutor for background repetitive tasks. I know that they will probably work fine but I'm not sure if they are best solution.
My question is: what's currently the best way for recurrent API calls that:
works with LiveData and ViewModel classes (observable result like normal API call)
is Kotlin-friendly (any way to make coroutine recurrent?)
is "lifecycle-aware", meaning that it will stop when app goes to the background?
WorkManager is for guaranteed works that needs to be executed even if your app exit or the device is restarted. From your description this doesn't seem your use case.
A threadpool seems the best option in this case, but you can judge yourself starting from this guide: "Background Tasks".

can I always use WorkManager instead of coroutines?

I wonder why should I bother with rx or coroutines when there is brilliant solution as WorkManager. But for almost all tutorials they use coroutines so may be WorkManager has disadvantages ?
The scope of both is different. WorkManager is to schedule deferrable (for any later time) or immediately.
tasks asynchronously.
As documentation says
The WorkManager API makes it easy to specify deferrable, asynchronous
tasks and when they should run. These APIs let you create a task and
hand it off to WorkManager to run immediately or at an appropriate
time.
On the other hand, coroutines are designed to compute a given task only immediately and asynchronously.
Also
Internally, coroutines and WorkManager work differently. Work manager heavily depends on Android system components like Services, Alarm manager, etc to schedule the work whereas coroutines schedule the work on Worker Threads and also is a language feature unlike WorkManager (API). So it is safe to say that coroutines do not go beyond your application. On the other hand, WorkManager can even execute the given tasks when your application is not active. for instance, background services.
Also as Marko answered, using coroutines will lead to better code readability and quality due to their fundamental design.
I would also like to include ANKO, Its a great library that provides a helpful API around coroutines for Android.
Background tasks fall into one of the following main categories:
Immediate
Deferred
Exact
To categorize a task, answer the following questions:
Does the task need to complete while the user is interacting with the application?
If so, this task should be categorized for immediate execution. If
not, proceed to the second question.
Does the task need to run at an exact time?
If you do need to run a task at an exact time, categorize the task as
exact.
Most tasks don't need to be run at an exact time. Tasks generally allow for slight variations in when they run that are based on conditions such as network availability and remaining battery. Tasks that don't need to be run at an exact time should be categorized as deferred.
Use Kotlin Coroutine when a task needs to execute immediately and if the task will end when the user leaves a certain scope or finishes an interaction.
Use WorkManager when a task needs to execute immediately and need continued processing, even if the user puts the application in the background or the device restarts
Use AlarmManager when a task that needs to be executed at an exact point in time
For more details, visit this link
If your goal is writing clean code without explicitly constructed callbacks you pass to background tasks, then you'll find that coroutines are the only option.
Using coroutines by no means precludes using WorkManager or any other tool for background operations of your choosing. You can adapt the coroutines to any API that provides callbacks as a means to continue the execution with the results of background operations.
From official Documentation:
It is important to note that coroutines is a concurrency framework, whereas WorkManager is a library for persistent work.
WorkManager:
Support for both asynchronous one-off and periodic tasks
Support for constraints such as network conditions, storage space, and charging status
Chaining of complex work requests, including running work in parallel
Output from one work request used as input for the next
Handles API level compatibility back to API level 14(see note)
Works with or without Google Play services
Follows system health best practices
LiveData support to easily display work request state in UI
Waits proper time to run.
Coroutines:
Clean code, works under the hood in a different way. Run immediately.
So depending on your requirements choose the better option.
Has others replied, WorkManager solves a different problem than Kotlin's corountines or a reactive library like RxJava.
WorkManager is now available as beta and additional documentation is produced that hopefully makes this clear.
One of these documents is the blog post I worte with some colleagues: Introducing WorkManager, where you can read:
A common confusion about WorkManager is that it’s for tasks that needs to be run in a “background” thread but don’t need to survive process death. This is not the case. There are other solutions for this use case like Kotlin’s coroutines, ThreadPools, or libraries like RxJava. You can find more information about this use case in the guide to background processing.

RxJava Observable stream inside doWork of WorkManager's Worker

I have a sync process with multiple Observables chained which I would like to call as a background service at a specific time.
I used to do this in a Service since now, but with the Android Oreo limitation, this is not possible anymore, so instead of choosing Job Scheduler + JobDispatcher for backward compatibility (min SDK supported is 19), I changed the Service class with a Worker (androidx.work).
Q1: How can I make the doWork's result be triggered only after the stream is closed (/make it synchronously)?
Q2: I need to close the whole stream (dispose of the sync subscription) manually on a specific action made by the user.
I tried doing that in "onWorkFinished" callback given by Worker class, but since the doWork returns the Result before the stream it's finished, this is not an option. I could inject the worker where I need to do this, but returning a Result before the whole job it's done doesn't sound like a good practice either.
Any suggestions are welcomed. Thanks!

Is there any development pattern that can replace an IntentService for network requests?

In the current app that I am developing with a co-worker, we're using IntentServices with Volley calls inside to process RESTful API network requests. It's just simple JSON string data, and some small images.
My question to those experienced in processing network requests is this: is there something more appropriate, or cleaner to implement out there?
From what I understand, the advantage of using an IntentService is that it runs in the background off the main thread, and is typically one of the last things killed by the Android OS. The downside being that IntentServices are run sequentially.
I've been reading a lot about RxJava, and Retrofit, and feel like our needs could be better served with that combination. Retrofit may be enough on its own, but I'd really appreciate some third-party insight.
My general rule of thumb is:
If the network I/O should be under a second, and you don't mind if it does not run to completion, any asynchronous option should be fine.
If the network I/O should be more than a second, or you really want to increase the odds that it will run to completion, use a Service. Whether you use IntentService or some other Service implementation is up to you, but you want to have a Service as an indicator to the OS that you're doing work, so it doesn't terminate your process quite so quickly once your app moves to the background. Remember that "moves to the background" is not always something initiated directly by the user, as incoming phone calls and such also move you to the background.
If the network I/O will take more than 15 seconds, not only do you need to use a Service, but you need to think about a WakeLock (via my WakefulIntentService, or WakefulBroadcastReceiver, or your own carefully-managed WakeLock) and possibly a WifiLock. 15 seconds is the minimum auto-screen-off period in Settings, which is where that figure comes from.
With all that in mind:
The downside being that IntentServices are run sequentially.
I am translating this as "an IntentService has a single thread for processing requests". This is true. In cases where you need a Service and you need parallel processing, create your own Service. Just be sure to call stopSelf() when you have no outstanding work.
I've been reading a lot about RxJava, and Retrofit, and feel like our needs could be better served with that combination
This has nothing to do with whether or not you use a Service. Just don't try doing asynchronous stuff (e.g., a Retrofit call using a Callback) from an IntentService, as you defeat the purpose of the IntentService (indicating to the OS that you're doing work). So, from an IntentService, you would use Retrofit's synchronous API, sans a Callback.
Using IntentServices just to perform a simple network request, IMO, is to much. You should use an AsyncTask if you don't want to use a library or if you prefer to go for Retrofit, Volley... (I would recommend Retrofit).
IMO, Services, or in this case IntentService are designed to execute long background tasks.
The real question is: do you load data to populate an Activity in the foreground or for doing background work, even when no UI is visible?
For background work, a Service is the way to go. You don't even need an IntentService if you rely on the threading management of Volley.
For foreground work, consider using Loaders, or Volley or Rxjava calls directly inside your Activity/Fragment.

Should I use a thread or service to run tasks in the background in Android?

I'm building an Android library to collect data from the host app and send this data to an online server in the background. I realize that this would require some sort of multi-threading/use of a service/forking.
The application simply keeps adding data through library calls, and the library should handle the sending of this data in the background without disturbing the main UI.
How should I got about making this library? Should the entire library run on an Android Service? Should I just use another thread? This is my first foray into parallelism. I'm not sure what the best way to do this is.
A detailed response is appreciated.
Some of the answers aren't quite correct. Services (Android Service component) are NOT made to run in the background, they run in the default UI thread.
In all honesty, the question shouldn't be service or thread or anything. Your library does NOT need to kick start a service, it could simply be a class (singleton/static, whatever it is) that should extend AsyncTask (or anything else running in the background that I'll explain in a bit) and use the doInBackground method to send stuff to the server. Note AsyncTask is nothing but a Thread internally. So here's what I would do:
Let's call your library's main class that interfaces with your server ServerHelper. You can make this a singleton (or static but that's a separate discussion). Within this class create an innerclass say ServerHelperCommandTask and extend AsyncTask. You really should review AsyncTask in detail to understand how that works. Because you would be asked to override doInBackGround. Anything you put in this method will autmoatically get exectued in a separate thread off the UI. Then a callback is invoked called onPostExecute that you can override as you will get the result from doInBackground here. This OnPostExecute is called in the mainThread so you can check for say error results here, etc etc.
This would be the simplest method; however, there are many other methods and libraries that help you with networking and deal with all the background stuff internally. Google just release a library called Volley which you may be able to plugin and use as it would do all the parallel processing for you. But that may take a bit of learning curve. Hope you understand AsyncTasks as in your case if the data pushed isn't a lot, then AsyncTasks is the way to go. Also note that you can call multiple AsyncTasks but while that seems on the surface that it is kicking off multiple parallel threads, that isn't quite accurate since honeycomb as internally you can call 5 Asynctasks but all 5 of those tasks will be executed sequentially so you wouldn't have to worry much about serializing.
Service would be a more reliable solution for situation You described.
I mean running background threads from service, not from Activity. Service itself does not provide separate thread by default, by the way.
The point is that Services have higher priority than acitivities so they will be destroyed with less probabilty, so your long-running task won't be interrupted.
You could do both but here's pros and cons for each solution :
Services are made to run in the background, even when your app is not in the foreground. sers usually don't like having services running for nothing.
From your description it seems that you would only need to have this thread running when the app is in foreground right ?
If so, a normal thread could do the job.
However, a service might be easier to implement.
Hope it helps
You should definitely use a Service in this situation. Async tasks and manually creating a thread is not really suitable for computations that need to run in the background for network communication. Use the Async task for long running local computations (e.g. for an algorithm doing sorting).
Note that if you use a service, it will by nature NOT run as a background thread! You need to handle threading manually. In order to save yourself from this hassle (especially if it is your first time with multi-threading) use an IntentService!
The IntentService may be invoked using the startService method as with any other service, but the IntentService class is able to handle multiple invocations as in a producer/consumer pattern. This means that you can queue commands automatically to the service just using the startService method. In the IntentService class that you make, you can then handle different types of commands by looking at the given action inside of the intent that is sent along as a parameter in the startService method.
Here is an example of how the implementation of an IntentService:
public class SimpleIntentService extends IntentService {
public SimpleIntentService() {
super("SimpleIntentService");
}
#Override
protected void onHandleIntent(Intent intent) {
String commnad = intent.getAction();
//handle your command here and execute your desired code depending on this command
}
}

Categories

Resources