Stopping IntentService by clicking Notification - android

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.

Related

onHandleWork() not called in a JobIntentService

I need to handle intents posted via AlarmManager both while my app runs and at boot time. I've written a subclass of JobIntentService to handle the intents but it's not working as expected: onHandleWork is not called.
My implementation worked when the handler was an IntentService, except at boot time due to restrictions on background services. I'm therefore attempting to use a JobIntentService instead.
public class MyJobIntentService extends JobIntentService
{
public int onStartCommand(#Nullable Intent intent, int flags, int startId)
{
// This gets called with the intent given to AlarmManager
return super.onStartCommand(intent, flags, startId);
}
public static void enqueueWork(Context context, Intent work)
{
// Not called
enqueueWork(context, NotificationService.class, JOB_ID, work);
}
public void onHandleWork(#NonNull Intent intent)
{
// Not called
...
}
// No other methods overridden
}
onStartCommand is called. The documentation says that this method:
Processes start commands when running as a pre-O service, enqueueing them to be later dispatched in onHandleWork(Intent).
I don't know what 'later' is supposed to mean here but onHandleWork is not in fact called, even though onStartCommand has been called with the expected intent.
I've read answers to similar questions and ensured that I don't override other methods. I've also created a manifest entry for the service which I assume is correct - or onStartCommand wouldn't be called.
This is how I create the PendingIntent used with AlarmManager:
Intent myIntent = new Intent( myContext, MyJobIntentService.class);
...
PendingIntent pendingIntent = PendingIntent.getService( mContext, 0, myIntent, PendingIntent.FLAG_UPDATE_CURRENT);
myAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, futureInMillis, pendingIntent);
Any ideas on what I'm missing?
your can try this recommendation. Also, you may want to check your AndroidManifest.xml to android.permission.BIND_JOB_SERVICE permission.

onStartCommand() called only once even when Service is started multiple times

The below service is triggered via button click from some other app (by firing pending Intent). The onStartCommand() creates a Messages and dispatches using send() method. Ideally, I expect onStartCommand to be called everytime button is clicked, as a pending intent is used to fire the service on buttonClick.
But onstartCommand() is called only once, for the first time the button is clicked. Subsequent button clicks do not trigger the onStartCommand().
Interestingly if I comment the line
replyTo.send(msg);
onStartCommand gets called each time the button from other app is clicked.
Therefore dispatching the Message using android IPC Messenger from within the service might be causing the issue. I confirmed the Message reaches the destination app successfully. Am I missing some detail about Messages , like blocking send call?
I am returning 'START_STICKY' from onStartCommand(), that also might be the reason.
Any insights on what is happening will be welcome.
//MyService.java
#Override
public void onCreate() {
// create RemoteViews -> rView
Intent intent = new Intent(getBaseContext(), MyService.class);
PendingIntent pendingIntent = PendingIntent.getService(getBaseContext(), 0, intent, 0);
rView.setOnClickPendingIntent(buttonId, pendingIntent);
//On click of the above button, this MyService will be started usingthe given pendingintent
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e("debug","Service onStartCommand");
Message msg = Message.obtain(null, UPDATE_REMOTE_VIEW, rView);
try {
replyTo.send(msg);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return START_STICKY;
}
Bonus Detail: The pendingIntent on the Button (from other app) is set using setOnclickPendingIntent() (RemoteViews class).
What I did in my similar case is to implement onStartCommand as follow:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//
// ... HERE support for intent sent by setOnClickPendingIntent ...
//
return super.onStartCommand(intent, flags, startId);
}
And it seems to work. onStartCommand is called multiple times (as many as the number of click on my RemoteViews).
From Docs:
Clients can also use Context.bindService() to obtain a persistent
connection to a service. This likewise creates the service if it is
not already running (calling onCreate() while doing so), but does not
call onStartCommand(). The client will receive the IBinder object that
the service returns from its onBind(Intent) method, allowing the
client to then make calls back to the service. The service will remain
running as long as the connection is established (whether or not the
client retains a reference on the service's IBinder). Usually the
IBinder returned is for a complex interface that has been written in
aidl.
So, it may be because of the use of bindService

Android service onStartCommand never called

I noticed that Service.START_STICKY doesn't work and when I tokk a closer look, I saw the onCreate() is running but onStartCommand is not called.
Any ideas why?
#Override
public void onCreate() {
mGlobalData = GlobalData.getInstance();
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
if (mTimer == null)
mTimer = new Timer();
Log.e(TAG, "onCreate()");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
int t = START_STICKY;
Log.e(TAG, "call me redundant BABY! onStartCommand service");
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return t;
}
If you have the same situation I had, my Service starts up and runs just fine (onCreate() and onServiceConnected() are both invoked) but onStartCommand(Intent,int) was never called. I found it's because the system started my Service instead of me explicitly starting the Service in code. According to the docs:
[onStartCommand(Intent,int) is] called by the system every time a client explicitly starts the service by calling startService(Intent)
So I had to call startService(new Intent(context, MyService.class)) explicitly in code to get onStartCommand(Intent,int) to trigger. Note that doing this will not restart the Service created by the system and it won't create a new instance of that Service either.
Try to insert the line android.os.Debug.waitForDebugger(); at the end of onCreate(). The debugger didn't reach onStartCommand()'s breakpoints for me until I did this.

How can we prevent a Service from being killed by OS?

I am using Service in my application and it needs to run until my application is uninstalled, but the problem is it gets killed by OS.
How can we prevent it from being killed by OS? Or if it gets killed can we restart that service again through programmatically?
You may run the service in the foreground using startForeground().
A foreground service is a service that's considered to be something
the user is actively aware of and thus not a candidate for the system
to kill when low on memory.
But bear in mind that a foreground service must provide a notification for the status bar (read here), and that the notification cannot be dismissed unless the service is either stopped or removed from the foreground.
Note: This still does not absolutely guarantee that the service won't be killed under extremely low memory conditions. It only makes it less likely to be killed.
I've been puzzled by the same issue to yours recently.but now,I've found a good solution.
First of all,you should know that, even your service was killed by OS, the onCreate method of your service would be invoked by OS in a short while.So you can do someting with the onCreate method like this:
#Override
public void onCreate() {
Log.d(LOGTAG, "NotificationService.onCreate()...");
//start this service from another class
ServiceManager.startService();
}
#Override
public void onStart(Intent intent, int startId) {
Log.d(LOGTAG, "onStart()...");
//some code of your service starting,such as establish a connection,create a TimerTask or something else
}
the content of "ServiceManager.startService()" is:
public static void startService() {
Log.i(LOGTAG, "ServiceManager.startSerivce()...");
Intent intent = new Intent(NotificationService.class.getName());
context.startService(intent);
}
However, this solution is just available for the situation of your service being killed by GC.Sometimes our service might be killed by user with Programme Manager.In this situation,your prosses will be killed,and your service will never been re-instantiated.So your service can not be restarted.
But the good news is,when the PM kill your service,it will call your onDestroy method.So we can do something with that method.
#Override
public void onDestroy() {
Intent in = new Intent();
in.setAction("YouWillNeverKillMe");
sendBroadcast(in);
Log.d(LOGTAG, "onDestroy()...");
}
The string of "YouWillNeverKillMe" is a custom action.
The most important thing of this method is,don't add any code before send the broadcast.As system will not wait for completion of onDestroy(),you must send out the broadcast as soon as posible.
Then regist a receiver in manifast.xml:
<receiver android:name=".app.ServiceDestroyReceiver" >
<intent-filter>
<action android:name="YouWillNeverKillMe" >
</action>
</intent-filter>
</receiver>
Finally,create a BroadcastReceiver,and start your service in the onReceive method:
#Override
public void onReceive(Context context, Intent intent) {
Log.d(LOGTAG, "ServeiceDestroy onReceive...");
Log.d(LOGTAG, "action:" + intent.getAction());
Log.d(LOGTAG, "ServeiceDestroy auto start service...");
ServiceManager.startService();
}
Hope this will be helpful to you,and excuse my poor written english.
Override method onStartCommand() in your service class and simply return START_STICKY (as suggested by "Its not blank"). That's all you need. If the process that runs your service gets killed (by a low memory condition for example), the Android system will restart it automatically (usually with some delay, like 5 seconds).
Don't use onStart() anymore as suggested in another answer, it's deprecated.
use
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//**Your code **
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
ref Documentation lifecycle of Service.
Edit added method.
As far i know, onDestroy() will be called only when the service is explicitly stopped(Force Stop). But this method won't get called in case the service gets killed by OS/swiping the Recent Apps list. In those cases another event handler named onTaskRemoved(Intent) gets called. This is due to a defect in Android 4.3-4.4 as per the link here. Try using the below code:-
public void onTaskRemoved(Intent intent){
super.onTaskRemoved(intent);
Intent intent=new Intent(this,this.getClass());
startService(intent);
}
I found another solution of the problem which gurantees that your service will be always alive. In my case, this scheme resloves also the problem with FileObserver, which stops work after some period of time.
Use an activity (StartServicesActivity) to start the service (FileObserverService) as Foreground service.
Use BroadcastReceiver class (in example CommonReceiver) to restart your service in some special situations and in case it was killed.
I used this code in my app "Email Pictures Automatically"
https://play.google.com/store/apps/details?id=com.alexpap.EmailPicturesFree
Here is CommonReceiver class.
public class CommonReceiver extends BroadcastReceiver {
public void onReceive(Context paramContext, Intent paramIntent)
{
paramContext.startService(new Intent(paramContext, FileObserverService.class));
}
}
Here is its definition in AndroidManifest.xml just before application closing tag.
<receiver android:name="com.alexpap.services.CommonReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.USER_PRESENT"/>
</intent-filter>
</receiver>
Start service in StartServicesActivity activity.
Intent iFileObserver = new Intent(StartServicesActivity.this, FileObserverService.class);
StartServicesActivity.this.startService(iFileObserver);
Here is onStartCommand() method of the service.
public int onStartCommand(Intent intent, int flags, int startId) {
int res = super.onStartCommand(intent, flags, startId);
/*** Put your code here ***/
startServiceForeground(intent, flags, startId);
return Service.START_STICKY;
}
public int startServiceForeground(Intent intent, int flags, int startId) {
Intent notificationIntent = new Intent(this, StartServicesActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("File Observer Service")
.setContentIntent(pendingIntent)
.setOngoing(true)
.build();
startForeground(300, notification);
return START_STICKY;
}
I tested this code using Task Killer app, and each time the service was killed, it was restarted again almost immediately (performs onStartCommand()). It is restarted also each time you turn on the phone and after rebooting.
I use this code in my application, which emails every picture you take with your phone to predefinde list of emails. The sending email and list of receiving emails are set in another activity and are stored in Shared Preferences. I took about 100 pictures in several hours and all they were sent properly to receiving emails.
#Override
public void onDestroy() {
super.onDestroy();
startService(new Intent(this, YourService.class));
}
write above code in your service and your service will never stop even user want to destroy it or they want to kill it it will never kill untill your app not get uninstall from your device
You can try to start your service repeatedly, for example every 5 sec.
This way, when your service is running, it will perform onStartCommand() every 5 sec. I tested this scheme and it is very reliable, but unfortunately it increases slightly phone overhead.
Here is the code in your activity where you start the service.
Intent iFileObserver = new Intent(StartServicesActivity.this, FileObserverService.class);
PendingIntent pendingIntentFileObserver = PendingIntent.getService(StartServicesActivity.this, 0, iFileObserver, 0);
AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
Date now = new Date();
//start every 5 seconds
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, now.getTime(), 5*1000, pendingIntentFileObserver);
And here is onStartCommand() of the service.
//class variable
public static boolean isStarted = false;
public int onStartCommand(Intent intent, int flags, int startId) {
int res = super.onStartCommand(intent, flags, startId);
//check if your service is already started
if (isStarted){ //yes - do nothing
return Service.START_STICKY;
} else { //no
isStarted = true;
}
/**** the rest of your code ***/
return Service.START_STICKY;
}
First create service in another process, and write broadcaster which runs in recursion in time intervals
protected CountDownTimer rebootService = new CountDownTimer(9000, 9000) {
#Override
public void onTick(long millisUntilFinished) {
}
#Override
public void onFinish() {
sendBroadcast(reboot);
this.start();
Log.d(TAG, "rebootService sending PREVENT AUTOREBOT broadcast");
}
};
After that register broadcast receiver in main process also with timer recursion that is launched after first broadcast from service arrived
protected static class ServiceAutoRebooter extends BroadcastReceiver {
private static ServiceAutoRebooter instance = null;
private RebootTimer rebootTimer = null;
private static ServiceAutoRebooter getInstance() {
if (instance == null) {
instance = new ServiceAutoRebooter();
}
return instance;
}
public class RebootTimer extends CountDownTimer {
private Context _context;
private Intent _service;
public RebootTimer(long millisInFuture, long countDownInterval) {
super(millisInFuture, countDownInterval);
}
#Override
public void onTick(long millisUntilFinished) {
}
#Override
public void onFinish() {
_context.startService(_service);
this.cancel();
Log.d(TAG, "Service AutoRebooted");
}
}
#Override
public void onReceive(Context context, Intent intent) {
if (rebootTimer == null) {
Log.d(TAG, "rebootTimer == null");
rebootTimer = new RebootTimer(10000, 10000);
rebootTimer._context = context;
Intent service = new Intent(context, SomeService.class);
rebootTimer._service = service;
rebootTimer.start();
} else {
rebootTimer.cancel();
rebootTimer.start();
Log.d(TAG, "rebootTimer is restarted");
}
}
}
Service will be auto-rebooted if time at RebootTimer (main process) expires, which means that "PREVENT AUTOREBOT" broadcast from service hasn't arrived
i found a solution .... late answer but i wanted to answer...
we can send a broadcast in the ondestroy of the service and create a receiver that receives the broadcast and starts the service again.... when it is destroyed by any reasons...
pls try following:
final Messenger mMessenger = new Messenger(new IncomingHandler());
class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
default:
super.handleMessage(msg);
}
}
}
#Override
public void onCreate() {
super.onCreate();
makeServiceForeground();
}
#Override
public IBinder onBind(Intent arg0) {
return mMessenger.getBinder();
}
private void makeServiceForeground() {
IActivityManager am = ActivityManagerNative.getDefault();
try {
am.setProcessForeground(onBind(null), android.os.Process.myPid(), true);
} catch (RemoteException e) {
Log.e("", "cant set to foreground" + e.toString());
}
}
also need add in manifest.xml
<uses-permission android:name="android.permission.SET_PROCESS_LIMIT"/>

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

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)

Categories

Resources