I have a listview contains many songs. Each time a song is clicked, I call an IntentService to download this song by sequence.
But sometimes I want cancel a download (for example: the 5th). It means I need to stop the 5th running IntentService. I attempt to stop it by call stopService() but it doesn't work.
Can anyone tell me a good way to stop IntentService? Thanks so much for your help.
There is only one running IntentService, per <service> element in your manifest. If you call startService() six times, at most one IntentService will be running. Commands will queue up and be processed one at a time by onHandleIntent(), if the IntentService is processing a command when another command is sent.
In your case, since you cannot readily cancel an outstanding command with IntentService, you will probably need to create your own version of IntentService, where you create your own Service subclass with its own work queue, one where you have the ability to cancel commands that are not yet running.
I had the same problem while downloading using intentservices.
In my app, each download has its own notification, and when clicking on it I want to stop the download.
I solved it by overriding the onStartCommand() method (called when a new intent arrives).
When I wanted to close one download, I started a new intent with an extra called "stopid":
Intent stopIntent = new Intent(DialogActivity.this,DownloadIntentService.class);
stopIntent.putExtra("stopid", id);
startService(stopIntent);
The onStartCommand() method looks like this:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
int id = intent.getExtras().getInt("stopid");
if(id!=0) {
stopList.add(id);
}
return super.onStartCommand(intent, flags, startId);
}
For normal intents, just put the id of your download in your intent as an extra.
While doing the download in the onHandle() method, check often if the stopList contains the id of your current download intent. If it does, stop downloading and remove the id from the stopList.
Related
Basically I have a service running to check my location every 30 minutes and when the location changes, pop up a basic notification. I only need this service when my app is closed (on stop/pause too). So im asking where should I start my service in my activity?
I want something similar as Facebook,Instagram and most of apps have... a service running from the background and when a notification pops up just open de app. While the app is open the service shouldnt do anything.
I tried onDestroy() method (in my MainActivity) but didnt work out, and onStop() method neither.
Do i explain myself?
Thanks!!
Recurring tasks should be scheduled using AlarmManager (or JobScheduler).
This is usually done in a BroadcastReceiver which reacts to BOOT_COMPLETED.
If you want to cancel such a job while your activity is active, call the respective methods on AlarmManager in onResume and onPause.
If you want your service to be running in the background at all times event after closing the app, you need to make your service STICKY and you can do that in the OnStartCommand
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("LocalService", "Received start id " + startId + ": " + intent);
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
Note that it returns START_SICKY which tells the OS to recreate the service after it has enough memory and call onStartCommand() again with a null intent.
Read also about application:persistent which is "Whether or not the application should remain running at all times". This is more troublesome - System will try not to kill your app which will effect others in the system, you should be careful using it.
My Android application is activating a service that call an 'Activity.
On mainActivity:
startService(new Intent(getBaseContext(),MyService.class));
And then on service:
public int onStartCommand(Intent intent,int flage,int startId){
// Toast.makeText(this, "Yes please", Toast.LENGTH_LONG).show();
Intent mIntent=new Intent(MyService.this,trackingActivity.class);
mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(mIntent);
return START_STICKY; }
At the end of trackingActivity this line is written (Again):
startService(new Intent(getBaseContext(),MyService.class));
That's creates a lot of Services. Is there a better way to create a background service that repeats itself always without creating each time a new Service ?
I tried to do a while loop inside the Activity :
while(true){
Actions on activity
}
But with no success.
Context.startService(Intent) does not create a new Service for each call.
If there is already a matching service running, it passes the intent to that running service, but does not create a new one each time.
See the Android Developer Docs for Context.startService(Intent):
If this service is not already running, it will be instantiated and
started (creating a process for it if needed); if it is running then
it remains running.
Every call to this method will result in a corresponding call to the target service's onStartCommand(Intent, int, int) method, with the intent given here.
There is similar information in the Android Developer Docs about Starting a Service:
[When starting a service using an intent] the startService() method returns immediately and the Android system
calls the service's onStartCommand() method. If the service is not
already running, the system first calls onCreate(), then calls
onStartCommand().
...
Multiple requests to start the service result in multiple
corresponding calls to the service's onStartCommand(). However, only
one request to stop the service (with stopSelf() or stopService()) is
required to stop it.
I have a android Service, in the onStart method i get many Strings from the Intent activity, and then execute a AsynTask to download files from internet.
When the activity is running this works ok, but when i stop the activity, this relaunch the onStart method, but obiusly the intent is null causing me nullPointerException.
What can i do the service dont entry on onStart, and continues the first asyntask to download all the files?
This is my code
#Override
public void onStart(Intent intent, int startId) {
desde = intent.getIntExtra("desde", 0);
hasta = intent.getIntExtra("hasta", 1);
email = intent.getStringExtra("email");
password = intent.getStringExtra("password");
new DescargaFotos().execute();
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
and in Inicio.java (The UI activity)
Intent iService = new Intent(contexto,
ServiceDownloader.class);
iService.putExtra("desde", 0);
iService.putExtra("hasta", 5);
iService.putExtra("email", email);
iService.putExtra("password", password);
startService(iService);
EDIT:
New question:
I am using IntentService and i bind the service like this:
Intent iService = new Intent(contexto,
ServiceDownloader.class);
ServiceConnection serviceConector = new ServiceConnection() {
#Override
public void onServiceDisconnected(ComponentName name) {
Log.i("INFO", "Service bound ");
}
#Override
public void onServiceConnected(ComponentName name,
IBinder service) {
Log.i("INFO", "Service Unbound ");
}
};
iService.putExtra("desde", 0);
iService.putExtra("hasta", 50);
iService.putExtra("email", email);
iService.putExtra("password", password);
startService(iService);
bindService(iService, serviceConector,
Context.BIND_AUTO_CREATE);
And now my problem is, in my galaxy nexus, if i enter in app runtime list, and destroy my app, the service stopped, and stop donwload, stop send notification etc? How must i bind the service to solve this?
Use an IntentService. IntentService is specifically designed to do what you want. You don't have to implement onStart, onStartCommand, etc. The work runs on a background thread. Once the work finishes, the Thread is destroyed.
IntentService will continue to run regardless of the state of the Activity.
One problem you may have is that you're sending out your Intent in the wrong place in your Activity, or you're not checking to see if the operation is complete. Before you send the Intent, check a flag in SharedPreferences (if the flag doesn't exist, it means you're starting for the first time). When you send the Intent, store a flag in SharedPreferences to indicate that you sent it. When your IntentService receives the Intent, have it update the flag to say it received it. Before the IntentService finishes, have it update the flag again. And so forth.
I do not have reputation to comment your question, so I will write a answer :)
First off all, in my opinion, if your Service do just this, get some string and download files from the Internet, you do not need the service. The AsyncTaks is enough to solve your problem and it is more simple to implement. Doing that, you avoid the problem with your intent.
From Android API:
AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.
However, if you decide to continue using a service, lt me know exactly when startService is called in your activity.
If you have some doubt about when use AsyncTask, Service, IntentService and Thread, this link can help you.
This would be my suggestion...
Use a thread in the service rather then AsyncTask since AsynTask should only be used small task. Check following statement from AsyncTask javadoc:
AsyncTask is designed to be a helper class around Thread and Handler
and does not constitute a generic threading framework. AsyncTasks
should ideally be used for short operations (a few seconds at the
most.) If you need to keep threads running for long periods of time,
it is highly recommended you use the various APIs provided by the
java.util.concurrent pacakge such as Executor, ThreadPoolExecutor and
FutureTask.
So from your on start check the status of your thread, if it is running then don't do anything, if it is not running then start the thread that would download your images...
My application synchronizes data with a remote database via web service calls. I make these calls in an IntentService so they can run in the background (I call it SyncService).
The code to launch my IntentService looks like so:
Intent intent = new Intent();
intent.setClass(appContext, SyncService.class);
// place additional values in intent
intent.putExtra("data_type", SyncService.ITEM_TRACKING);
intent.putExtra("user_id", intUserId);
// call SyncService
appContext.startService(intent);
This, normally, looks great. However, one of my friends, who is also a user of my app, often tells me his data doesn't sync and get displayed on our website. His device happened to be displaying the symptoms while I was around. I plugged his device into my computer and here is what I found:
The code to launch SyncService (ie: the code above) was hit.
I had a breakpoint inside the onHandleIntent method of my IntentService and it never gets hit.
I checked his device's list of running services and SyncService was there and running. Interestingly, it had been running for about 20 minutes. I was under the impression that IntentService killed itself when it was all out of Intents to process.
I force stopped the SyncService (not the app) and, all of the sudden, onHandleIntent started getting hit over and over. It was like all the Intents were queued up somewhere on the device and were just now getting thrown at the SyncService.
Does anyone have any ideas as to what may be the problem? Do you think it's an issue with my app? With Android?
Again, I am handing a message to Android saying, "Start this IntentService or send the message to the already running IntentService." At that point, I have no control. The message never gets to the IntentService. Once I force quit the app, the messages get sent to the IntentService and it does its job.
UPDATE: I think this code is fine, but I'll put it up since a lot of you may want to see it.
Every Intent that comes in to the IntentService has an Extra denoting what "type" of call is to me made (ie: do I call this web service or that web service, etc). When an Intent comes in to the IntentService, I check the "type" and, if there is already an Intent in the queue for that type, I add an Extra to it called "skip" so, when it is reached, I don't execute the search (basically the IntentService can build up lots of Intents and it makes no sense to call this web service when this webservice was called 20 seconds ago). It basically protects the app from spamming the website.
It is important to note that none of this code is hit anyway (once the problem starts occurring). onStartCommand does not get called until the app is killed
#Override
public int onStartCommand (Intent intent, int flags, int startId) {
// here be dragons
// overriding this method and adding your own code is dangerous. i've wrapped
// my code in a try/catch because it is essential that the super method be called
// every time this method is entered. any errors in my code should not prevent this
// or the app will explode.
try {
if (flags == 0 && intent != null && intent.hasExtra("data_type")) {
Integer intDataType = intent.getExtras().getInt("data_type");
if (!mCurrentTypes.containsKey(intDataType)
|| !mCurrentTypes.get(intDataType)) {
mCurrentTypes.put(intDataType, true); // put this type in the list and move on
}
else {
intent.putExtra("skip", true); // mark this Intent to be skipped
}
}
}
catch (Exception e) {
// Log.e("Error onStartCommand", "error: " + e);
}
return super.onStartCommand(intent, flags, startId);
}
private void processIntent(Intent intent) {
// do stuff if no "skip" Extra
mCurrentTypes.put(intDataType, false);
}
There is definitly something that keeps your service running on your friend's device. If so all subsequent call to this intent service are queued until the current one finishes. If it doesn't finish then you will get what you have : next services won't start.
You should double check that :
you give proper timeouts to nework operations
you give proper timeouts to nework connections operations
there is no race condition between threads.
you log any exception that can occur inside the service, you don't wanna loose that kind of information.
Afterwards, if you think everything is green : just log what the service does and use some bug reporting mechanism to get it automatically sent from your friends device. A simple solution could be to use bugsense or equivalent.
Next, put in place some kind of watchdog : a thread that will go on running until your service stops (you just tell your thread to stop when service is stopped). The thread will have to stop your service after some time limit has been passed.
This watchdog thread could be put inside the service itself, or outside, although this may be more complex to put in place.
This answer suggests a solution that worked for me in similar situations. It doesn't fix your current code but suggests another, perhaps simpler (and easier to debug) option:
Add a BroadcastReceiver to your calling Activity that listens for SUCCESS Intents from the IntentService.
In your calling Activity, include the logic for when to start the IntentService (and don't include it in the IntentService). The logic is:
Call startService() and set a flag in the calling Activity to CANNOT_CALL.
If the Activity's BroadcastReceiver has not received a SUCCESS broadcast from the IntentService, then startService() can not be called again.
When the Activity does receive a SUCCESS intent, set the flag to CAN_CALL, and startService() can be called when the timer hits again.
In your IntentService, write your onStartCommand() like so:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
In you IntentService, when you've received, parsed and stores the web service response, call sendBroadcast() with an Intent with custom action SUCCESS.
This logic is just an outline and has to be fine-tuned for error messages from the web service that have to be broadcast from IntentService to the listening Activity.
Hope this helps.
It seems to me that setting a set of flags to your Intent may solve the problem.
Intent intent = new Intent();
intent.setClass(appContext, SyncService.class);
// This way
intent.setFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK|Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
You can make your Service start as fresh using the above flag in a fresh task.
One more comment. It's not an answer for your question. However, it may affect overall behavior of a service.
You do following:
return super.onStartCommand(intent, flags, startId);
Internally Service.onStartCommand() looks like following
public int onStartCommand(Intent intent, int flags, int startId) {
onStart(intent, startId);
return mStartCompatibility ? START_STICKY_COMPATIBILITY : START_STICKY;
}
mStartCompatibility is false if your app target SDK API 7 or later (which is most likely a case).
So, as result your service will be started as START_STICKY.
Here is piece from documentation:
For started services, there are two additional major modes of operation they can decide to run in, depending on the value they return from onStartCommand(): START_STICKY is used for services that are explicitly started and stopped as needed, while START_NOT_STICKY or START_REDELIVER_INTENT are used for services that should only remain running while processing any commands sent to them. See the linked documentation for more detail on the semantics.
Base on what you have described, I recommend to replace "return super.onStartCommand(intent, flags, startId);" to "return START_NOT_STICKY;"
I have a questions here. I wanted to communicate to my service from my broadcast receiver. So I used the StartSevice(intent) method. In the service side the OnStartCommand() is called everytime I called StartService(). I just need to know how can i recognize in the onStartCommand() from where it is called from?
I hope I am clear in my question. If not please let me clarify...
You can't really check where you're called from, but you can do something a little bit related. You can specify certain tags in the Intent that you pass to the onStartCommand(). Notice that onStartCommand() takes an intent as its argument, this will be the intent that you sent. Whenever you send an intent to start the service from your broadcast receiver, you can specify certain "extras" in your intent and then use get those in your service.
http://developer.android.com/reference/android/app/Service.html#onStartCommand(android.content.Intent, int, int)
By the way, the use case you give seems to line up more with an IntentService than a regular service. This is a service which does what you're doing already: it sits there and waits for intents to be fired at it, and then reacts accordingly:
http://developer.android.com/reference/android/app/IntentService.html
In the onStartCommand method, the Intent you used to start service is passed as an argument.
onStartCommand(Intent intent, int flags, int startId)
So you can add Extras to intent to distinct where from it is called.
intent.putExtra("From", "MainScreen");