Delay between bindService() and onBind() when starting a Service - android

I'm facing a strange problem with my app. I have a LocationService that runs in the background. The way I manage its lifecycle is:
In onResume, every activity uses bindService(intent,serviceConnection, BIND_AUTO_CREATE) like that the service is automatically created when needed.
In onStop, every activity uses unbindService(serviceConnection)
This works because when switching activities, the new Activity onResume is called before the old Activity onStop method
The problem I have is, lets say I start from the home screen and I launch the app with an Activity that has a fragment in it. The order of the function call is as follows
Activity onCreate --> setContentView is called here
Activity onResume --> here bindService is called and should create the Service
Fragment onResume
Service onBind method is called
My question is why is there a something else between my bindServiceand onBind calls?? I have a feeling this has something to do with threading issues.

Well, this isn't a very good answer, but why wouldn't there be something else between your bindService() and onBind() calls? You seem to be assuming that when you call bind the system will immediately create and start your service, and no such guarantee is provided.
You haven't indicated whether yours is an IntentService, or just a regular Service, and this might affect the speed with which your service is launched, but my answer would be the same.
Also, I'm assuming that your ServiceConnection object is called, as expected, sometime after your service's onBind() returns.
I say this not based on having read anything definitive, but because I had a similar experience: I was surprised and annoyed at how long it took before my service was created and my ServiceConnection was called, even though I returned from the service's onBind very quickly. (My fragments needed data from the service to create the initial UI so any delay in the creation of the service meant a delay in displaying the initial UI to the user.)
In the end I settled upon launching my service using an Intent rather then a bind, e.g.
Intent si = new Intent( this, com.company.MyService.class );
si.setAction( MyService.SVC_BIND );
startService( si );
I then sent MyService.SVC_UNBIND in place of calling unbind. When I received the UNBIND intent in my service I cleanup and then call stopSelf(). These are user actions - I just named them for how I'm using them.
I believe this was faster, but looking back upon my comments from that code I don't have any specifics. Note that this meant no ServiceConnection, but I'm making some direct calls from the activities into the service, and using LocalBroadcastManager a fair bit.
Another option to consider (in order that your service be started more quickly, if that is your goal here??) is to launch it in Appliction.onCreate(), rather then waiting for Activity.onResume(). All of these options make it necessary to do some extra work to determine when to stop the service, compared to your current, normal, scheme where that is taken care of for you.

In my case, my issue was using android:process attribute for <service> element within Android Manifest, which is supposed to improve performance, but in reallity, maybe it does once the service is running, but it takes a very long while to reach onCreate() (and so also to reach onBind()). For me it was taking minutes. Now Apps and services run smooth and as expected.
More info:
https://developer.android.com/guide/topics/manifest/service-element

Related

how to auto start service after load all views in MainActivity?

I have a problem like the above question. I want to know when all views will load on MainActivity and after loading I will start running Service. Is there any way? Thank you.
You can start the service in onResume() of your MainActivity. Based on the documentation:
This is where the lifecycle components can enable any functionality
that needs to run while the component is visible and in the
foreground, such as starting a camera preview.
If lifecycle of your service is tied to that of Activity, then remember to call stopService() in onPause(). Otherwise, you have to do some condition checking beforestating the Service to avoid calling startService multiple times.

Android: How to backup a database when the application gets closed?

I am needing help to determine the right approach. I want to make a backup of an internal database to a location in the external storage every time the whole application gets interrupted or terminated/destroyed. I want to call this method from a central class called Main which extends Application. The reason for that is, that I need to use several activites and I want to call the backup Method only when needed (like described when the whole application gets destroyed or interrupted by another application). I try to avoid calling this backup method in every activity in their onPause() methods.
I thought about starting a service in the onCreate() method of the application, and starting the backup method when the service gets destroyed. But this won't help in the case of an interrupt, as far as I understood the logic behind services. And also the service doesn't seem to start. startService(new Intent(getApplicationContext(), BackupService.class)); Furthermore I don't think it is a good approach to just use the onDestroy() method of a service, this is not what the service class is made for in my opinion.
So summarizing my Question, do you know a better way then using a service, or if not do you know how I should use the service to be able to call a backup only at the point when the whole app (and not only an activity) is interrupted or destroyed.
First of all, if your service "doesn't seem to start", you are probably doing something wrong.
To accomplish your goal make a backup of an internal database to a location in the external storage every time the whole application gets interrupted or terminated/destroyed:
There are three cases in general here.
If you want to do it in the activity layer:
To know when your application is crashed, you need to implement a custom handler to catch the uncaught exceptions.
To know when your activity is "interrupted", the only way is do it in onPause.
To know when your activity is "terminated", the only way is to do it in onDestroy.
This will require you to have a clear navigation and only do it in your "main activity", and all the other activity starts and comes back to it OR use a flag to indicate if the pause was caused by going to another activity.
If you want to do it in the service layer: (Your way of doing it onDestroy won't allow you to detect interrupted case since you will have to start service sticky to keep it running)
You will have to set up a flag on each activity onBind (you will have to bind it and unbind it) to know if it is a crash/interrupt/termination, which will complicate other part of your code.
To avoid running repetitive code, you will have to create a generic base class and extend your other activities from it.
I use this approach to play background music in one of my games, but I guess it works in this scenario as well.
Use a boolean flag to indicate whether or not your app is launching another part of your app.
boolean movingInApp = false;
....
movingInApp = true;
Intent intent...
.....
public void onPause() {
if(!movingInApp) {
//start service
}
}
public void onResume() {
movingInApp = false;
//Stop service
}
By setting the value of movingInApp to true before launching any intent etc, you can prevent your app from starting the service. Remember to set it to false again later in your onResume() method. If the system makes your app go to the background, this will be false, and your service will be started.
Why dont u have all of your activities extend a base activity which in turn extend the android activity class
I the base activity have backupDB method in the onPause
Therefore u dont have to put it in every activity pause method

Send data from service to activity and screen rotation

I am using an Intent Service that performs an action and needs to pass back to the activity that started it the results of the action.
I've searched through dozens of similar posts but as far as i can tell, all solutions i found have a problem. They don't handle well screen rotation. Suppose an activity starts the Intent Service, the service takes 10 seconds to perform the action, and during those 10 secs, the screen gets rotated. The activity gets destroyed and a new one is created.
Use Receiver : It creates a memory leak , as the receiver is bound to the activity that must be destroyed, so the activity never gets destroyed.
Use Broadcast : You have to register a listener, and unregistered the listener before the activity gets destroyed. If the broadcast message arrives after the listener is unregistered, and before the new activity's listener is registered, the message will never be received.
Use Messaging : Same as receiver.
Use Shared Preferences/database with listener : Same as Broadcast.
The solution i came up with, is having the service save the result in a preference file, and the activity checking regularly (lets say every 200ms) for a change in the preference file. Thus, when the screen rotates, the activity stops checking, and starts again when recreated. If the result was delivered in between, it still gets to the (recreated) activity. However, it seems as though this consumes cpu and performs unnecessary reads from the SD card.
Another solution would be to have the service save the result in preference file/database and set a global variable to the time it saved it. The activity has a listener to the preference file/database. Before registering the listener, it checks the global variable to see if the result was put during the screen rotation (global var < currentTimeMillies()) and if true, gets the result, if not, registers the listener. Since the result might be put between the check and the registration, this has to be done inside a block in which the activity holds a lock that the service must acquire to put the result. This would also work, but it is way too complicated.
Is there a simpler and more elegant way of doing it, surviving a screen rotation?
Have a look at my answer to this question:
How to handle IPC between a service and an activity (and its subactivity)?
Perhaps that will give you an idea.
EDIT (Add following suggestion):
Another approach would be to use a Receiver which you create in the Activity. On a screen rotation, the OS will call onRetainNonConfigurationInstance() where you can return the Receiver instance and it will get handed off to the new Activity (see getLastNonConfigurationInstance()). NOTE: These methods have been deprecated in 4.0 and you can use a Fragment and setRetainInstance() to achieve similar behaviour.

Android: service destroyed when display is rotated

I've got a service that is running in a separate process. I'm finding that after the main process UI thread exits from onDestroy() that my service is being destroyed even though I've provided the application context with the binding and specified BIND_AUTO_CREATE.
In my main process' UI thread onCreate() I've got this binding code:
Intent intent = new Intent(mAppContext, MyService.class);
mAppContext.bindService(intent, mMyServiceConnection, Context.BIND_AUTO_CREATE);
In my main process' UI thread onDestroy() I've got this unbinding code:
mAppContext.unbindService(mMyServiceConnection);
Note that I never call stopService().
Android's documentation for bindService() says:
The service will be considered required by the system only for as long as the calling context exists.
If I'm reading that correctly, because I supplied the application's context, the service is considered required by the system for the life of the application.
I have thought that maybe the application's context dies with onDestroy(). This is what Android's documentation says for getApplicationContext():
Return the context of the single, global Application object of the current process.
If the application's context dies with onDestroy(), then I think Android has a big issue. The issue is that when the display is rotated, onDestroy() is called (and immediately followed by onCreate()). Thus the effect is that when the display is rotated -- and it occurs quite frequently in my case! -- my service always exits.
Note that the pid of my app's process never changes, i.e. it is the same process. That is important in light of the documentation for getApplicationContext() stating "current process."
Here are what my debug logs show:
04-03 05:15:12.874: DEBUG/MyApp(841): main onDestroy
04-03 05:15:12.895: DEBUG/MyApp(847): service onUnbind
04-03 05:15:12.895: DEBUG/MyApp(847): service onDestroy
04-03 05:15:12.934: DEBUG/MyApp(841): main onCreate
04-03 05:15:12.966: DEBUG/MyApp(847): service onCreate
04-03 05:15:12.975: DEBUG/MyApp(847): service onBind
So my questions are:
1) Is my understanding about binding/unbinding correct?
2) Is there a way to have my service not get destroyed when UI thread's onDestroy() is called?
A hack for question #2 is to never unbind. But I don't like it because then I am leaking a binding every time onDestroy() is called. I could "remember" that I've got one leaked binding, and leak just that one, but then I've got cascaded hacks and it's real ugly.
1) Yes, I think your understanding is correct (I say I think because I think I understand what you're saying ;-) ). The flag you are using means "start this service automatically if somebody tries to bind to it and keep it running as long as somebody is bound to it, but once nobody is bound to it, feel free to kill it".
2) Check out the START_STICKY flag as described here. That should allow you to start the service and keep it running regardless of what happens to the calling Context
In general, onDestroy() means your activity is about to be killed. When you rotate the display, the Activity is killed and recreated. You are responsible for saving any state to the Bundle in the appropriate method and then restoring it in onCreate().
Does your service get killed:
if there is a second Activity on the stack?
if you handle configuration changes?
Why do you need your service to stay alive after your application has been destroyed?
I think the general rule of thumb is that you can't be sure when your activities and services will be killed. If this is blocking something you're trying to achieve, there may be a clever way around it.
Edit - you can actually handle the orientation configuration change so that your activity isn't restarted. For more info see the second half of this answer.
About the "second" Activity: Image you start activity A and then activity B. Now you rotate the screen while B is showing, causing B to restart. Will A be restarted at this point? I'm not sure, but I have a hunch that A will tend to stay alive and keep your application alive during the orientation change. This might be another strategy for keeping your service alive if that's what you're aiming for.
a service is destroyed only when both of the following are true:
All calls to bindService() have been matched by corresponding calls to unbindService().
If somebody called startService(), somebody also called stopService() or the service called stopSelf().
A service can be both started and have connections bound to it. In such a case, the system will keep the service running as long as either it is started or there are one or more connections to it with the Context.BIND_AUTO_CREATE flag. Once neither of these situations hold, the service's onDestroy() method is called and the service is effectively terminated.
this gives a great solution which is also fairly correct and safe!
say there are 2 activities ('viewer' and 'chat' in this example) that need a service, if both bindService AND startService. Also using the binder they update 'viewer_connected' and 'chat_connected' during onStart and onStop.
Then the service runs a loop in a thread that does this:
public isRunning = true;
while (isRunning) {
if (viewer_connected) {
// send update to viewer activity
}
if (chat_connected) {
// send update to chat activity
}
try {
Thread.sleep(5000);
} catch (Exception e) { isRunning=false; }
// 3 second timeout before destroying service
if (!viewer_connected && !chat_connected) {
try { Thread.sleep(3000); } catch (Exception e) { isRunning=false; }
if (!viewer_connected && !chat_connected) isRunning=false;
}
}
stopSelf();
This works because it needs both the activities to unbind AND the service to stopself() before it destroys the service, meaning there is a timeout before the service gets destroyed.

Stopping and starting a Service based on application state

I have a Service which tracks the location of the user. Currently, the Service boots when the application starts and stops when the application terminates. Unfortunately, if users keep the application in the background, the Service never stops and drains battery.
I would like the Service to stop when my application is not in the foreground. I was hoping the Application class would let me Override onPause and onResume handlers, but it does not have them. Is there another way I can accomplish this?
I haven't tested this yet, but it looks like if you use Context#bindService() (instead of Context#startService()), the service should stop when no more activities are bound to it. (see Service lifecycle).
Then use onPause()/onResume() in each activity to bind/unbind from the service.
Alternatively, you could add a pair of methods on your service which tell it to start/stop listening for location updates and call it from each activity's onResume()/onPause(). The service would still be running, but the location updates wouldn't be draining the battery.
Reading all the above answers I would suggest Simply add a boolean global flag for each activity & put it in your onResume & onPause & also while launching an Activity Something like this
public void onPause()
{
super.onPause();
activity1IsResumed = true;
}
&same for onResume
& similarly when launching a new Activity
startActivityForResult(myintent ,0);
activity2IsResumed = true;
activity1IsResumed = false;
then in your Service simply check
if(activity1IsResumed || activity2IsResumed || activity3IsResumed)
{
//your logic
}
else
{
//another logic
//or dont run location tracker
}
& you are done!
You should override the onPause and onResume methods on your Activity. If you have multiple activities you may want to have a common base class for them and put the start/stop logic into the base class.
I have not tried this approach but I think you can override the home key of android device by using KeyEvent.KEYCODE_HOME and you can use stopService(Intent) to stop your service and when again application resumes, you can write startService(Intent) in the onResume() method of your Activity.
This way I think your service will only stop when user explicitly presses home button to take application in the background and not when he switches from one activity to another.
What I would suggest is overriding the onPause/onReume methods as others have said. Without knowing more about the flow of your application and interactions between Activities, I can't give much more information beyond guesswork.
If your Activities are persistent, however, my recommendation would be to utilize the Intents better when switching between Activities.
For instance, each Activity should have a boolean "transition" flag. So, when you move from one Activity to the next, you set up an Intent extra:
intent.putExtra("transition",true);
Followed in the receiving Activity by: (in onCreate)
intent.getBooleanExtra("transition",false);
This way, for each Activity that launches, you can know whether it has come from another Activity, or if it has been launched from a home screen launcher. Thus, if it gets a true transition, then onPause should NOT stop the service--that means you will be returning to the previous Activity after it returns. If it receives no "transition" extra, or a false transition, then you can safely assume there is no Activity underneath it waiting to take over for the current one.
On the first Activity, you will simply need to stop the service if you are switching to another Activity, which you should be able to figure out programmatically if one Activity is started from another.
It sounds like the real problem is how to only stop the service when you go to an activity that isn't one of your own? One way would be to in your onPause method to stop the activity. Do this for all your activities. Then override your startActivity method. And in here do a conditional test to confirm that you are purposefully navigating to one of your own. If your are set a flag to true.
Now go back to your on pause overridden method. And only stop your service if the flag is not equal to true. Set the flag to false.
All events that navigate away will close your service. Navigating to your own will leave it intact.
Do the overriding in a base class that all your activities extend.
Writeen in my andolroid. Will post ezaple later.
Try using the Bound Services technique to accomplish this.
Bound Services | Android Developers
You can use bound services in a way such that the service will stop when no activities are bound to it. This way, when the app is not in the foreground, the service will not be running. When the user brings the app back to the foreground, the Activity will bind to the service and the service will resume.
Create methods registerActivity() and unRegisterActivity() in your Application object and implement first method in all you acts onResume() and second in acts onPause().
First method add activity to List<Activity> instance in your app object, unRegisterActivity() checks size of list in every call if==0 stopService();.

Categories

Resources