Is it possible to create a service in Android, which can not be started programmatically via startService call? I mean, that an application with this service and the service itself should be started explicitly by a user only, and should be accessible only via bindService, in the case if the service is already started at the moment.
Is it possible to create a service in Android, which can not be started programmatically via startService call?
Not really. You could presumably call stopSelf() from onStartCommand(), though I have never tried this and it may cause unexpected problems.
the service itself should be started explicitly by a user only
There is no way for a user in Android to start a service directly, only by means of some app, such as your own app.
and should be accessible only via bindService
You have no means of preventing a call to startService(). If your service is not exported, no other application will be able to start or bind to it.
and if it's started, it's available to other applications via bindService
Only if you export it. As #Argyle notes, if you do not want other applications starting or binding to the service, do not export it.
Can't be started by startService? How else would it be started?
I suspect what you want is to ensure that only your own app can start it and not any external non-written-by-you apps. If that's the case, set
android:exported="false"
in your AndroidManifest.xml file for each Service you want protected in this way.
Related
I have an Android Library with a service, which I implemented using AIDL. I want a single instance, cross application to be used with other apps. So I have a base app with the service and I managed to make the library for other apps to use the same instance.
My problem comes when I close all apps using the service, because for every app, the on destroy unbinds from the service, but the service is still running.
Also, I'm only using the service by binding, not by startService().
I checked through android studio that the service is running after closing the apps, and the counter I have on the service for each bind/unbind call is 0!. I increment the counter when there is a call to bind, and decrease for calls to unbind. My only way to make the service stop is by opening the base app, which has the service defined and closing it.
Edit: Also noticed that onBind is being called only once, even for other apps that are binding, but the reference for all those apps is still the same service, they share the same information and only one Service is shown in the android studio.
Edit: I've also observed that if one of the apps using the service is the base app, if I close it, the service dies and another one is started, the other apps don't notice the change, they keep using the service as if nothing happened, which is understandable because it's a remote service.
What is happening and what can I do about it?
For the two points being questioned:
I was able to verify what CommonsWare said that the process in which the service is run is still up, but the service itself is not. At first I had a thread running in the service after every app unbound, but after making sure it wasn't up in the end made the service be destroyed.
The second issue, regarding the service being destroyed while still bound with other activities was solved by seeing this link which describes a bug in android that kills services when it shouldn't. My case was simply solved by making my service run in the foreground, which I didn't know was possible.
After some tests I verified the service is still intact as long as any app is bound with it, and that the service indeed is destroyed correctly after no more app is bound.
It's my understanding that Android services are supposed to be singletons - no more than one class instance running at a time. So you're supposed to start them via intents, as opposed to
MyService mse = new MyService();
However, in Google's in-app billing sample, that's exactly what they do in Dungeons.java, line 235. So it's obviously legal.
I'm wondering, if I start a service like this, will the framework later recognize that it's running? In other words, if I try to call startService() on the same service later on, will the framework recognize that an instance of the service already exists and dispatch startService() calls to it?
I don't know what example you are referring to. But you absolutely positively cannot instantiate an Android component (Activity, BroadcastReceiver, Service, Provider) yourself using the new keyword. These components can only be instantiated by the Android framework, as the framework needs to set up the Context of the component.
There is (unfortunately) nothing stopping you from writing Service s = new MyService();, but it will do you no good. Android will never call any of the lifecycle mathods on this Service and any calls you make to methods of the Service will probably fail spectacularly because the instance has no Context.
If you instantiate the service directly instead of using intents that guarantees the service will run within your Activities process. If that activity should be killed then down goes the service too. Is that a bad practice? Well it depends on what you wanted. If you need that service to live through potential shutdowns of activities then yes that's a bad thing to do. If you don't care or your app can tolerate those shutdowns then it's ok. However, I'd argue if you need a background job running that can be stopped when your Activity stops then you need to use AsyncTask and NOT a service.
I have an Android app, in which Activities fire long running operations that run in the background. These operations interact with the Activities when done. I'm developing a component that handles the Activity/Long-Running-Task coupling, taking care of activities being destroyed and recreated.
Right now that component is implemented as an Android service. The activities call bindService and use the resulting IBinder to start and track tasks. I decided against using startService, because I prefer the richer API possible through a Java interface.
Now the problem. Activity A start ups, binds to the service and calls serviceApi.runTask(...). Activity A is then destroyed (because the user flips the phone, for instance) and recreated as Activity A'. A' then binds again to the service, announces its existence and everything should be running nicely.
Except that my Service gets destroyed. When Activity A is destroyed, it unbinds from the service. Android sees there are no more clients, and kills the service. When Activity A' is created, the service is created again, and I lose everything the old service had.
The only solution I can see is using a singleton for the service. And then it doesn't really have to be an Android service, just an instance that's accessible to everyone. Is that frowned upon in Android? Is there a better design that fits this problem?
Editted: Even if I call startService and then bind to it, nothing guarantees that the service instance will exist as long as the application is running. Android can kill sticky services if resources are low. Killing the service will cause the application to malfunction, and I can't have that.
Even if I call startService and then bind to it, nothing guarantees that the service instance will exist as long as the application is running.
Correct.
Android can kill sticky services if resources are low.
Also correct. All "sticky" means is that Android might restart the service.
Killing the service will cause the application to malfunction, and I can't have that.
It is impossible to create a service that is guaranteed to run forever. For starters, users can get rid of your service whenever they want, because users detest developers who have pointless services that run forever. Writing everlasting services is necessary only in very few cases; otherwise, it's just sloppy programming.
The only solution I can see is using a singleton for the service. And then it doesn't really have to be an Android service, just an instance that's accessible to everyone. Is that frowned upon in Android?
Singletons (a.k.a., static data members) will go away when the process is terminated. The process will be terminated eventually, particularly if there are no active services and none of your activities is in the foreground..
Call startService and in onStartCommand return START_STICKY. It should keep the service going.
You may also want to look into foreground services:
http://developer.android.com/reference/android/app/Service.html#startForeground(int, android.app.Notification)
Yo have to create persistent service. Refer to this manual.
In a few words - don't call bindService, call startService.
Is there a possibility to autostart Service upon starting Application ? The problem is that I am developing separate UI component that depends on service. Ideally this service should be started as soon as hosted application starts. Could this be done via manifest only or could it be done at all ? I know I can start service from my UI component's code, but I want to start service immediatelly after starting main application even in case if my UI component hasn`t been created yet.
Thanks in advance.
Create a MyApp class which extends Application, and make sure it's declared in your manifest. MyApp's onCreate() is then a good place to start the service if you need to.
See the documentation for the Application class.
You want to use the PERMISSION that notifies you of boot completion. This will allow you to know that the device has started and take action, such as start your service.
RECEIVE_BOOT_COMPLETED Allows an application to receive the ACTION_BOOT_COMPLETED that is broadcast after the system finishes booting.
I would like my android app service to run all the time.
that is -
1. right after installation,
2. on boot
3. if its closed - it will be relaunched -
how do i achieve all of the above code-wise?
thanks!
I am not putting the code here however you can easily find it.
Right after installation use the default activity to launch the service, in case you do not have any UI then create an activity without any UI (no setContentView) and in its onCreate start the service.
You need to create a broadcastReceived that listens to ACTION_BOOT_COMPLETED and call that as Service Manager. On Receiving the broadcast in that receiver just start the service again.
Make your service as foreground and that should ideally take care of this scenario.
You shouldn't use the Service foreground feature! The best practice in current android version is to return START_STICKY from your Service's onStartCommand(). It will cause the Android system to re-launch your service.
Regards.