I am asking a vexed question that has been (partially, to my mind) addressed here and here. Let's say like in many examples we want to create a music application, using (say) a single activity and a service. We want the service to persist when the Activity is stopped or destroyed. This kind of lifecycle suggests a started service:
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
Ok, but we also want to be able to communicate with the service, so we need a service binding. No problem, we have both a bound and started service as this answer suggests:
At activity startup (or some other point) we call startService()
After that we call bindService() to obtain the IBinder interface, and proceed from there.
So far so good, but a problem arises from the fact that when the activity starts, we do not know if the service is around or not. It may have been started or it may not have been. The answer could be something like:
At startup, try to bind to the service (use bindService() without the BIND_AUTO_CREATE flag)
If that fails, then start the service using startService(), and then bind to it.
This idea is premised on a particular reading of the docs for bindService():
Connect to an application service, creating it if needed.
If zero flag means "service is not really needed" than we are OK. So we try something like this using the following code:
private void connectToService() {
Log.d("MainActivity", "Connecting to service");
// We try to bind to an existing service
Intent bindIntent = new Intent(this, AccelerometerLoggerService.class);
boolean bindResult = bindService(bindIntent, mConnection, 0);
if (bindResult) {
// Service existed, so we just bound to it
Log.d("MainActivity", "Found a pre-existing service and bound to it");
} else {
Log.d("MainActivity", "No pre-existing service starting one");
// Service did not exist so we must start it
Intent startIntent = new Intent(this, AccelerometerLoggerService.class);
ComponentName startResult = startService(startIntent);
if (startResult==null) {
Log.e("MainActivity", "Unable to start our service");
} else {
Log.d("MainActivity", "Started a service will bind");
// Now that the service is started, we can bind to it
bindService(bindIntent, mConnection, 0);
if (!bindResult) {
Log.e("MainActivity", "started a service and then failed to bind to it");
} else {
Log.d("MainActivity", "Successfully bound");
}
}
}
}
And what we get is a successful binding every time:
04-23 05:42:59.125: D/MainActivity(842): Connecting to service
04-23 05:42:59.125: D/MainActivity(842): Found a pre-existing service and bound to it
04-23 05:42:59.134: D/MainActivity(842): onCreate
The global question is "Am I misunderstanding bound versus started services and how to use them?" More specific questions are:
Is it the correct understanding of the docs to think that zero flag passed to bindService() means "Do not start the service"? If not, is there no way to call bindService() without starting the service?
Why does bindService() return true even if the service is not running? In this case it doesn't seem like the service has been started, based on Log calls.
If there the previous point is the correct/expected behavior of bindService(), is there a workaround (i.e. somehow ensure that startService is called only if the service is not running?)
P.S. I've moved on from the problem in my own code: I issue startService() calls regardless, since repeated startService() are simply ignored. However, I would still like to understand the issues better.
If you bindService with 0 flag then the service will not start. You can bindService with BIND_AUTO_CREATE flag and if the service is not started, it will be started. However, when you unbindService the service will be destroy.
bindService with 0 flag always returns true.
You can always call startService. If the service is already running then a new service will not be created. The running service onStartCommand will be called.
There should not be any problem if you startService in onCreate and then bindService in onResume and unbindService in onPause.
Related
I am using a service in my app. The service is used to show notifications. I send extras to the service when I start it. The problem is that when I remove the application window from the background the onStartCommand method is called again with a null intent so the extras become not exist. So my question is how can I stop the sticky service? Is it possible or no?
You can stop Stiky service by Calling stopService(intent_of_your_service) at onPause() or onDestroy() of your service.
Here, intent_of_your_service is the Intent you created at onCreate(), or Use stopSelf() (i.e., it stops the service) for stop sticky service.
My Android application starts a local foreground service (in the same process as my activity). The service have to stay alive, even if the system destroys the activity due low memory.
Next time a user starts the app, I need to check whether the service is running or not.
I tried to store state in public static boolean Started = false; flag in service class (I know that it is bad practice).
My activity starts in the process where service is running. I thought, that static constructor is called ones. So, I would be able to check this flag in activity. However, static data is reinitialized to defaults.
Could any one give me a cue about this behavior?
here is just an example how I restart the service when apps come forground
public void storeLastLocation() {
if (!isStoreLocationActive){
Log.d("IQ MA", "storeLastLocation() called");
String access_token = Configs.access_token;
Intent intent = new Intent(MainActivity.this, StoreLocationService.class);
intent.putExtra("access_token", access_token);
stopService(intent);
startService(intent);
isStoreLocationActive = true;
}
}
then store boolean on service is running
I noted that when I close my app definitively, the method runInBackGround of the class MultiplyTask stops working. It works when the activity is in the phase STOP or PAUSE, but when I close my app, the method finishes ( it's a loop created with a cycle while(true) {...} ).
How can for example Whatsapp send notifications though it's closed? I want to create a similar thing. Thanks!
When the app is closed, all code will stop running. If you are looking to execute while the app is open and continue executing code while the app is closed, you will want to look into using a Service.
Take a thorough look at the Service documentation and it will hopefully be what you are looking for.
Services are also killed when your app is closed, but using the START_STICKY return value you can make sure your service is restarted upon termination.
EDIT WITH MORE INFORMATION:
<service
android:name="MyService" />
Add the above to your AndroidManifest.xml
public class MyService extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// This is where you would place your code that you want in the background
// Putting your while loop here will make sure it runs when the app is closed
return Service.START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
//TODO for communication return IBinder implementation
return null;
}
}
Create a new class with the above code.
Intent i= new Intent(context, MyService.class);
startService(i);
Call this code from your launcher Activity to start the service when the app is launched.
Hope this helps!
Asynctask is ideal for short operation which are needed to be performed in the background. Usually Asyntask is implemented as sub class of activity which is destroyed when app is close. Also It communicates with the UI thread at some points ... so It needs the activity to be in memory... For long running operation, Service is better. Some Apps notify user even when they are not running. In fact they have one or more services running in background. You can see those in your phone setting->apps menu.
Study this for more information about services.
I have added Google Now search within my application. The way it works is, the user types something in the EditText object and oonce they press the button, this intent runs -
Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
String term = searchInput.getText().toString();
intent.putExtra(SearchManager.QUERY, term);
startActivity(intent);
However I am having issues killing the service after the intent has been launched.
calling finish();after the intent will not work unless I am directing the intent to a different class itself.
Here's a video preview fo what I mean, you can clearly see I am pressing home to close the app but when I reopen it . It opens to the Google Now app. But when pressing back, it stops. How exactly can I get the intent to stop after I press the home key?
And my app is a dialogue type app so I have excluded it from the recent menu because of allot of reasons. Everywhere else the app kills itself as soon as the user exits it but here the service keeps running.
Video - Video Preview
I have also tried many questions answers, have researched but nothing has helped so far. I may be doing something wrong. Could someone kindly help me out here, thank you very much! :)
A started service must manage its own lifecycle. That is, the system does not stop or destroy the service unless it must recover system memory and the service continues to run after onStartCommand() returns. So, the service must stop itself by calling stopSelf() or another component can stop it by calling stopService().
use stopSelf() upon compleetion of task assigned to service.
And if you want to finishs ervice when application finishes, then in onDestroy(), call stopService() after checking whether its running or not, such method can be used:
public static boolean isMyServiceRunning(Class<?> serviceClass, Activity pActivity) {
ActivityManager manager = (ActivityManager) pActivity.getSystemService(Context.ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
to stop service from activity use below code:
stopService(new Intent(yourMainActivity.this,yourIntentService.class));
or you can write condition in service to stop itself like below:
if(check your condition here)
stopSelf();
You don't have to stop an IntentService - it will terminate itself when it has no more work to do.
It runs on separate worker thread.
Multiple intents are queued on the same worker thread.
So even if your Activity was destroyed from where this service was started it will continue performing its tasks until all the queued ones are complete and after completion it is automatically terminated.
Caution: It's important that your application stops its services when it's done working, to avoid wasting system resources and consuming battery power. If necessary, other components can stop the service by calling stopService(). Even if you enable binding for the service, you must always stop the service yourself if it ever received a call to onStartCommand().
stopService() : Request that a given application service be stopped. If the service is not running, nothing happens. Otherwise it is stopped. Note that calls to startService() are not counted -- this stops the service no matter how many times it was started.
Note: that if a stopped service still has ServiceConnection objects bound to it with the BIND_AUTO_CREATE set, it will not be destroyed until all of these bindings are removed.
In my project I am starting a service when a button is clicked.
But I don't want to start that service again when that button is clicked unless the previous one is already stopped. So I need to check first whether the service is running or not. I used the following method
private boolean isMyServiceRunning() {
ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if ("com.example.MyService".equals(service.service.getClassName())) {
return true;
}
}
return false;
}
But its not working for me, it doesnt give any exception but always returns false. what should I do now?
I think the reason why your service is not listed in running services is the way you start your service. The following proposal is the same thread you took your method from.
You MUST call startService for your service to be properly registered and
passing BIND_AUTO_CREATE will not suffice.
Like the following:
Intent bindIntent = new Intent(this,ServiceTask.class);
startService(bindIntent);
bindService(bindIntent,mConnection,0);
Try this and see if it works for you too.
Your method return false because after stop, process is destroyed by system if there are no any clients bound to service.