I have an application that needs to track my location, read a sensor while doing this and update a Activity with information (i.e. my current speed). The Activity also has a "stop" button, that allows the user to cancel the background job. The data collection should continue if the user navigates away and notification should allow the user to get back to the view showing the information.
So far I have:
a Service
that has a private static MyService instance field and a getter for it
it sets instance = this in onStartCommand(...)
it also has a getter for the current data (wrapped in an object)
it provides a goForeground() method that calls startForeground(...) and shows a notification (goBackground() does the inverse)
an Activity
that calls MyService.getInstance() in onCreate(...), if it returns null it creates the service with startService(someIntent)
an updateViews() method that pulls the data from the Service and uses a Handler to schedule calls to itself to update the Views every 500ms
This works.
However I'm pretty sure that this is not the recommended way to do this. It's ugly as hell and all calls to the return value of getInstance()are wrapped in a not null check. Additionally all the work is happening in the UI thread (which will certainly cause problems when I add more calculations to the Service). Also, I would like to get rid of the Activity polling the Service for data.
What is the recommended way to do this?
The Android documentation is not very clear at this point and everything I found with Google was either outdated or solved different problems. Please keep in mind:
my background job is essentially endless and will not be "finished" at some point in time. Only the user can decide this (this rules out an AsyncTask, or not?)
I need to pass some non primitive date from the background to the foreground
I need to control the background from the Activity
the Activity should be able to "reconnect" to the background if it is closed and reopened
As you said pulling the data from the service is not the best practice, fortunately there is such thing called a BroadcastReceiver, this class wake up when an intent sent and the receiver is configured to receive it (in the manifest).
This allows your service sending the data when ever its ready, or the other way, your activity can send a request for new data.
note that an intent can hold data, thus you don't need to hold any static instances to services, just pass you data to an intent
Related
My app makes frequent use of the following pattern:
User clicks button
API request is fired
response is parsed
Callback returns data Data is updated in view.
However, due to the nature of the API these callbacks can take some time and I find that I can easily crash the app if I navigate around the app at a high pace. Mostly this is caused by NullPointerExceptions related to the fact that the activity and/or fragment no longer exists. My question is what the best practice is when dealing with these issues. Should I just check for null values everywhere? I've read somewhere that you should just avoid using callbacks to update the UI at all but I'm not sure what the alternative is.
Thank you all!
for Fragment you can check by isAdded()
public void onResponse(){
if(isAdded()){
// Do your stuff here
}
}
In activity isFinishing()
public void onResponse(){
if(!isFinishing()){
// Do your stuff here
}
}
Let me share how I did in one of my apps.
I created a class which extends Application and that class is responsible to initiate a database. It is Singleton Static database and everytime I need to do something, I call db.getInstance().doSomething()
When any API method is called, I start an AssyncTask which store the data on database after completed (in case of failure, nothing is saved).
When Database is updated, it sends a LocalBroadcast. You can send broadcasts to notify the error (which stops the Refresh animation and show a error message, for example).
Each activity has a BroadcastReceiver which register to receive the local broadcast sent by Database. I register during onStart() and de-register during onStop(). Each activity register to proper event (since you can create multiples intents and actions... This way, your activity receives only the desired intent and not every single broadcast of your app.
This way, when the activity is opened, it checks the data from database and if any content change, it receives a broadcast notification and take proper actions.
When the activity is closed, it no longer receive broadcast.. However, the updated data will be there on database after download is completed.
You must handle situations where some API call was already started to avoid calling twice (at least, until first call of same method finishes etc)..
You can also use ContentObserver to monitor some database etc.
This is one way to handle. It may work or not for you case etc... Just sharing since it may help you
I have my MainActivity which gives the user a selection of pages to open, all of which involve downloading some data from the internet and displaying it. To save the user waiting when they choose their page I've made an AsyncTask as a subclass of MainActivity which produces an object DATAwhen the download is complete.
How would I pass DATA on to the SecondActivity in the following circumstances:
The user chooses the SecondActivity before the AsyncTask download has completed.
The download completes before the user chooses the SecondActivity.
the AsyncTask doesn't have to be a sub-class of MainActivity its just been tidy to do it that way so far,
thanks for the help!
Here's one way to do this:
Create a reference to your data in your Application. The Android Application is a good place to store global data. Next, populate the data via your AsyncTask (Watch out for the pitfalls of using an AsyncTask). You can now access your data via a call similar to this: ((MyApplication)getApplication).mydata
As you mentioned, two scenarios can come up. Either the data has been populated, or not. To handle this, use an observer that observes changes to the data. Have SecondActivity register as an observer when the data is null. When the data is available your SecondActivity's update method will get called and you can do whatever you please with it. Finally, make sure to unregister from being an observer.
Hope this helps.
Passing information directly between activities works only if it is Parcellable (via Intent). Almost anything could be made Parcellable but it is not always a good idea especially when the amount of data is large.
The next problem is that your AsyncTask most likely keeps the Context of your first activity alive when it is running longer than the activity lasts. Activity instances are quite often recreated when you rotate the device and naive implementations tend to start another asynctask in the new instance and end up with multiple tasks that download the same data. You would need to pass the reference of a running task between instances of the same Activity.
The simplest solution is probably to create a singleton (or a Service) accessible from both activities that hosts the AsyncTask & loads the data. If it requires a Context use getApplicationContext() since that's safe to use outside the lifetime of Activites.
Activities could register themselves as listeners for "data loaded" events while they are active.
I've recently struggled with AsyncTask and had difficulty having the UI behave while the task was running in the background. While there are comments around that services aren't really appropriate for the sort of thing you're describing, I've found them much easier to work with. You might check intentService as a middle ground. Good tut's can be found here and, specifically concerning intentService, here.
I'm creating an app that will do the following:
If a mail comes in with a messageword. It needs to start to locate the phone by GPS functionality. after that it must show the result to the user.
I already have it working with using a broadcastreceiver for fetch the message. After that is starts an Activity. If don the userinterface is updated.
I have here two problems:
1) if the screen rotates all variables are set to zero... well can solve that with saving variables at onPause() and read those values at onResume()
2) if the user use the backbutton... the thread is cancelled.
Actually I want that the user can't cancel the GPS-action.
Is it a good idea to use an Intentservice for this?!
-message comes in
-BroadcastReceiver fetch the message and start the intentService
-if the processing is done... I can start an activity with the results (and the service is closed automaticly after the processing is done?!
Is there maybe a better way to attack the problem?
You certainly can and should use an IntentService for long running operations in the background. However, if the user backed out of your activity, they most probably did this for a reason, and popping up an activity with results they might not care about anymore, might not be such a good strategy. You could cache the result instead, and show it next time the user opens your activity.
I have implemented some computationaly heavy functions into my application. Depending on the type of input it can take up to several minutes for a thread to return. During this time, I want the user to be able to work with other activities to perform different tasks (i.e. prepare data for the next run). Then, after the Service has finished it's computations, the user should be notified with a Toast that the result is ready and that he should check back to the Activity he started the Service in. Unfortunately I'm stuck at this point.
Is it possible to somehow communicate with an Activity which is destroyed at the moment? Like modifying the saved state, so that when it get's recreated the result will be displayed. The only way of communication I did find was via broadcasting from the Service, but this requires the Activity to listen, which is not possible as it doesn't exist at the moment the Service finishes.
The only solution that occured to me was writing a file when the Service is finished and then trying to read it in the Activity, but I would prefer not to work with the file system if that's possible.
Am I missing something here or thinking in the wrong direction?
use Asynctask
http://developer.android.com/reference/android/os/AsyncTask.html
You could just write to a SharedPreference from the Service.
The Activity can check the preference whenever it is started and you can have some marker to indicate that the result was from your computation.
You could also write to the same SharedPreference from the Activity to nullify it so that it can be used for the next result.
I would use an AsyncTask, and return the value from your computation to some stateful location (use an Application class, or SharedPreferences even).
Have the AsyncTask launch your Activity on completion, and get the value you statefully stored to display.
Basically, combine the 1st 2 answers and you should be set.
I want to pass a parameter between two activities at a certain time interval. The passed parameter is an int, that represents the number of satellites seen by the device and is used to draw a chart in the second activity.
There is no harm in keeping a GpsStatus Listener in both of those activities provided that you stop GPS properly when not needed. :)
But anyway, If I were you I'd let the Application class hold on to these details. I'd use a Service to listen to the GpsStatus and the Application class bind to this service. Your Application class is now the gateway. Your activities can now smoothly communicate with the Application data, instead of the clumsy Intents.
This pattern is taken from here
It is very likely that you are trying to do something in a strange way because you do not fully understood yet how activities in Android are working.
There is only one active Activity in your whole app. Sending data from one activity to another is only needed if the users changes the active Activity, either through clicking on a button and switching to a new Activity or through clicking back and going back to the previous Activity. Please read the Activity Lifecycle very carefully to understand how activities should be used.
I assume that you have registered a location listener in one activity and try to use the values of this listener in other activities. Normally you would use the onPause method to deactivate the listener in the first activity and then activate a new one in the new activity. This will get you the number of known satellites in every activity. If the user goes back you will have to reregister the listener in the onResume method to reenable the gps updates for the new Activity.
Keeping an active GPS Listener in a paused Activity will lead to an active GPS Sensor even if your App is in the background and not used anymore. This will drain the battery of the phone very fast and very likely have your app uninstalled in an instance.
You could use a shared preference to store the int in a kind of variable that both activities can access.
Otherwise, try a custom intent and implement a receiver in one or both Activities. This is a little harder to implement, but with the receiver you can act as soon as the value changes.
If it is rather important to do it in the time interval, use a shared preference ;-)
You can also pass bundles between two activities. That Intent can be used for storing
parameter values in PutIntent.
More information here: http://remwebdevelopment.com/dev/a33/Passing-Bundles-Around-Activities.html