Defensive code for controlling a service - android

I'm trying to revamp my multi-activity app to use just once instance of a LocationListener which I intend to implement in a service. Prior to doing this I've been experimenting with a stub activity and a stub service to see what happens under error conditions.
I want to see what happens if I attempt to unbind from a service which has already been unbound and avoid any errors if this should happen. The activity has two buttons to bind/unbind. If I deliberately hit the unbind twice in succession I do get a runtime error.
What condition can I test for at the point marked '<<<<<' in the code below to skip calling unbind again?
My activity code is
public void myClickHandler(View target) {
switch (target.getId()) {
case R.id.bind:
Log.d("STAG", "Activity One pressed BIND button");
mServiceConnected = bindService(new Intent(
"com.nbt.servicetest.LOCATIONSERVICE"), mServconn,
Context.BIND_AUTO_CREATE);
break;
case R.id.unbind:
Log.d("STAG", "Activity One pressed UNBIND button");
try{
if (mServconn != null) // <<<< What to put here if already unbound?
unbindService(mServconn);}
catch(Exception e){
Log.d("STAG", "Exception " + e.getMessage());
}
break;
}
}
ServiceConnection mServconn = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("STAG", "Activity One service connected");
mIbinder = service;
}
#Override
public void onServiceDisconnected(ComponentName name) {
Log.d("STAG", "Activity One service disconnected");
}
};
The service is starting/stopping OK. I've put log lines in the service code with the same tag on all the pertinent lines. The output is :
STAG(2945): Activity One onCreate
STAG(2945): Activity One onStart
STAG(2945): Activity One onResume
STAG(2945): Activity One pressed BIND button
STAG(2945): Loc service ONCREATE
STAG(2945): Loc service ONBIND
STAG(2945): Activity One service connected
STAG(2945): Activity One pressed UNBIND button
STAG(2945): Loc service ONUNBIND
STAG(2945): Loc service ONDESTROY
STAG(2945): Activity One pressed UNBIND button
STAG(2945): Exception Service not registered: com.nbt.servicetest.ServiceTesterActivityOne$1#43b8b290
I note that the activity's onServiceDisconnected() never gets called, is this normal?

The simplest thing to do would be to introduce another variable, say, isServConnBound, and add checks on both bind and unbind actions. Of course, remember to update the variable after you call bindService and unbindService.

I agree with vhallac - just use boolean flags. What are your concerns with this approach? As for me there's nothing to be afraid of.
As to why "the activity's onServiceDisconnected() never gets called" - yes, this is normal. Look what API says on this callback:
Called when a connection to the Service has been lost. This typically happens when the process hosting the service has crashed or been killed.
Your process has neither crashed nor been killed, so this is an expected behavior. Even more, since you have your service in the same process, you'll never get this called. This is important when you bind to a service that runs in another process (inter process communication).

Related

onDestroy of a service is never called

I add a local service to my MainActivity, in the onResume, I did this
#Override
public void onResume() {
super.onResume()
boolean is_start = isMyServiceRunning(MyService.class)
if (is_start) {
bindMyService()
} else {
startMyService()
bindMyService()
}
}
In onPause I just simply do the "unBindMyService" operation.
Also, I add the Context.BIND_AUTO_CREATE flag to bind the service, the result is very strange.
I can see MyService's "onCreate" and "onBind" with logcat, this goes smoothly
When I switch to another activity or app, The "Unbind" is called, which is correct!
When I "force stop" the service in settings, the "onDestroy" of the Service is called in response, that is OK.
When I remove the app from the "Recent List" of the apps, there are no "onDestroy" of the Service is called, I can explain it as that the service is not terminated. also OK.
What I can't explain is that after 4, I launched my app again, I've noticed that the "onCreate" and "onBind" of the service is called, but without a single "onDestroy" of the Service. Even when "is_start" is true, the Service is created again without an "onDestroy" called.
So what happened between 4 and 5? The service is still alive or is dead?
you need to stop service to call onDestroy.
Use this:
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
stopService(new Intent(this,MyService.class));
}

Can we have blocking waits in onDestroy()?

I have a BroadcastReceiver which listens for an intent in onDestroy() callback. And there is a blocking while which goes on till bluetooth discoverability is switched off. Once discoverability is off, the changeModeReceiver will call its onReceive() and set destroy_ok to true, and hence breaking out of the while loop. But, this is not giving desired results.
Toast message, "In onDestroy()" is not getting printed
"In onDestroy()" is getting printed in the logcat
The bluetooth is still switched on
The code is as follows.
boolean destroy_ok = false;
protected void onDestroy(){
System.out.println("In onDestroy()");
Toast.makeText(getApplicationContext(), "In onDestroy()", Toast.LENGTH_LONG).show();
BroadcastReceiver changeModeReceiver = new BroadcastReceiver(){
public void onReceive(Context context, Intent intent){
String mode = intent.getStringExtra(BluetoothAdapter.EXTRA_SCAN_MODE);
if (mode.equals(BluetoothAdapter.SCAN_MODE_NONE))
destroy_ok = true;
}
};
IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
registerReceiver (changeModeReceiver, filter);
Intent discoverableIntent = new
Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,1);
startActivity(discoverableIntent);
while (!destroy_ok){}
unregisterReceiver(changeModeReceiver);
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter.isEnabled())
adapter.disable();
System.out.println("Leaving onDestroy()");
super.onDestroy();
}
The onDestroy method (as well as all other activity lifecycle methods, view callback methods, etc.) is called on the application's main UI thread, so no, you shouldn't block for a significant period of time when called. Doing so will likely result in lag, and may even spawn an ANR (application not responding) error if you block for more than 5-10 seconds.
Note: do not count on this method being called as a place for saving
data! For example, if an activity is editing data in a content
provider, those edits should be committed in either onPause() or
onSaveInstanceState(Bundle), not here. This method is usually
implemented to free resources like threads that are associated with an
activity, so that a destroyed activity does not leave such things
around while the rest of its application is still running. There are
situations where the system will simply kill the activity's hosting
process without calling this method (or any others) in it, so it
should not be used to do things that are intended to remain around
after the process goes away.
http://developer.android.com/reference/android/app/Activity.html#onDestroy()
So simply said, use onPause() for such operations. Also, I would use a Service or a new Thread in your case.

Android: How to tell when a bound service has been destroyed?

I've got a service that's bound to from a couple of activities, each using a ServiceConnection.
Each activity needs to check before calling the service whether the service is already in use. So in the service I have a function (let's say getCurrentId() ) which returns details of what the service is currently doing.
Then in the client activity, the service connection is set up:
private MyService mService = null;
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder binder) {
MyService.MyBinder myBinder = (MyService.MyBinder) binder;
mService = myBinder.getService();
activeId = mService.getCurrentId();
log.i(TAG, "Service bound" );
}
public void onServiceDisconnected(ComponentName className) {
log.i(TAG, "Service has been killed");
mService = null;
}
};
A button toggles binding to the service:
activity.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
and unbinding:
activity.unbindService(mConnection);
I'm not calling startService() at all.
Before I bind to the service, I check if it's already active and what it's doing:
if (mService == null)
activeId = -1;
else
activeId = mService.getCurrentId();
The problem is, if an activity binds to and then unbinds from the service, the service onDestroy() method is called (I've logging in it to confirm this), which is fine.
BUT this doesn't trigger onServiceDisconnected().
So mService is never set to null, and when I get to that if statement, it happily carries on and calls getCurrentId(), which returns whatever the previous details were.
I gather that onServiceDisconnected() is only supposed to be called when the thread the service is running in is unexpectedly killed, so it's correct that it's not called when the service is destroyed due to the last activity using it unbinding.
As far as I can tell, the service isn't being reinstantiated, I've got logging throughout it.
Which gives me two questions:
Is there an alternative callback function or some way where a ServiceConnection is notified that its service has been destroyed by unbinding?
If the service has been destroyed, then how can I still call its functions? Or is something else going on - is the ServiceConnection or the Binder somehow returning the value without actually calling the service?
onServiceDisconnected() is only called
when a connection to the Service has been lost. This typically happens
when the process hosting the service has crashed or been killed.
Quoted from the Android docs. This seems to be a very rare case, and will not be called when simply unbinding normally from a service.
To keep my connections with a service sane, I would suggest you bind to the service in the Activities onResume method and unbind from it in the onPause method.

onDestroy() not getting called while the activity is getting killed by the user?

I have an activity with two tabs. Clicking on two tabs will change the the fragments below the tabs. While that activity is in front I give out a notification, After that I minimize the app and kill that activity(not force stopping).
My problem is that am not getting call back in onDestroy while the activity is been killed by the user. Now if I click the notification the app will force close and thats because the activity for pending intent is been missing. Why am not getting the call back in onDestroy?
I found solution of that:
Create service:
public class MyService extends Service {
#Override
public final int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
#Override
public final IBinder onBind(Intent intent) {
return null;
}
#Override
public void onTaskRemoved(Intent rootIntent) {
Toast.makeText(getApplicationContext(), "APP KILLED", Toast.LENGTH_LONG).show(); // here your app is killed by user
try {
stopService(new Intent(this, this.getClass()));
} catch (Exception e) {
e.printStackTrace();
}
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
super.onTaskRemoved(rootIntent);
} else{}
}
}
and then start your service when app start:
startService(new Intent(this, MyService.class));
make sure you register service in your AndroidManifest.xml
<service
android:enabled="true"
android:name="yourPackageName.MyService"
android:stopWithTask="false" />
onDestroy is guaranteed to be called when you explicitly call finish().
On the contrary, when you are minimizing your app by pressing Home key onDestroy may well not be called right now. If your app stays in the background for a long time then onDestroy will be called.
For debugging purposes you can enable Settings|Developer Options|Don't save Activities. This way onDestroy will be called immediately when your app goes to background.
It is not sure to get callback in fragment's onDestroy(). When we kill the app Activity's onDestroy() will get the callback and the activity will be killed and fragment may not get callback.
As stated jn the documentation, onDestroy() can't be depended on, it will be called when the OS wants to kill the app, say in low memory conditions. Thus when the user hits the back button or home, onPause() or onStop() are called in place of it. Try implementing your callback in thr onPause() or onStop() method.

Service won't stop when stopService method is called

I currently have a Service that runs fine when I start it but when I try to stop it using the stopService method its onDestroy method doesn't get called.
Here is the code I use to try to stop the Service
stop_Scan_Button = (Button)findViewById(R.id.stopScanButton);
stop_Scan_Button.setOnClickListener(new View.OnClickListener(){
public void onClick(View v){
Log.d("DEBUGSERVICE", "Stop Button pressed");
Intent service = new Intent(CiceroEngine. CICERO_SERVICE);
releaseBind();
Log.d("Stop_Scan_Button", "Service: " + service.toString());
stopService(service);
Log.d("Stop_Scan_Button", "Service should stop! ");
}
});
Am I right in thinking that when stopService is used it calls the onDestroy method of the Service? When I press my stop scan button the onDestroy() method in my Service is not called.
Is there anything else I am missing that I should put in to stop the service?
EDIT: to add onServiceConnected() gets called when stopService is run instead of onServiceDisconnected(), why would that be happening?
EDIT:To add more info regards Binding
I call bindService in the onCreate() method and I then have the releaseBind() method unbind the Service.
Here is the code for that method:
public void releaseBind(){
unbindService(this);
}
So I presume that the unbinding is not my problem?
I am going to guess that your having a method call for releaseBind() means that you previously called bindService() on this service and that releaseBind() is calling unbindService(). If my guess is incorrect, please ignore this answer.
A service will shut down after all bindService() calls have had their corresponding unbindService() calls. If there are no bound clients, then the service will also need stopService() if and only if somebody called startService() on the service.
So, there are a few possibilities here:
You still have bound clients (e.g., other activities), in which case you cannot stop the service until they unbind
Since both unbindService() and stopService() are asynchronous, something might be going haywire with the timing, in which case you may get better luck if you call stopService() from your ServiceConnection's onServiceDisconnected() method
Also, bear in mind that the exact timing of the service being destroyed is up to Android and may not be immediate. So, for example, if you are relying upon onDestroy() to cause your service to stop some work that is being done, consider using another trigger for that (e.g., activity calling a stopDoingStuff() method through the service binder interface).
Are all your bindings closed?
A service can be used in two
ways. The two modes are not
entirely
separate. You can bind to a service
that was started with startService().
For example, a background music
service could be started by calling
startService() with an Intent object
that identifies the music to play.
Only later, possibly when the user
wants to exercise some control over
the player or get information about
the current song, would an activity
establish a connection to the service
by calling bindService(). In cases
like this, stopService() will not
actually stop the service until the
last binding is closed
.
hai guys sorry for the late answer but as far as i know i have successfull stop the service in this code: you may check a link here.
public void onClick(View src) {
switch (src.getId()) {
case R.id.buttonStart:
Log.d(TAG, "onClick: starting srvice");
startService(new Intent(this, MyService.class));
break;
case R.id.buttonStop:
Log.d(TAG, "onClick: stopping srvice");
stopService(new Intent(this, MyService.class));
break;
}
}
and in services class:
public class MyService extends Service {
private static final String TAG = "MyService";
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
Toast.makeText(this, "My Service Created", Toast.LENGTH_LONG).show();
Log.d(TAG, "onCreate");
}
#Override
public void onDestroy() {
Toast.makeText(this, "My Service Stopped", Toast.LENGTH_LONG).show();
Log.d(TAG, "onDestroy");
}
#Override
public void onStart(Intent intent, int startid) {
Toast.makeText(this, "My Service Started", Toast.LENGTH_LONG).show();
Log.d(TAG, "onStart");
}
}

Categories

Resources