Android service killed immediately after start, despite calling startForeground() - android

I'm having a problem with my IntentService. Every time I start my service, the onDestroy() method is called as soon as the service becomes idle. I set up my service to run in the foreground, and despite this the service is still being killed right away. I have only one other activity in my application, and it is not calling stopService().
Reading the developer docs gives me the impression that calling startForeground() will allow your service to persist, even when idle, except when there is an very high demand for memory, or am I reading this wrong?
My code below:
public class FileMonitorService extends IntentService {
public int mNotifyId = 273;
public FileMonitorService(){
super("FileMonitorService");
}
#Override
protected void onHandleIntent(Intent arg0) {
}
#Override
public void onDestroy() {
Toast.makeText(this, getText(R.string.toast_service_stop), Toast.LENGTH_SHORT).show();
stopForeground(true);
super.onDestroy();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Notification notification = new Notification(R.drawable.icon, getText(R.string.notification_short), System.currentTimeMillis());
notification.flags|=Notification.FLAG_NO_CLEAR;
Intent notificationIntent = new Intent(this, FileMonitorActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, getText(R.string.notification_short),getText(R.string.notification_long), pendingIntent);
startForeground(mNotifyId, notification);
Toast.makeText(this, getText(R.string.toast_service_start), Toast.LENGTH_SHORT).show();
return super.onStartCommand(intent, flags, startId);
}
}

You need to look into using a regular Service instead of an IntentService. IntentService is designed to keep running while it has work to do. Once you've finished your onStartCommand method, it tries to stop.
See the docs:
Clients send requests through startService(Intent) calls; the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work.
(Emphasis mine)

Related

Service is killed after a short period of time (1 minute)

I've created a service that its job is to clear the notifications when the app is closed by the user. Everything works perfectly well but sometimes when the application is in the background for more than 1 minute the service is killed (which means that the notifications are not cancelled).
Why is this happening? I thought that the only way that you can stop a service is by using either stopSelf() or stopService().
public class OnClearFromRecentService extends Service {
private static final String TAG = "onClearFromRecentServic";
private NotificationManagerCompat mNotificationManagerCompat;
#Override
public void onCreate() {
super.onCreate();
mNotificationManagerCompat = NotificationManagerCompat.from(getApplicationContext());
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "Service Started");
return START_NOT_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "Service Destroyed");
}
#Override
public void onTaskRemoved(Intent rootIntent) {
//Put code here which will be executed when app is closed from user.
Log.d(TAG, "onTaskRemoved was executed ");
if (mNotificationManagerCompat != null) {
mNotificationManagerCompat.cancelAll();
} else {
Log.d(TAG, "onTaskRemoved: mNotifManager is null!");
}
stopSelf();
}
}
I start the service from the splash screen Activity like this: startService(new Intent(this, OnClearFromRecentService.class));
Also here are some Log messages:
Try returning START_STICKY form onStartCommand.
Then system will try to recreate.
check this official doc.
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "Service Started");
return START_NOT_STICKY;
}
Also you can try returing START_REDELIVER_INTENT,if you also want Intent to be re-delivered.
START_REDELIVER_INTENT
Constant to return from onStartCommand(Intent, int, int): if this
service's process is killed while it is started (after returning from
onStartCommand(Intent, int, int)), then it will be scheduled for a
restart and the last delivered Intent re-delivered to it again via
onStartCommand(Intent, int, int).
From docs.
I found a solution with the help of #emandt.
I just added these lines of code in onStartCommand() :
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "Service Started");
Notification notification = new NotificationCompat.Builder(this, CHANNELID)
.setContentTitle("title")
.setContentText("text")
.setSmallIcon(R.drawable.baseline_pause_white_24)
.build();
startForeground(2001,notification);
return START_NOT_STICKY;
}
According to docs the startForeground method :
If your service is started then also make this service run in the foreground, supplying the ongoing notification to be shown to the user while in this state...By default started services are background, meaning that their process won't be given foreground CPU scheduling (unless something else in that process is foreground)
Also,
If your app targets API level 26 or higher, the system imposes restrictions on using or creating background services unless the app itself is in the foreground. If an app needs to create a foreground service, the app should call startForegroundService(). That method creates a background service, but the method signals to the system that the service will promote itself to the foreground. Once the service has been created, the service must call its startForeground() method within five seconds.

ContentObserver registered in service is killed when app killed

I need to be able to detect when changes occur in contact data (any type of change to any contact in the address book).
I am using this ContentObserver:
public class ContactsContentObserver extends ContentObserver {
private Context context;
private Handler toastMessageHandler;
public ContactsContentObserver(Handler handler, Context ctx) {
super(handler);
context = ctx;
}
#Override
public void onChange(boolean selfChange) {
this.onChange(selfChange,null);
}
#Override
public void onChange(boolean selfChange, Uri uri) {
// Change Detected! Do something.
}
}
I register this observer in a service like this:
#Override
public int onStartCommand (Intent intent, int flags, int startId){
super.onStartCommand(intent, flags, startId);
registerContactsContentObserver();
return START_STICKY;
}
private void registerContactsContentObserver(){
ContactsContentObserver myObserver = new ContactsContentObserver(new Handler(), this);
getContentResolver().registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true, myObserver);
}
I start this service in my main activity.
It works fine as long as the activity is open (even if in the background), and the onChange() method is called every time I change any contact data.
But it doesn't work at all if I manually close the app by clearing it from recent apps in android
Am I doing something wrong? Isn't the service supposed to run even when the app is closed?
I've been able to solve the issue with the help of pskink
Starting the service in the forground like this:
private void runAsForeground(){
Intent notificationIntent = new Intent(this, LauncherActivity.class);
PendingIntent pendingIntent=PendingIntent.getActivity(this, 0,
notificationIntent, PendingIntent.FLAG_ONE_SHOT);
Notification notification=new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentText("test")
.setContentIntent(pendingIntent).build();
startForeground(1, notification);
}
#Override
public int onStartCommand (Intent intent, int flags, int startId){
super.onStartCommand(intent, flags, startId);
runAsForeground();
registerContactsContentObserver();
return START_STICKY;
}
solved the issue.
Although, I still don't understand why the issue occurred in the first place, since, the service is sticky and supposed to run even if the app is closed. Even if android stopped the service to save memory, it is supposed to restart it.
Can somebody explain to me why starting the service in the foreground is any different than a regular sticky service in the background?

Want to make a persistent service like Whatsapp/Truecaller

Description of Truecaller/Whatsapp service
**Note: Here force close means killing the app by pressing and holding back button not just stopping the service from service manager/app manager (see Kill app back button).
After killing the truecaller app when I make a call it restarts automatically, same for whatsapp also, After killing it when a message is received it still shows the notification and restarts the service. These services also restarts after a few delay
What I have done so far to achieve this
I want to make a service like this so called my backgroundservice from onDestroy() of my backgroundservice class. Code for this: ServiceDemo 0.1
public class BackgroundService extends Service {
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(BackgroundService.this, "Service Started...", Toast.LENGTH_SHORT).show();
return START_STICKY;
}
#Override
public void onDestroy() {
Intent intentService = new Intent(this,BackgroundService.class);
startService(intentService);
Toast.makeText(BackgroundService.this, "Service Will Be Restarted...", Toast.LENGTH_SHORT).show();
}
}
The service is restarting if I stop it from my service manager. But if I force close/kill it , it's gone.
After this I implemented Broadcast receiver which doesn't make any difference. Code for this: ServiceDemo 0.2
BackgroundService.java
public class BackgroundService extends Service {
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(BackgroundService.this, "Service Started...", Toast.LENGTH_SHORT).show();
return START_STICKY;
}
#Override
public void onDestroy() {
Toast.makeText(BackgroundService.this, "Service Will Be Restarted...", Toast.LENGTH_SHORT).show();
sendBroadcast(new Intent("RestartMe"));
}
}
RestartBackgroundService.java
public class RestartBackgroundService extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// I have also used AlarmManager , but it doesn't make any difference for me
// AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
// PendingIntent pi = PendingIntent.getService(context, 0, new Intent(context, BackgroundService.class), PendingIntent.FLAG_UPDATE_CURRENT);
// int interval = 5000;
// am.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + interval, interval, pi);
context.startService(new Intent(context.getApplicationContext(),BackgroundService.class));
}
}
As whatsapp is using gcm so I thought if I implement that it would help.
Then I implemented firebase cloud messaging (fcm) to receive push notification and I removed the code to restart background service from onDestroy().
Now if I stops the service from service manager it remains stopped then I sends a notification from my firebase console it receives the notification as google's gcm service is running. If I click the notification it restarts my service again.
But if I force close/kill my app no notifications are received though gcm service is running. Code for this: (I posted a link to make the description a bit short)
ServiceDemo 0.3
What I want
I want my service to be persistent like whatsapp/truecaller even if I force close it. They keep on starting after a few delays. And I want to achieve it without using third parties like fcm.
If someone can give any hints/solution about how to start my service when a particular system service/app (like dialer) starts that would be a great help.

Restart Android Started Service after application was killed

I'm trying to do a StartedService in android to send periodically the user location to a server.
Until this moment I managed to create the service and starting it from the 'parent ' application and I don't know how to keep it alive after the application was killed. From what I found on internet the 'StartCommandResult.Sticky' should restart the service if this one is killed but from some reason this is not restarted.
I overried the OnStartCommand:
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
locationTask = new Task(() =>
{
//stuff to do
});
locationTask.Start();
return StartCommandResult.Sticky;
}
And the service is started like this:
Intent intent = new Intent(this.Activity, Class.FromType(typeof(LocationService)));
this.Activity.StartService(intent);
Any suggestions what should I do in order to keep my service alive after the application was killed?
As observation I'm using xamarin to do it, but I won't mind an answer in android(java).
As stated in the official documentation:
A service is "started" when an application component (such as an
activity) starts it by calling startService(). Once started, a service
can run in the background indefinitely, even if the component that
started it is destroyed. Usually, a started service performs a
single operation and does not return a result to the caller. For
example, it might download or upload a file over the network. When the
operation is done, the service should stop itself.
So, starting the service like this
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return Service.START_STICKY;
}
Will have your service running even while your app is destroyed.
Regarding Service.START_STICKY in the official documentation:
If the system kills the service after onStartCommand() returns,
recreate the service and call onStartCommand(), but do not redeliver
the last intent. Instead, the system calls onStartCommand() with a
null intent, unless there were pending intents to start the service,
in which case, those intents are delivered. This is suitable for media
players (or similar services) that are not executing commands, but
running indefinitely and waiting for a job.
This is how I do it but its in JAVA code.
in your service you should implement a LocalBinder, onStartCommand and onCreate methods.
public class LocalBinder extends Binder {
ServiceName getService() {
return ServiceName .this;
}
}
#Override
public void onCreate() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("LocalService", "Received start id " + startId + ": " + intent);
return START_STICKY;
}
The onStartCommand should return START_STICKY;. And also, include this code on your Service:
#Override
public void onTaskRemoved(Intent rootIntent) {
// TODO Auto-generated method stub
Intent restartService = new Intent(getApplicationContext(),this.getClass());
restartService.setPackage(getPackageName());
PendingIntent restartServicePI = PendingIntent.getService(getApplicationContext(), 1, restartService, PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmService = (AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
alarmService.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() +1000, restartServicePI);
}
This will restart your Service on 1 second from the time you close it. Also, dont forget to add your service in your AndroidManifest.xml
<service android:name=".ServiceName"
android:exported="false"
android:stopWithTask="false" >
</service>
Override onStartCommand like this
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return Service.START_STICKY;
}
For me, everything in the code was right but the problem was with the use of debugging while checking if the service was on. When I use a release version (or just dev version without connecting to the debug), the process is not killed and the service is running normally.
No idea why though. I am using Xamarin Studio 6.0.

Stopping IntentService by clicking Notification

I'm doing some background work in an IntentService and trying to make it stop by clicking a notification. For stopping the work I have a static method, that sets a flag.
public static void stopService() {
if (task != null) {
task.setCancelFlag(true);
}
}
The notification has a PendingIntent, that sends a Broadcast to a Receiver, that attempts to stop the service.
Intent intent = new Intent(this, AlarmReceiver.class);
intent.setAction(AlarmReceiver.STOP_SERVICE);
PendingIntent contentIntent = PendingIntent.getBroadcast(getBaseContext(), 0,
intent, 0);
notification.contentIntent = contentIntent;
The Receiver calls the stopService() method when it receives a broadcast.
if (intent.getAction().equals(STOP_SERVICE)) {
UpdateCheckService.stopService();
}
Strangely enough, the stopService() method is not called properly. If I try to log it, the part with the flag setting is not executed. Even if I set a breakpoint on the Receiver and try to debug it, it doesn't work.
However, if I call the same method from an Activity by clicking a button, everything works as intended.
Does somebody know, where this strange behavior comes from?
I did it using the intent of IntentService to create the PendingIntent
In onHandleIntent I invoke the notification :
#Override
protected void onHandleIntent(Intent intent) {
PendingIntent pStopSelf = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
Then I added the stop button and call my notification (insert this line on NotificationCompat.Builder):
.addAction(R.drawable.ic_close_white_24dp, "Stop", pStopSelf)
Clicking the stop on the notification will not trigger onHandleIntent, but invoke onStartCommand. Here you can check if the intent contains the flag we set to stop the service.
private boolean shouldStop = false;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent.getFlags() == PendingIntent.FLAG_CANCEL_CURRENT) {
Log.d(TAG, "Stop pressed");
stopSelf();
shouldStop = true;
return Service.START_NOT_STICKY;
}
return super.onStartCommand(intent, flags, startId);
}
stopSelf() stops any intents containing work requests to do more work in the intent service to restart the service. But it does not stop the service itself.
To stop the service from continuing to execute, use the boolean previously set to check if the work should continue like this in onHandleIntent()
#Override
protected void onHandleIntent(Intent intent) {
//Invoke notification
doStuff();
}
private void doStuff() {
// do something
// check the condition
if (shouldContinue == false) {
return;
}
}
Use the boolean to check in between the code to check if service should stop and return from the method.
You should not use the IntentService class for your case. IntentService use a queue to process one Intent at a time. Just create your own class that extend Service as the example here.
Then handle the stop request as you did to stop the worker thread.
The mystery is solved: My BroadcastReceiver had the remote process flag set, which I copied from some tutorial on the web without too much thinking. Removing this tag made everything work as expected.

Categories

Resources