I see this example with programs like Advance Task killer, Watch Dog, eBay, battery widgets, ect. There is a background service running that monitors device activity, a broadcastreceiver, but there is an option to disable the notification icon displayed. Currently my application works flawlessly by calling:
Context context = getApplicationContext();
Intent intent = new Intent(context, SomeBackGroundService.class);
context.startService(intent)
and then in my service I am calling:
startForeground(NOTIFICATION_ID, notification);
Now how is it that these other applications can have a long running broadcastreceiver running without a notification icon? What do I need to do in order to disable/set ect as to hide the icon and add this feature to my application?
Thanks in advance
I'm not expert on this, but I disagree with the accepted answer.
Your startForeground is key to ensuring that your service continues running.
Without that, it might keep running, but it has a much lower priority so it is much more likely to be killed by the system.
Your service is entirely independent of whether or not a notification exists for it. If you removed the line, startForeground, the service would continue to run. The only time a service stops is if you call context.stopService() or stopSelf(). Throw some logs into your service, and you'll see that it continues to run.
Override the onStartCommand to determine behavior of your service after garbage collection:
#Override
public int onStartCommand(Intent intent, int flags, int startId() {
super.onStartCommand(intent, flags, startId);
doWork();
return START_STICKY;
}
START_STICKY recreates your service after garbage collection. START_REDELIVER_INTENT will recreate your service after garbage collection using the original intent, use this only if you need extra intent data.
Create a notification that contains an icon made with a png and is only 1 pixel in height and width and make that pixel transparent.
Related
I want to stream an mp3 file that has a length of about 1 hour. I want to play the file in background. I saw many tutorial videos. Some of them used service and some used asyntask. I dont know what can i choose between these too. Which is the better one?
You'll definitely want to use a service, and not an AsyncTask. The main reason for this being that, if you want the music to run even when the app has been suspended/put into the background, like when the user moves to another app, only a service will do this. AsyncTasks will not run in the background in that way.
To include some background information about background services, they use events from app contexts such as activities and foreground services to notify them of when to do work. This work is handled via the service's onStartCommand() function. More can be read about services in the Android docs https://developer.android.com/guide/components/services
That being said, a service will allow running in the background, but it can still be preempted if the OS needs to complete another task. Therefore, for the music to play reliably, and restart shortly after the OS has preempted the service for any reason, you will need to specify START_STICKY as the return value from the service's onStartCommand() function. But, like with everything Android, prefer the compatibility version, START_STICKY_COMPATIBILITY, to the not compatible version. START_STICKY/START_STICKY_COMPATIBILITY is appropriate to return in the case of the PLAY command. I.e. if the event the service is receiving is PLAY.
Returning START_STICKY or START_STICKY_COMPATIBILITY from the onStartCommand() in every case will wind you up with a service that never dies, thus consuming processing power and battery life from the phone running it. This could cause processor consumption and a drain on battery. That is why it is important to return START_NOT_STICKY from the onStartCommand if the user is attempting to pause. I.e. if the event the service is receiving is PAUSE.
Here is a stripped down version of what you might want your onStartCommand of your service to look like:
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent.getAction().equals(ACTION_PLAY)) {
...
return START_STICKY_COMPATIBILITY;
} else { // i.e. action is ACTION_PAUSE
...
return START_NOT_STICKY;
}
}
Edit: To caveot this and the remainder of this answer - In an attempt to simplify the post, I excluded considerations for waiting for the mediaplayer to prepare. As a note, the service will likely also need to handle waiting for the mediaplayer to prepare with either a separate event, or from within the handling of the PLAY event. The could possibly also be handled from within the activity before starting the service but this may be more or less complicated. Explaining the rest of the issues/considerations in this answer is much easier without talking about this aspect of the problem, although it will have to be considered to make a functional music player app.
Provisions for when the device is locked are also required so that some hardware peripherals don't shut off. Consider adding the following in response to the PLAY event in the service's onStartCommand to account for this:
// Setup Wake Mode Wake Lock so the music can play while device screen is locked
mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.FULL_WAKE_LOCK);
// Setup wifi lock so wifi can stay on while device is locked and trying to save power
wifiLock = ((WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE))
.createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");
wifiLock.acquire();
Another issue is that your user will ideally be able to kill the service if it is running. If they kill the app, it will not kill the service, as intended, and the music will keep playing. So the user should be able to control the service via a notification with controls to pause and play the music. This can be done using a foreground service. If you want to add the foreground service layer, you can add a call to startForeground() in the onStartCommand() of the service in response to the broadcast event for PLAY. Here is the stripped down onStartCommand() with the foreground logic added:
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent.getAction().equals(ACTION_PLAY)) {
...
Notification notification = setupNotification(); // where setupNotification is your own function
startForeground(1, notification);
return START_STICKY_COMPATIBILITY;
} else { // i.e. action is ACTION_PAUSE
...
stopForeground(false);
// NotificationManagerCompat needed here for swipeable notification b/c service will be killed
Notification notification = setupNotification();
NotificationManagerCompat nmc = NotificationManagerCompat.from(this);
nmc.notify(1, notification);
return START_NOT_STICKY;
}
}
The startForeground() functions takes an id and a Notification object as its params. The notification can be created with the NotificationCompat.Builder with code that looks something like this (noting that some variables here will need to be subbed out for your respective application):
Bitmap icon = BitmapFactory.decodeResource(getResources(),
R.drawable.ic_launcher_round_large);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setContentTitle(getString(R.string.app_name))
.setContentText("Music is now playing") // change to paused on paused
.setTicker("Music Playing")
.setSmallIcon(R.drawable.status_bar_icon_xhdpi_48px)
.setLargeIcon(Bitmap.createScaledBitmap(icon, 128, 128, false))
.setContentIntent(pendingTapIntent)
.setDeleteIntent(pendingSwipeIntent)
.addAction(iconId, buttonText, pendingButtonIntent)
.setWhen(System.currentTimeMillis());
Note the pending intents in the code above. These are created with the PendingIntent class.
I.e. create a pending intent for a play button on the notification like so (where "this" is the background service, assuming you are creating this intent from within the background service)
Intent playIntent = new Intent(this, MyService.class);
playIntent.setAction(ACTION_PLAY);
PendingIntent pendingPlayIntent = PendingIntent.getService(this, 0, playIntent, 0);
Likewise, create a pending intent for when the user taps on the notification so that it opens the app with the following code (again, where "this" is the background service, assuming you are creating this intent from within the background service):
Intent notificationIntent = new Intent(this, StreamActivity.class);
notificationIntent.setAction("ACTION_MAIN");
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Furthermore, create the pending intent for swipe action on the notification to kill the background service like this (again, where "this" is the background service, assuming you are creating this intent from within the background service):
Intent swipeIntent = new Intent(this, MyService.class);
swipeIntent.setAction(ACTION_END_SERVICE);
PendingIntent pendingSwipeIntent = PendingIntent.getService(this, 0, swipeIntent, 0);
Hopefully this covers enough to get you going, but I would recommend starting this process without the foreground activity layer of complexity. Then add it once you see the music play from within the app is working.
If you want the music to play even after user exits the app, you need to use a foreground service for this. All music players use this approach.
If you want the music to end when user exists a perticular activity, only in that case would consider anything other than the service.
Async Task should not be used for this purpose as it is ideal for small background tasks. Playing music is not in this category.
I need a service to run in background and calculate every minute distance between two locations. I used Thread in order to execute a method every minute, then I understood that when the application is closed, the service stops too since application and service use the same thread.
How can i create a simple method that is invoked every 1 minute, in background even when the application is closed?
You can run the Service in a separate process by modifying the manifest:
<service
android:name="com.example.myapplication.MyBackgroundService"
android:exported="false"
android:process=":myBackgroundServiceProcess" >
</service>
But that might not really bring any benefit. And most of the time it may even be a bad idea.
Of course the main thing is that if the Service gets shut down it's then re-started.
The Service's onStartCommand() can return the START_STICKY flag:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Other code goes here...
return START_STICKY;
}
This (and the other) options are explained in the documentation. Basically START_STICKY means "Hey Android! If you really really have to shut down my precious Service because of running low on memory, then please please try to start it again."
Whereas START_NOT_STICKY would mean "Nahh...don't bother. I'll call startService() again myself if I really need my Service running."
This (start sticky) is probably fine most of time. Your Service will just start from scratch again. You could try if that's suitable for your use case.
Then there are "foreground services" which are less likely to get shut down by Android as they are treated more like visible apps. And in fact they show up in the notification drawer with an icon and (if you make it so) a status text. So they are visible to the user like e.g. SportsTracker, Beddit and such apps.
This involves modifying your Service's onStartCommand():
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Tapping the notification will open the specified Activity.
Intent activityIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0,
activityIntent, PendingIntent.FLAG_UPDATE_CURRENT);
// This always shows up in the notifications area when this Service is running.
// TODO: String localization
Notification not = new Notification.Builder(this).
setContentTitle(getText(R.string.app_name)).
setContentInfo("Doing stuff in the background...").setSmallIcon(R.mipmap.ic_launcher).
setContentIntent(pendingIntent).build();
startForeground(1, not);
// Other code goes here...
return super.onStartCommand(intent, flags, startId);
}
The Service is started as usual and you can get out of the foreground mode with:
myBackgroundService.stopForeground(true);
The boolean parameters defines if the notification should also be dismissed or not.
You have to use thread for this and set a flag on starting the service. And check that flag for stoping the service.
In addition to the previous solutions provided by our friends...
MAKE SURE YOU CHECK THE "Allow background activity" BUTTON IN YOUR APP'S SETTINGS!
See the picture: In the Battery section
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.
As the title says I am having the following problem. My foreground service is being killed when the activity that started it is swyped away from recent tasks list.
I am starting a service with
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
startForeground(notificationID, notification);
return START_STICKY;
}
Can someone please tell me why is this happening and how can I make it so the service stays running when user swypes the activity away.
I don't have access to public void onTaskRemoved (Intent rootIntent) for some reason but I don't know what to do in that method anyway...
I am starting the service like this this and it's not a bound service
serviceIntent = new Intent(this, RecordingService.class);
startService(serviceIntent);
If little use case description helps I am trying to control sound recorder from a remote view in the notification bar so restarting a service is not an option since it should continue to record even if activity is destroyed.
BTW.I did tried starting a service in another process by android:process=":recordingProcess" and the service does continue to run then but I am suspecting this is not how you should do it.
Even i had the same issue and i had access to onTaskRemoved() function.Please check this link, "Process life cycle" topic.
Try to return from onStartCommand() START_REDELIVER_INTENT, service will get start again.
From Android Developer Reference
A service can be both started and have connections bound to it. In
such a case, the system will keep the service running as long as
either it is started or there are one or more connections to it with
the Context.BIND_AUTO_CREATE flag. Once neither of these situations
hold, the service's onDestroy() method is called and the service is
effectively terminated.
How are you starting your service?
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;"