I have a remote Service that I start with startService() then bind to with bindService(). I do this because I want the Service to run in the background until the app explicitly stops it, even if the user closes the Activity with a swipe. Here are the relevant parts of the Service:
public class TrackService extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("onStartCommand","got to onStartCommand");
return START_NOT_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
Log.i("onBind", "got to onBind");
return trackServiceBinder;
}
}
Here is the activity:
public class GPSLoggerActivity extends Activity implements ServiceConnection {
#Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(TrackService.class.getName());
intent.setClass(getApplicationContext(), TrackService.class);
startService(intent);
Log.i("onStart", "started TrackService");
if (!getApplicationContext().bindService(intent, this, 0)) {
// close the Activity
}
Log.i("onStart", "bound to TrackService");
}
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i("onServiceConnected", "got here");
// get and use the interface from the Binder
}
}
Here is the manifest for the Service:
<service
android:name=".TrackService"
android:process=":track_service_process"
android:stopWithTask="false"
android:label="#string/track_service_label"
android:exported="false"
android:enabled="true">
<intent-filter>
<action android:name="com.mydomain.gpslogger.TrackService" />
</intent-filter>
</service>
When I run this in the debugger step-wise through startService() and bindService() everything is fine. Within the Service, onStartCommand() is invoked followed by onBind(), and in the Activity onServiceConnected() is invoked with the Binder. However, when I'm not debugging, for some reason the Service gets its onBind() method invoked before its onStartCommand() method. Here's the output from the log:
11-28 23:17:01.805: I/onStart(1103): started TrackService
11-28 23:17:01.815: I/onStart(1103): bound to TrackService
11-28 23:17:01.985: I/onBind(1165): got to onBind
11-28 23:17:01.995: I/onStartCommand(1165): got to onStartCommand
The Activity's onServiceConnected() method is never invoked. I've tried using BIND_AUTO_CREATE in the bindService() call, to no effect. It's pretty clear I have some sort of race condition going on here, but I've run this probably 20 times and it always comes out in the same order. The only way I can think of to enforce the correct order of calls in the Service is to put a BroadcastReceiver in the Activity and send an Intent from the onStartCommand() method of the Service to let the Activity know that it's time to call bindService(). That seems pretty kludgy...is there a better way to do this? Or is there something that I'm missing that is causing the out-of-order calls?
You could call startService() and then wait some period of time before calling bindService(). How long to wait is certainly a question, but I would think if your Service doesn't do massive amounts of work when started, 500 or 1000 milliseconds delay should do it.
I would question why this is important. The Service has an onCreate() method, which is called when the Service is instantiated, regardless of whether onStartCommand() or onBind() is called first. You should be able to setup your architecture so that you do all important setup in onCreate() and then it shouldn't matter in what order onStartCommand() or onBind() is called. NOTE: You must use flag BIND_AUTO_CREATE in the call to bindService()
I also do not understand why the onServiceConnected() method is not called. Unless the call to bindService() returns false (which basically means that Android can't find the Service to bind to), you should get the onServiceConnected() callback. This sounds strange. One possibility is that the Service crashes in onCreate(), or it takes too long to execute the onCreate() method, in which case Android will declare the Service broken.
Related
If my app is running and I press home button, the app goes in background. Now if I long press the home button and kill the app by swiping it from the recent app list, none of the events like onPause(), onStop() or onDestroy() gets called rather the process is terminated.
So if i want my services to stop, kill notifications and unregister listeners, how can I do that?
I just resolved a similar kind of issue.
Here is what you can do if its just about stopping service when application is killed by swiping from Recent app list.
Inside your Manifest file, keep flag stopWithTask as true for Service. Like:
<service
android:name="com.myapp.MyService"
android:stopWithTask="true" />
But as you say you want to unregister listeners and stop notification etc, I would suggest this approach:
Inside your Manifest file, keep flag stopWithTask as false for Service. Like:
<service
android:name="com.myapp.MyService"
android:stopWithTask="false" />
Now in your MyService service, override method onTaskRemoved. (This will be fired only if stopWithTask is set to false).
public void onTaskRemoved(Intent rootIntent) {
//unregister listeners
//do any other cleanup if required
//stop service
stopSelf();
}
Refer my question for more details, which contains other part of code, too.
We need to create a service that would clear the application from recent service
public class ClearService extends Service {
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("ClearService", "Service Started");
return START_NOT_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d("ClearService", "Service Destroyed");
}
#Override
public void onTaskRemoved(Intent rootIntent) {
Log.e("ClearService", "END");
//Code here
stopSelf();
}
}
register this service in manifest.xml
<service android:name="com.package.ClearService" android:stopWithTask="false" />
Then start this service on your splash activity
startService(new Intent(getBaseContext(), ClearService.class));
And now whenever you will clear your app from android recent Then this method onTaskRemoved() will execute.
I resolved similar issue. If you want after swiping from recent task and on next launch it to behave properly then follow below steps:-
1) Save process ID in shared preference:
SharedPreferencesUtils.getInstance().putInt(SharedPreferencesUtils.APP_PROCESS_ID, android.os.Process.myPid());
2) When application is launched from launcher after clear from recent task then do:
int previousProcessID = mSharedPreferencesUtils.getInt(SharedPreferencesUtils.APP_PROCESS_ID);
int currentProcessID = android.os.Process.myPid();
if ((previousProcessID == currentProcessID)) {
// This ensures application not killed yet either by clearing recent or anyway
} else {
// This ensures application killed either by clearing recent or by anyother means
}
When you press home - onPause and onStop of your Activity is being called, so at this time you have to do all savings and cleanup, because Android platform doesn't further guarantee that onDestroy or any other lifecycle method would be invoked, so the process could be killed without any notification.
ViewModel.onCleared() can be useful, if the goal is to release some resource (perhaps a system running somewhere else on the network) when the user executes a surprise exit by swiping, rather than by pressing the "stop" or button. [This is how I originally arrived at this question].
Application doesn't get a notification, and Activity.onDestroy() gets called for configuration changes such as changes in orientation, so the answer isn't there. But ViewModel.onCleared gets called when the Application is swiped away (as well as when the user backs out of the activity). If the resource you want to use is associated with more than one activity in the stack, you can add reference counts or some other mechanism to decide if ViewModel.onClear should release the resource.
This is yet another of many good reasons to use ViewModel pattern
I don't really know why the above approaches are not working on my case even I set android:stopWithTask="false" that onTaskRemoved() not called.
Another good approach would be using AndroidViewModel. This one even works on the case when user exits the applcation on pressing back button.
Just bound ViewModel class to your MainActivity then do your task onCleared() callback.
Example:
public class MainViewModel extends AndroidViewModel {
public MainViewModel(#NonNull Application application) {
super(application);
}
#Override
protected void onCleared() {
// Do your task here
Log.e("MainViewModel", "OnCleared mainViewModel");
super.onCleared();
}
}
then bound it to your MainActivity:
MainViewModel viewModel = new ViewModelProvider(this).get(MainViewModel.class);
~ Voila!
As Bob Cram mentioned in his answer, View Model's onCleared() method is the answer.
It works in both cases :
When the user removes the app by swiping the app from background.
When the user clear all the app using the clear list button.
Service's onTaskRemoved() will work when the user swipes the app from the background, but will not work when the apps are cleared using the kill all button.
But the viewModel's onCleared() method works in both cases. You can use if to stop any ongoing process or clearing any task in the remote server.
override fun onCleared() {
super.onCleared()
Log.d(TAG , "App Killed")
}
You need to save your data when on onPause() is called.
Look at this life cycle diagram:
Android Developer
You can see that an app can be killed after onPause() or onStop().
Handle your data there and recover it in onRestart() \ onCreate().
good luck!
This worked for me on android 6,7,8,9.
Make one service like this:
public class OnClearFromRecentService extends Service {
#Override public IBinder onBind(Intent intent) {
return null; }
#Override public int onStartCommand(Intent intent, int flags, int
startId) {
Log.d("ClearFromRecentService", "Service Started");
return START_NOT_STICKY; }
#Override public void onDestroy() {
super.onDestroy();
Log.d("ClearFromRecentService", "Service Destroyed"); }
#Override public void onTaskRemoved(Intent rootIntent) {
Log.e("ClearFromRecentService", "END");
//Code here
stopSelf(); } }
2) Register this service in manifest.xml:
<service android:name="com.example.OnClearFromRecentService"
android:stopWithTask="false" />
3) Then start this service on your splash activity
startService(new Intent(getBaseContext(),
OnClearFromRecentService.class));
I have been looking for some way to get the ServiceConnection when I start my Service using startService(...).
I haven't found a way, so I have been searching a bit and found this:
Does each Activity need to Bind to a Service & What happens when it was created with startService()
There, Commonsware says that it doesn't matter if I call bindService after the startService call.
So I thought that I first run startService(...) and then directly after do a bindService(...) (so that onServiceConnected is called). But then the Service.onCreate is executed twice. Probably because startService isn't "finished" yet...?
Question is: How do I get a reference to my Service (the IBinder), ie. how do I get the onServiceConnected to fire if I start my Service with startService?
--- EDIT ---
I still do want to know any answers and ideas you might have. I made a "hack" to get around this:
I simply made a static reference (in SRef.java I have public static IBinder myBinder = null), and in my Service.onCreate I simple do
SRef.myBinder = myBinder;
This doesn't seem right to me, so any other ideas on how it is supposed to work would be appreciated.
I use the exact same technique (a samba client service), onCreate is never called twice for me and I get the binder (by connection callback) as I would expect. A new start of activity doesn't fire a onCreate either because previous startService performed the startup of the service already.
Here is my code (might be trivial, but maybe it helps):
Activity (onCreate):
startService(new Intent(this, SambaService.class));
bindService(new Intent(this, SambaService.class), sambaServiceConnection,
Context.BIND_AUTO_CREATE);
Service:
private ServiceBinder mServiceBinder = new ServiceBinder();
public class ServiceBinder extends Binder {
public SambaService getService() {
return SambaService.this;
}
}
public IBinder onBind(Intent intent) {
return mServiceBinder;
}
I just want to know could I bind a service from another service. For example, currently I have an activity A starting a service B and now I just want service B to bind and start another service C. So does anybody know how to do that? That means could I use the same method for activity A to start a service on a service to start another service?
You can call bindService from a Service exactly the same way you can call it from an Activity. You'll notice from the javadoc that the only place you can't call bindService is in a BroadcastReceiver. You can use a ServiceConnection as well to receive the Binder.
This works for me. If I call bindService from onCreate then onServiceConnected is in a race with the first call to onHandleIntent, so re-submit the intent if it arrives too soon. My code is roughly like this.
class MyService extends IntentService implements ServiceConnection {
IMyOtherService iService;
#Override
void onCreate() {
bindService(intent);
}
#Override
void onServiceConnected(ComponentName name, IBinder service) {
iService = IMyService.Stub.asInterface(service);
}
#Override
void onHandleIntent(Intent intent) {
if (iService == null) {
/* onHandleIntent has lost the race with onServiceConnected
* so wait 250 ms and resend the Intent.
*/
try { System.getCurrentThread().sleep(250); } catch (InterruptedException e) { }
startService(intent);
}
iService->method1();
}
I am getting confused with all the different terminology when using Android: Activity, Service...
Right now I create a service:
startService(new Intent(this, RingerServer.class));
And this service starts a thread:
public class RingerServer extends Service {
public void onCreate() {
super.onCreate();
new Thread(new Ringer()).start();
}
public class Ringer implements Runnable { ... }
public void refuseConnection() { ... }
}
In this service, the RingerServer, I also have methods that I want to use. I would like to keep a reference to the RingerServer. I would basically like the Activity that created the service to be able to call refuseConnection(), but not make that method static.
startService returns a ComponentName, so I've been trying to cast it back to RingerServer but that doesn't seem to work. I see that it has getClass() and I've checked and getClassName() gives me the correct class. I haven't been able to use getClass() properly though.
Is there any way I can please keep a reference to the newly created RingerServer class? I am sure this is trivial, but I am stuck right now.
Thank you very much,
James
You have two options
1.Override onStartCommand of the service and start the server with intent using an action. that intent will be received in service, based on the intent action you can call refuseConnection()
//In Activity
...
//Start the service
Intent intent=new Intent("com.xx.xx.REFUSE_CONNECTION");
startService(this,intent);
...
//In Service
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
if(intent.getAction().equals("com.xx.xx.REFUSE_CONNECTION")){
//Refuse the connection
refuseConnection();
}else {
//Do something else
}
}
//In Manifest
<service android:name="RingerService">
<intent-filter>
<action android:name="com.xx.xx.REFUSE_CONNECTION"></action>
</intent-filter>
</service>
Implement AIDL interface and override onBind() of service , and use this interface to call refuseConnection(). Refer to this link http://developer.android.com/guide/developing/tools/aidl.html regarding AIDL.
You can use a ServiceConnection to get access to your service class. See sample code here:
Android service running after pressing Home key
That said, managing things via the service's onStart handler is much simpler.
I start a service in an activity then I want the service to stop itself after a while.
I called stopSelf() in the service but it doesn't work.
How to make the service stop itself?
By saying "doesn't work", I guess you mean that the onDestroy()-method of the service is not invoked.
I had the same problem, because I bound some ServiceConnection to the Service itself using the flag BIND_AUTO_CREATE.
This causes the service to be kept alive until every connection is unbound.
Once I change to use no flag (zero), I had no problem killing the service by itself (stopSelf()).
Example code:
final Context appContext = context.getApplicationContext();
final Intent intent = new Intent(appContext, MusicService.class);
appContext.startService(intent);
ServiceConnection connection = new ServiceConnection() {
// ...
};
appContext.bindService(intent, connection, 0);
Killing the service (not process):
this.stopSelf();
Hope that helped.
By calling stopSelf(), the service stops.
Please make sure that no thread is running in the background which makes you feel that the service hasn't stopped.
Add print statements within your thread.
Hope this helps.
since you didnt publish your code, i cant know exactly what you are doing, but you must declare WHAT you are stopping:
this.stopSelf();
as in:
public class BatchUploadGpsData extends Service {
#Override
public void onCreate() {
Log.d("testingStopSelf", "here i am, rockin like a hurricane. onCreate service");
this.stopSelf();
}
If by "doesn't work" you mean the process doesn't get killed, then that's how android works. The System.exit(0) or Process.killProcess(Process.myPid()) will kill your process. But that's not the Android way of doing things.
HTH
stopForeground(true);
stopSelf();
To let your service to stop itself.. create a BroadcastReceiver class.. In your service call your receiver like this..
In service
sendBroadcast(new Intent("MyReceiver"));
In Broadcast Receiver
public class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
context.stopService(new Intent(context,NotificationService.class));
}
}
Manifest file
<receiver
android:name="MyReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="MyReceiver"/>
</intent-filter>
</receiver>
Use stopSelf() to stop a service from itself.
I know this is an old question, but in my case (floating window as service) I had to remove the view first, and then call stopSelf().
windowManager.removeView(floatingView);
stopSelf();
I just ran into the same issue. In my case, I have a singleton service manager that I use to communicate with the service. In the manager the service is started like this:
context.bindService(new Intent(context, MyService.class), serviceConnection, Context.BIND_AUTO_CREATE);
By removing Context.BIND_AUTO_CREATE as suggested by Alik Elzin, I've been able to stop the service using this.stopSelf() and to have onDestroy() called when doing so. This problem is that after that I wasn't able to restart the service from the manager using the command above.
Finally I've fixed this by using a callback from the service that tells the manager to stop the service. This way the manager is always in charge when it comes to start/stop the service and everything seems to work fine. I don't know if there are any counter indications in doing it this way.
The code is really simple. Create a callback in the service and set it in the manager like this in your connection class:
private ServiceConnection mServiceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
myService = ((MyService.LocalBinder)service).getService();
myService.setCallback(new MyService.MyServiceCallback() {
#Override
public void onStop() {
stopService();
}
});
}
public void onServiceDisconnected(ComponentName className) {
myService = null;
}
};
and stop service:
public void stopService()
{
if(mServiceConnection != null){
try {
mContext.unbindService(mServiceConnection);
} catch (Exception e) {}
}
mContext.stopService(new Intent(mContext, BleDiscoveryService.class));
}
In the service, simply call myCallback.onStop() when you need to stop it.
Another dirty hack not mentioned here is to throw an exception like NPE. One day I needed to stop InputMethodService and this hack was useful.
if you use separate Thread in your service, after stopping service by calling stopSelf() or stopService() the Thread keeps running. if u want to stop Thread u should call Thread.interrupted() in the Thread(it might cause an Exception if Thread is already sleeping)