Playing around with WorkManager recently as I try to move out of using old solutions for scheduling tasks in the background.
I was wondering though, is there any sort of feature for the Worker to post events during it's task? I normally would use some sort of an event bus like Otto wherein I'd register the bus to a subclass that posts events whenever the task reaches a certain milestone.
If there is no way for workers to post such events during a task, is there a way for us to register the eventBus into the worker so we can do this?
There were no takers so I ended up finding a workaround.
Instead of posting events via an eventbus, we can observe a live WorkStatus.
There are a few ways to observe work statuses and in my way, I've decided to observe base on the tag of my worker. Below is a snippet of how I went to do this:
WorkManager.getInstance().getStatusesByTag("MyWorker")
.observe(this, new Observer<List<WorkStatus>>() {
#Override
public void onChanged(#Nullable List<WorkStatus> workStatuses) {
doSomething();
}
});
Every time something changes, I look through all of my work statuses and decide what to do. You can also grab Data values here if you ever required them.
Related
I know how to save states and restore them, but I just get confused when I have to do work with the Web services and to update UI. For times I was using the AsyncTask but then I came to point where I loose my activity/fragment context for example when I rotate the device. So in this way, I am thinking how other apps are handling such situations.
If I use the IntentService and call my web service from there, then I came to think that for each web service I have to make IntentService differently, and update the UI of each activity and fragment I have to make the BroadcastReceiver for each activity and fragments.
So what is a good practice for calling web service from the activity and the fragments?
How can I Update UI when the service return arrives (or call next service based on first services results)?
If you want your data to be instantly available through configuration changes (which you do), then you probably want to use Loaders.
It gives the developer a mechanism of loading data asynchronously for an activity or fragment. Since loaders are specifically designed to solve the issue of async loading, one does not have to spend too much time designing async tasks to handle all different scenarios efficiently.
Good article about Loaders https://medium.com/google-developers/making-loading-data-on-android-lifecycle-aware-897e12760832
Try using retrofit. It's a great networking libraries for Android apps and it's easy to use.
The entire network call + JSON/XML parsing is completely handled by it (with help from Gson for JSON parsing). Documentation is great and the community is huge.
check out this sample.
I noticed a comment you made:
...and my webservices are soap and I cant change them
The way I'm currently calling my web service, which is also SOAP, is via an Intent. I do this by passing in the data that I'm submitting to the Web service with putExtra then receiving it on my WebService, as you probably do right now. I then get the result from that web call and process it inside an AsyncTask, the async task will then utilize EventBus to post to Results as needed which are received on my MainThread via ThreadMode.Main.
So with that said, I highly recommend the use of a library called EventBus from Greenrobot.
You greatly simplify communication between Activities and Fragments, You can get started immediately using a default EventBus instance available from anywhere in your code. For example, you can do the following.
EventBus.getDefault().post(new ModelForOtherActivityToSee(data));
In the model, you can include anything you want, and react accordingly when received.
The best part is that when received, EventBus handles how the data will be executed by either running ASYNC, MAIN, BACKGROUND
ASYNC - Event handler methods are called in a separate thread. This is always independent from the posting thread and the main thread. Posting events never wait for event handler methods using this mode. Event handler methods should use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number of long-running asynchronous handler methods at the same time to limit the number of concurrent threads. EventBus uses a thread pool to efficiently reuse threads from completed asynchronous event handler notifications.
Background - Subscribers will be called in a background thread. If posting thread is not the main thread, event handler methods will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single background thread that will deliver all its events sequentially. Event handlers using this mode should try to return quickly to avoid blocking the background thread.
MAIN -Subscribers will be called in Android’s main thread (sometimes referred to as UI thread). If the posting thread is the main thread, event handler methods will be called directly (synchronously like described for ThreadMode.POSTING). Event handlers using this mode must return quickly to avoid blocking the main thread.
An example of receiving an event broadcasted from EventBus:
//ThreadMode can be ASYNC, MAIN, BACKGROUND
#Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(ModelForOtherActivityToSee eventModel) {
/* Do something with eventModel received, this runs on UI thread */
};
Full example on how to use EventBus:
1 - Open your build.gradle for the app and set your dependency for EventBus:
dependencies { compile 'org.greenrobot:eventbus:3.0.0'}
2 - Create your first model to use in publishing an EventBus, I will use a very simplistic example of a model:
package com.myapp.models.eventbusmodels;
public final class EventBusMyModel {
private final String dataRaw
public EventBusMyModel(final String rawData) {
this.dataRaw = rawData;
}
public String getRawData() {
return this.dataRaw;
}
}
3 - Now all that's left is pushing out a broadcast by using from anywhere.
EventBus.post(new EventBusModel("My Data here"));
4 - To enable Activities/Fragments to receive events from EventBus you must attach and detach, this is what I mean. From inside an Activity on the onResume() and onStop() overrides:
public class SomeActivity {
#Override
protected void onResume() {
if(!EventBus.getDefault().isRegistered(this))
EventBus.getDefault().register(this);
}
#Override
protected void onStop() {
if(EventBus.getDefault().isRegistered(this))
EventBus.getDefault().unregister(this);
super.onStop();
}
}
5 - The final thing to do is receive that broadcast, you can receive it in Any Fragment, Activity, or in all your fragments/activities. Here's an example from inside the SomeActivity:
#Subscribe(threadMode = ThreadMode.MAIN)
public void eventThisNameDoesNotMatter(final EventBusMyModel resultModel) {
String receivedData = resultModel.getRawData();
//Do whatever with receivedData. Since we are on ThreadMode.MAIN, this is on the UI thread.
}
Do You know if there is any way to start a Service from the MainActivity, send some data and when the service is done, get the data from the Service. For example:
//MainActivity.class
private ArrayList myList = new ArrayList();
public void fetchDataFromInternet()
{
Intent intent = new Intent(this, DataFromInternetService.class);
intent.putArrayListExtra("MYLIST", myList);
startService(intent);
//In DataFromInternetService the app fetches data from the internet and puts them in the myList.
/*
TODO:
1. Get the data from the service after the it has finished.
*/
}
//Note: The service runs only 1 time.
Also, If this is not possible, what alternative do You suggest, in which MainActivity has access to the data from the DataFromInternetService.class.
Note: I don't want to use LoaderManager, I use Services to do background tasks that don't effect the UI.
To communicate with your service you can use Bound Service:
Please check this:
https://developer.android.com/guide/components/bound-services.html
or you can send your data within: broadcast receiver, database, shared preferences. It depends on your needs.
Instead of using a service to run stuff on the background you should use Async Tasks
you can do the following:
new AsyncTask<Void,Void,Void>(){
doInBackground(){
//DO STUFF
}
onPostExecute(){
//Do What You want with the results
}
}
}
If you want to communicate with your service you have to use bound service and bind to it in your activity. Then create handlers for your activity and also for your service. Using these handlers you can notify your activity once the download is finished.
Please let me know if you managed to solve your problem or some further instructions are needed.
That's the kind of question where any discussion at this point was already finished few years ago, but still bothers us every time we need to choose the most comfortable approach for our app. I don't thing that I should dive into code samples or low level explanations. You can find more than needed information by yourself. One thing I can be helpful(I hope so) is to share my approach.
When I'm facing issues like how to pass data between two different components? - I treat such situations kind of straight-forward. You need to notify your hungry component(activity in our particular case) that's fetching was finished an it can get his info from a trustworthy place. Or to be more cruel, pass the data directly to it. This can be achieved by different implementations/solutions but the abstract ideology remains the same.
What can you do? Here are some links :
Read docs?
Trust Mr. Snowflake?
Broadcasts?
Magic with handler?
I'm even not talking about binding a service or using Event Bus.
And still there are even more valuable solutions, but those are enough.
I am developing an Android application where I need to check a web service for updates everytime the user wishes to. This involves quite a bit in the background, and I used SwipeRefreshLayout to indicate the updating act.
I assumed the refreshing animation happened async, and independent of any work im doing to fetch the updates, but when I perform a pull, there are visibles stutters in the animation thoughout the process, ruining the whole process visually. Any ideas on why this is happening and thoughts on how to fix it? I havent included code in case that the info i have provided is enough, if not, i will supply the necessary code sample.
The code looks something like the following:
onRefresh()
{
startService(syncServiceIntent);
}
The service then checks the service and updates a local database with any possible updates using an API call that returns an Observable of my data. After finishing, I broadcast it and upon receiving this broadcast, I accordingly repopulate my RecyclerView using another Observable of my data, and finally on
onCompleted()
{
//do some stuff
mSwipeRefreshLayout.setRefreshing(false);
//do some other stuff
}
Why is the animation stuttering?
I'm writing a library that uses a Timer to perform scheduled background processing. One key feature is to conserve battery life, so I'd like to have my library pause the background processing when the app isn't actively used.
Ideally, it would be great if equivalents to Activity.onPause() and Activity.onResume() could be received by my library without having to create an API for it. I don't want to have to rely on the dev, who's implementing my library, to have to call MyLibrary.onPause() and MyLibrary.onResume() throughout the various activities of their app.
Are there any other solutions apart from API's ? I thought maybe there is a broadcast that my library could register for - but I haven't found anything useful at the moment... looking for suggestions...
Thanks !!
You can use registerActivityLifecycleCallbacks() to set up a listener for Activity life cycle events. It's API 14+ but this should not be a big deal nowadays.
There are many misconceptions in your questions so it's hard to answer but I can give you a few advices.
First you should never use a Timer to schedule work on Android. You should use Handler to schedule UI events and AlarmManager with a Service (like IntentService) for background work which is independent from Activities.
If the work is directly tied to your Activities, you should not schedule the work using AlarmManager but use a bound Service. This kind of service automatically stops when no Activity is connected to it.
If the work is independent from your Activities, then the best way to save battery is to choose an appropriate running frequency in AlarmManager and use inexact repeating. This way the system will likely run all pending tasks in a single batch to save power.
I invite you to read the documentation of all the components I mentioned.
You can use some few Hacks
View.isShown()
by taking in an Activity as a context, in your library, and continuously checking for isShown() on its DecorView
MyLibrary(Context context){
if(context instanceof Activity){
boolean activityStatus = ((Activity)c).getWindow().getDecorView().isShown();
//true means it is shown, and you start your loop and keep checking something like this
Thread t = new Thread(new Runnable() {
#Override
public void run() {
for(;;){
boolean tempStatus = ((Activity)c).getWindow().getDecorView().isShown()
&& activityStatus && /*((Activity)c).getWindow().
getDecorView().isLaidOut() if api 19+*/;
if(tempStatus){
//it is still showing or its previous value is being maintained
}else{
//it has been changed..
}}}});}//ending braces
some limitations will be that, when a DialogFragment is shown it might check out true, maybe or maybe not, but you can check if the current focus on a View is on that Window by doing getWindow().getCurrentFocus() which will return a non-null; you can try this and see
Activity.hasWindowFocus()
which will always return false on onPause() and true when your Activity is on
I'm refactoring an app I made some time ago. Back then I was taking my first steps into Android and the easy way to go was to avoid orientation changes, for almost all my CRUD operations I used the AsyncTask class, didn't implement a Content Provider or used Fragments.
Now I have made some changes:
I use Fragments to encapsulate functionality
All accesses to my database are done through a Content Provider
I use CursorLoaders to retrieve my data taking advantage of the content observation and automatic background loading the LoaderFramework brings.
The only problem I have now is that I'm not quite sure how to handle the rest of the CRUD operations (Create,Update, and Delete).
I've found that I can use the AsyncQueryHandler class, but there's not enough information online.
One thing I like the Loader Framework, is that is aware of the Activity or Fragment lifecycle, but what about the AsyncQueryHandler?
What happens to the operations that were started with startDelete, startInsert, startUpdate when the Activity/Fragment undergoes a configuration change? Or when I press the back button? Or worse yet, if the activity is destroyed?
What is the expected behavior of this kind of operations in such cases? Should they be cancelled or should they continue to do their work?
All the operations I've mentioned above are not that complex. For real complex operations I've used Services or IntentServices, but since I don't consider a good idea to run SQLite operations on the main thread I want to use a better solution, but first I need to know how that solution should react to the Activity/Fragment lifecycle events.
Any comments or suggestions would me greatly appreciated.
Thanks.
If you use AsyncQueryHandler you have to take into consideration that this abstract wrapper is created and executes provider operations on the thread that it was started on. If you call this from UI thread the callbacks will be sent to the UI thread. If you call this from another working thread, the callbacks will be sent to that working thread.
This is not bound to the lifecycle of the fragment or the activity, ContentProviders don't have an lifecycle.
The AsyncQueryHandler at an basic level, creates an Message object which is added to the MessageQueue of an single background thread. No matter from which activity/fragment you use the AsyncQueryHandler all the Message objects will end up on the same MessageQueue.
Once the background thread processes an request it will send the response back to the instance of the AsyncQueryHandler from which the request was initially made.
My recommendation is to use the Loaders from Android. These are directly tied to the lifecycle of the activity/fragment. You can have multiple Loaders in a LoaderManager(one LoaderManager per activity/fragment) which allows to do more complex operations. Your activity/fragment will automatically be notified when the content has changed(very useful when you want to combine it with your custom methods for CRUD operations or if you need to use long running services). Another very important feature they have is that they will always reconnect to the last Loader, thus you will avoid re-querying your content.
I recommend you search for some tutorials for implementing Loaders in Android. You can start with these:
Loaders - part 1
Loaders - part 2
Loaders - part 3
Loaders - part 4
Answer for your last comments:
I suggest to use the EventBus library (https://github.com/greenrobot/EventBus) to make the communication between your AsyncTasks/Thread and your other components.
You can start by creating an abstract AsyncTask/Thread and on top of that to make your specific command.
For example:
public abstract class AbstractThread extends Thread {
#Override
public void run() {
super.run();
command();
}
abstract void command();
}
In the abstract class you could make some DB initialization, verification or anything else that might make sense for your application.
public class InserTask extends AbstractThread{
#Override
void command() {
//TODO: Add logic for the insert task
}
}
public class UpdateTask extends AbstractThread{
#Override
void command() {
//TODO: Add logic for the update task
}
}
In these specific classes, just add your logic of the CRUD operation.
To have control over these threads, like when they should be stopped, paused, resumed you could create and ThreadPool manager which controls your threads. You can read more how to achieve this here: Thread Pool