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

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

Related

Intent is Null all the time in service

I have two Application A and B.In app B I have a service that I can run it from app A. I want to send data to app B with intent but always my intent is null!
I run app B's service from app A with this Code:
try {
String packageName = "app_B_package";
String appService = packageName + ".activity.InternetService";
Intent start = new Intent();
start.setComponent(new ComponentName(packageName, appService));
start.putExtra("LAUNCHER_COMMAND_CLOSE" , true);
G.context.startService(start);
} catch (Exception e) {
e.printStackTrace();
}
But when service of app B will run the intent is null. This is onStart of the service in app B:
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
Log.i("LOGO_OFFICE_IN", "onStart");
if (intent != null) {
if (intent.getExtras().getBoolean("LAUNCHER_COMMAND_CLOSE")) {
Tools.clearApplicationData(InternetService.this);
new AppStatus(InternetService.this).isAppRunning(getPackageName(), true);
}
}
}
Why my intent is null all the time? I can't find it out.
Thank you for your help.
It looks like your service is type fire-and-forget - it does one quick thing and should quit immediately because it's done. Correct?
1. Don't leave your idle service running
Documentation says
If a component starts the service by calling startService() (which results in a call to onStartCommand()), the service continues to run until it stops itself with stopSelf() or another component stops it by calling stopService().
so after your workload is done call stopSelf().
When your service is not running there's nothing to restart.
2. Use correct start mode
Unless you stop it, your service is by default automatically restarted after it's killed by system (because system needed resources). The default mode is called START_STICKY and does this:
This mode makes sense for things that will be explicitly started and stopped to run for arbitrary periods of time, such as a service performing background music playback.
Since your service is a quick one-time job, it makes no sense for it do be restarted later at an arbitrary time.
To let Android know, you should return START_NOT_STICKY from onStartCommand.
3. Use current API
Don't use onStart, it was deprecated 9 years ago. It doesn't support start modes mentioned above. Implement onStartCommand instead. Your service would look like this:
#Override
public void onStartCommand(Intent intent, int flags, int startId) {
// No super call.
Log.i("LOGO_OFFICE_IN", "onStart");
// Intent cannot be null.
if (intent.getExtras().getBoolean("LAUNCHER_COMMAND_CLOSE")) {
Tools.clearApplicationData(InternetService.this);
new AppStatus(InternetService.this).isAppRunning(getPackageName(), true);
}
stopSelf(); // Work is done, stop service.
return START_NOT_STICKY; // Don't restart if killed.
}
Now that I think of it, only step 1 is absolutely necessary. Anyway, get into habit of using current APIs and finding out how things work.

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.

Android service stopSelf(int)

Here is my code -
public class BackgroundService extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
new ServiceThread(startId);
return START_NOT_STICKY;
}
class ServiceThread extends Thread {
private int startId;
ServiceThread(int startId) {
this.startId = startId;
}
#Override
public void run() {
super.run();
try {
Thread.sleep((long) Math.random());
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
stopSelf(startId);
}
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
According to this link "Stopping a service", I can/should call stopSelf with received "startId".
However, if your service handles multiple requests to onStartCommand()
concurrently, then you shouldn't stop the service when you're done
processing a start request, because you might have since received a
new start request (stopping at the end of the first request would
terminate the second one). To avoid this problem, you can use
stopSelf(int) to ensure that your request to stop the service is
always based on the most recent start request. That is, when you call
stopSelf(int), you pass the ID of the start request (the startId
delivered to onStartCommand()) to which your stop request corresponds.
Then if the service received a new start request before you were able
to call stopSelf(int), then the ID will not match and the service will
not stop.
My question is, what will happen, if I invoke stopSelf with last "startId", and still some earlier start is still not finished. In that case, the startId will match, and according to that document the service will die? All all other earlier "starts" will just be killed?
If the answer is "yes", then what is the best practice to achieve that the service will not be killed until all earlier start is not finished.
I just run into exactly the same problem yesterday. And I think I found answer in the javadoc of Service.stopSelfResult(int startId) method.
If you may end up processing IDs out of order (such as by dispatching them on separate threads), then you are responsible for stopping them in the same order you received them.
A solution is to maintain a boolean hash map with the start ID as the key. Then in your worker threads, instead of calling stopSelf(int) call a custom method which does the following:
Set the hash map entry to true using the start ID as the key. Iterate through the keys in ascending order and call stopSelf(key) until you encounter a false entry.
This is a subtle problem - I'm not sure any of the solutions above are adequate/efficient. The solution I use is based on a class called ThreadPoolService, which extends Service and operates as follows:
It defines HashSet<Integer> that stores the startId passed by onStartCommand()
It defines an int field called mLastStartId that stores the most recent startId passed by onStartCommand()
It defines an ExecutorService initialized by either newCachedThreadPool() or newFixedThreadPool()
It defines a beginIntentProcessing() method that adds the startId parameter to the HashSet and records the latest startId in mLastStartId
It defines an endIntentProcessing() method that removes the startId parameter from the HashSet and returns if the HashSet is non-empty. If the HashSet is empty, however, it calls the stopSelf() method on the Service superclass, passing in the mLastStartId.
I've omitted some details of the solution, but it's efficient and solves the underlying problem described above. I'll be covering this topic (and many others) in my upcoming MOOCs on Android concurrent programming, which are described at http://www.coursera.org/course/posaconcurrency and http://www.coursera.org/course/posacommunication.
Doug

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.

Registering a ContentObserver in a Android Service

I am working on parental control/adult content filtering application. This app continuously monitors the calls and smses on a child's mobile and logs all the activity onto a server. For this I am starting a service (MyService.java)
on BOOT_COMPLETED and in the onCreate method of the service I register a contentobserver for the callLog and sms uri ( refer to the code snippet below ) .
Now the issue is, Since I want to monitor every outgoing, incoming call s and sms I want the service to be continuously running ( without being stopped/killed) . Moreover this Service is being just used for registering content observers and not doing any other processing(its OnstartCommand method is dummy ) , so android OS kills the service after sometime. How do I ensure that the service runs continuously and keeps the contentobserver object alive ?
public class MyService extends Service {
private CallLogObserver clLogObs = null;
public void onCreate() {
super.onCreate();
try{
clLogObs = new CallLogObserver(this);
this.getContentResolver().registerContentObserver(android.provider.CallLog.Calls.CONTENT_URI, true, clLogObs);
}catch(Exception ex)
{
Log.e("CallLogData", ex.toString());
}
}
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onDestroy() {
if( clLogObs !=null )
{
this.getContentResolver().unregisterContentObserver(clLogObs);
}
super.onDestroy();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return Service.START_STICKY;
}
#Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
You cannot ensure your service to be running continuously on Android.
For the use-case you mention, it is better to rely on Broadcast receiver like ACTION_NEW_OUTGOING_CALL & SMS_RECEIVED.
If you feel, above supported Broadcast receivers doesn't cover all your use-cases. Use AlaramManager to periodically start your SERVICE and look into CALL_LOGS and SMS table for any change in data and take appropriate action (this may involve check marking the last visited data on CALL_LOGS and SMS table).
you can set the service to run in the foreground . this will give your service a much lower chance of being killed by the OS .
read here for more information .
The content observer get unregistered as soon as your service get killed. So registering once is not an option and your service must be running all the time. to guarantee this you must return START_STICKY from onStartCommand. You can also start the service in foreground.
But what if a new call arrive when the service is killed? To handle this you can store last processed _id in preferences of your app. and each time get all the new call logs with greater id than saved id. You must process the logs on service onCreate as well as on observer onChange.
You cant guarantee it will run continuously, but you can return START_STICKY from onStartCommand which guarantees that Android will re-start your service if it is killed for any reason.

Categories

Resources