In my activity, I start my service in onStart() of my activity & bind to service in onResume() :
public class MyActivity extends Activity{
private boolean isBound;
ServiceConnection myConnection = new ServiceConnection(){...};
#Override
public void onStart(){
super.onStart();
startService(new Intent(this, MyService.class));
}
#Override
public void onResume(){
super.onResume();
Intent service = new Intent(this, MyService.class);
isBound = bindService(service, myConnection, Context.BIND_AUTO_CREATE);
}
}
I have a BroadcastReceiver class , in its onReceive() callback, I want to re-start my service. I mean destroy it completely & creat it by calling startService() again:
public class MyBroadcastReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
//I want to re-start MyService from scratch, i.e. destroy it & start it (create it) again
Intent service = new Intent(context, MyService.class);
stopService(service);
startService(service);
}
}
But as Android document says, my above code doesn't guarantee the previous started service will be destroyed since I have also bind to it.
My question is what is the most efficient way to unbind MyService in MyBroadcastReceiver to restart MyService from scratch? As you see, the bound myConnection instance is in MyActivity...
From the Service documentation:
Multiple clients can connect to the service at once. However, the
system calls your service's onBind() method to retrieve the IBinder
only when the first client binds. The system then delivers the same
IBinder to any additional clients that bind, without calling onBind()
again.
When the last client unbinds from the service, the system destroys the
service (unless the service was also started by startService()).
So, when using your service, don't start your service using startService(intent), but rather by called bindService(service, conn, flags). This way, if your Activity is the only activity bound to the service, when you call unbindService(ServiceConnection conn) the system will kill your service, then you can rebind afterwards.
Related
On Android, I have an Activity called FirstActivity which starts a Service named MyService to do networking stuff in the background. The Activity and the Service communicate with each other all the time by calling methods.
Now when the user navigates from FirstActivity to SecondActivity, the background service should not be killed or re-created, but kept alive and passed to SecondActivity which will now be the one communicating with the service.
In other words, the Service shall be running as long as one of the two Activitys is running, and it should not stop while the user navigates between the two Activitys.
One of the Activitys will always be in the foreground and during this time, the service should (optimally) never get killed. I think this should not be a problem because one of those two Activitys is always active and thus Android knows the service is important and not something that must be killed.
(If there was no way to prevent Android from killing and re-creating the service from time to time, I would need a way to restore the full state of the service gracefully.)
To sum up, the Service should have the same lifespan as the two Activitys "combined". It should start with the first of them and stop not before both of them have been destroyed.
So is the following code correct for that setup and goals?
public class MyService extends Service {
public class LocalBinder extends Binder {
public MyService getService() {
return MyService.this;
}
}
...
}
public class FirstActivity extends Activity {
private MyService mMyService;
private ServiceConnection mMainServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
MyService mainService = ((LocalBinder) service).getService();
mMyService = mainService;
mMyService.setCallback(FirstActivity.this);
}
#Override
public void onServiceDisconnected(ComponentName className) {
mMyService = null;
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
...
startService(new Intent(FirstActivity.this, MyService.class));
}
#Override
protected void onResume() {
super.onResume();
bindService(new Intent(FirstActivity.this, MyService.class), mMainServiceConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onPause() {
super.onPause();
if (mMainServiceConnection != null) {
unbindService(mMainServiceConnection);
}
if (mMyService != null) {
mMyService.setCallback(null);
}
if (!isUserMovingToSecondActivity) {
stopService(new Intent(FirstActivity.this, MyService.class));
}
}
#Override
public void onBackPressed() {
stopService(new Intent(FirstActivity.this, MyService.class));
super.onBackPressed();
}
...
}
public class SecondActivity extends Activity {
private MyService mMyService;
private ServiceConnection mMainServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
MyService mainService = ((LocalBinder) service).getService();
mMyService = mainService;
mMyService.setCallback(SecondActivity.this);
}
#Override
public void onServiceDisconnected(ComponentName className) {
mMyService = null;
}
};
#Override
protected void onResume() {
super.onResume();
bindService(new Intent(SecondActivity.this, MyService.class), mMainServiceConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onPause() {
super.onPause();
if (mMainServiceConnection != null) {
unbindService(mMainServiceConnection);
}
}
#Override
protected void onDestroy() {
...
stopService(new Intent(SecondActivity.this, MyService.class));
}
...
}
Is this the best way to guarantee a long-lasting service in the background of the Activitys that will not be killed or re-created?
What about Context.BIND_AUTO_CREATE? Is it correct to have this flag set here? What about Context.BIND_ADJUST_WITH_ACTIVITY and Context.BIND_WAIVE_PRIORITY -- do I need these?
(Many thanks to #corsair992 for his useful pointers!)
If the activities are always called in that order (i.e. FirstActivity starts SecondActivity, and never the other way around, then you should, basically, attempt to "tie" the Service's life-cycle to FirstActivity's lifecycle.
In general (see caveats later), this means:
Call startService() in FirstActivity.onCreate().
Call stopService() in FirstActivity.onDestroy().
Call bindService()/unbindService() in the onStart()/onStop() methods of both Activities (to get access to the Binder object, and be able to call methods on it).
A service started this way will be alive until stopService() is called and every client unbinds, see Managing the Lifecycle of a Service:
These two paths are not entirely separate. That is, you can bind to a
service that was already started with startService(). (...) In cases like this, stopService() or
stopSelf() does not actually stop the service until all clients
unbind.
and:
When the last client unbinds from the service, the system destroys the
service (unless the service was also started by startService()).
With this basic strategy, the Service will live as long as FirstActivity is around (i.e. it is not destroyed). However, an important point still remains: in the event of a configuration change (e.g. a screen rotation) that is not handled explicitly will cause the activity to restart itself, and the service will be destroyed (since we're calling stopService() in onDestroy()).
To prevent this, you can check isChangingConfigurations() before actually stopping the service (since an onDestroy() callback occurring due to this reason means that although this particular instance of the Activity is being destroyed, it will be recreated afterwards.
Hence, the full solution would be something like:
public class FirstActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
startService(new Intent(this, MyService.class));
}
private ServiceConnection mServiceConnection = new ServiceConnection() { ... }
#Override
protected void onStart() {
super.onStart();
bindService(new Intent(this, MyService.class), mServiceConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
unbindService(mServiceConnection);
super.onStop();
}
#Override
protected void onDestroy() {
if (!isChangingConfigurations())
stopService(new Intent(this, MyService.class));
super.onDestroy();
}
While SecondActivity would only implement the onStart()/onStop() methods (in the same way).
A couple of notes about your particular implementation:
It's not necessary to override onBackPressed(), since if the activity is destroyed the necessary lifecycle methods will be called (plus, it could be finished without pressing the back button, for example if calling finish() on it).
Stopping the service in onDestroy() instead of onPause() saves you from having to check for isUserMovingToSecondActivity.
I have a background Service that need to be running even if the application gets killed by Android. This is currently working perfectly.
My problem is that when I restart the application (with the background service still running), I want my Activity to bind to the service to have access to some of its methods. When I try to bind with a ServiceConnection, the onServiceConnected is never called.
final private ServiceConnection serviceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
Log.d(TAG, "onServiceConnected"); //this is never called
MyBackgroundService.ServiceBinder binder = (MyBackgroundService.ServiceBinder) service;
backgroundService = binder.getService();
}
public void onServiceDisconnected(ComponentName className) {
Log.d(TAG, "onServiceDisconnected");
backgroundService = null;
}
};
private void bindBackgroundService(){
this.bindService(new Intent(this, MyBackgroundService.class), serviceConnection, Context.BIND_AUTO_CREATE);
}
Am I doing this the wrong way? Is it better to stop the Service and restart it?
Since the class that binded the background service is a singleton and my alarm broadcast receiver that is making sure the background service is always running instantiates this singleton, I had access to this singleton and I was trying to bind to the service that was already binded.
First question here, but I've been around for a while.
What do I have:
I'm building an Android app which plays audio streams and online playlists. Everything is working fine now, but I'm having issues in communicating with my service.
The music is playing in a Service, started with startForeground, so it doesn't gets killed.
I need to communicate from my activity with the service, for getting the track name, image, and a couple of things more.
Whats my issue:
I think I need to start my service with bindService (instead of my current startService) so the activity can talk to it.
However, when I do that, my service gets killed after closing the Activity.
How can I get both? Binding and foreground service?
Thanks!
No. bindService will not start a service . It will just bind to the Service with a service connection, so that you will have the instance of the service to access/control it.
As per your requirement I hope you will have the instance of MediaPlayer in service . You can also start the service from Activity and then bind it. If the service is already running onStartCommand() will be called, and you can check if MediaPlayer instance is not null then simply return START_STICKY.
Change you Activity like this..
public class MainActivity extends ActionBarActivity {
CustomService customService = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// start the service, even if already running no problem.
startService(new Intent(this, CustomService.class));
// bind to the service.
bindService(new Intent(this,
CustomService.class), mConnection, Context.BIND_AUTO_CREATE);
}
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
customService = ((CustomService.LocalBinder) iBinder).getInstance();
// now you have the instance of service.
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
customService = null;
}
};
#Override
protected void onDestroy() {
super.onDestroy();
if (customService != null) {
// Detach the service connection.
unbindService(mConnection);
}
}
}
I have similar application with MediaPlayer service. let me know if this approach doesn't help you.
Quoting Android documentation:
A bound service is destroyed once all clients unbind, unless the service was also started
And about the difference between started and bound just take a look to https://developer.android.com/guide/components/services.html
So, you have to create the Service using startService and then bindService, like #Libin does in his/her example. Then, the service will run until you use stopService or stopSelf or until Android decides that it needs resources and kills you.
In my Android project, I have a normal Service:
public class MyService extends Service{
#Override
public int onStartCommand(...){...}
...
#Override
public void onDestroy() {
super.onDestroy();
Log.d("MyApp","MyService onDestroy() is called!");
}
}
In my BroadcastReceiver class, I stop MyService & do another task :
public static class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
context.stopService(new Intent(context, MyService.class));
doAnotherTask();
}
}
According to my log, onDestroy() of MyService is executed after doAnotherTask() is done.
How can I guarantee that onDestory() of MyService is executed before doAnotherTask() get called?
P.S.: I thought I could do something like:
boolean isStopped = context.stopService(new Intent(context, MyService.class));
if(isStopped){
doAnotherTask();
}
But it could be possible that there is no service has been started, which means stopService(...) does nothing. So, I can't rely on my above code.
call startActivityForResult() .... and after you got the onActivityResult,.... call your method doAnotherTask()
i think that will do the job
How about sending a special intent to your broadcast receiver from the onDestroy() function? When your receiver gets it, then call doAnotherTask(). (I am assuming that you can't simply call doAnotherTask() from onDestroy() directly.)
send a broadcast in the service's onDestroy function and in it's observer do your after things
The call to stopService() is asynchronous. You are basically telling Android that you want it to stop the Service. You have no control over when this actually occurs.
If you need to trigger something AFTER the Service is destroyed, then you send a broadcast Intent in MyService.onDestroy() and use that to trigger whatever you want to happen when the Service is destroyed.
I would like to have a service (doing occasional monitoring) be active continuously. I plan to start it by listening to a BOOT_COMPLETE, which I believe makes it a "Started Service". I want a UI application to bound to it, which is working and documented. However, after the binding activity is destroyed, the Service dies because it's "unbound from all clients".
Is there a way to have a started service allow binding and still continue after the last bound services un-binds?
Returning true from onUnbind() wouldn't help, as the service should continue to be active even if no additional binder exist.
In Android, services are started in one of two ways - through the startService(Intent i) method, or the bindService(Intent i). The method used to start the service determines whether it is started or bound. A service can be started, then bound to a client - or bound and then have calls to start sent to it (it doesn't restart if already started).
As you mention listening for BOOT_COMPLETE, I assume this is an action for an Intent, which is sent via a Broadcast object. This means that you can create an IntentFilter object with the BOOT_COMPLETE action added to it via the addAction(String action) method. Then a BroadcastReceiver object can be created, which upon receiving an intent with an action of BOOT_COMPLETE can then call the startService(Intent i) method (this is done by overriding the onReceive(Context context, Intent intent) method).
If you call startService(Intent i) when the Intent is received, then the service will be a started service. This means that it will only stop when a call to stopService(Intent i) is made by the app, or if the service calls the stopSelf() method. It can be bound and unbound from by multiple activities during the time it is running, and it will not stop (as it is started, not bound).
Here is an example of this, using two Activity objects and a Service:
Activity 1 (first activity of your app):
public class ServiceActivity extends Activity {
private IntentFilter filter = new IntentFilter();
private BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if(action.equals(BOOT_COMPLETE) {
startService(new Intent(ServiceActivity.this, MyService.class));
}
}
};
#Override
protected void onStart() {
super.onStart();
filter.addAction(BOOT_COMPLETE);
registerReceiver(receiver, filter);
}
#Override
protected void onStop() {
super.onStop();
unregisterReceiver(receiver);
}
//Some other code
}
Activity 2 (used at some point after activity 1):
public class AnotherActivity extends Activity {
private MyService service;
private ServiceConnection connection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
service = ((MyService.MyBinder)service).getService();
}
#Override
public void onServiceDisconnected(ComponentName name) {
service = null;
}
};
#Override
protected void onStart() {
super.onStart();
bindService(new Intent(this, MyService.class), connection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
unbindService(connection);
}
//Some other code
}
Service:
public class MyService extends Service {
private MyBinder binder = new MyBinder();
#Override
public IBinder onBind(Intent intent) {
return binder;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
//Some other code
final class MyBinder extends Binder {
MyService getService() {
return MyService.this;
}
}
Final notes
To be able to use the service as bound, you need to override the onBind(Intent intent) method, and return an instance of binder MyBinder. Not doing so will result in not being able to bind (the binding sets the service variable by using the getService() method defined in MyBinder).
The BroadcastReceiver must alwasy be unregistered when the Activity it's in closes, as it would be leaked otherwise. That is why in the example, I have registered and unregistered in the onStart() and onStop() methods respectively. Using onDestroy() to unregister is not recommended as it is not always called.
The MyService object that is used when binding must also be unbound when it's Activity closes, as it too can be leaked. It is set to null when onServiceDisconnected(ComponentName name) is called for garbage collecting.
Sources for further reading
https://developer.android.com/reference/android/content/BroadcastReceiver.html
https://developer.android.com/reference/android/app/Activity.html
https://developer.android.com/reference/android/app/Service.html
https://developer.android.com/reference/android/content/ServiceConnection.html