From what I can tell, a Service can only be accessed from an Activity via an IBinder, Messenger or AIDL.
Why can a service instance's methods not be directly called by, for example, an activity?
For example, when the service is started, have the service save its instance in a singleton which would then be accessed by Activitys, allowing them to directly call the service's methods?
From what I can tell, a Service can only be accessed from an Activity via an IBinder, Messenger or AIDL.
That would depend upon what you define as "accessed". startService() can also be used to direct the service to perform some operation.
Why can a service instance's methods not be directly called by, for example, an activity?
Android's component model is designed around loose coupling and replaceable implementations, where the replacements may be local or remote.
Moreover, a service should only be running when it is actively delivering value to the user. As such, there will be plenty of times when the service is not yet running, and the activity would need to start or bind to the service anyway.
For example, an IntentService should only be interacted with via startService(), as the IntentService will only be running while there are commands (supplied via startService()) to be processed.
Finally, the primary role of a Service is as a marker to the OS, letting it know that the process is still doing work, even while it is in the background. A pattern of "let's stuff the Service into a singleton" suggests that you are using the service in cases where the object in question does not necessarily need to be a Service. Just because an object is a singleton does not mean that it needs to be a Service. Hence, you need to sit back and ensure you know exactly why you are using a Service in the first place and whether that is best for the user.
For example, when the service is started, have the service save its instance in a singleton which would then be accessed by Activitys, allowing them to directly call the service's methods?
There's nothing stopping you from doing this. I wouldn't -- I'll use startService() and event buses for communications. And, while it is technically possible to do what you want, whether it is sensible and reliable depends entirely upon the use case of the service and the communications with that service.
Related
I have a service, I need to communicate to with it (one service - many fragments/activities). There are two options for this:
Have a singleton that controls the service - starts it and then binds to it (using the app context)
Have a singleton that controls the service - starts it, the service when ready registers back as a delegate to the singleton (in a WeakReference)
Solution no 2 seems simpler to me, but whenever I read about communication with services there is the concept of the bound service.
Is there any benefit of having a bound service instead of the service registering itself as a delegate (and unregistering with onDestroy)?
Edit 1: The service is to keep the communication alive, it's expensive to set up a new communication channel. Even if no one requested any data it should keep the channel alive (heartbeat).
The service is foreground, it should run even if the activity that requested the data gets killed by the system. The next time it is created the data will be there.
The data requested by one screen might be useful for some other (therefore has to be stored in a singleton).
Bound and unbound services are both usable patterns and you should pick whatever pattern is better for you use case.
You should pick bound service if you want your service to have the same lifecycle as the components that bind to it. If you need an independent service use an unbound version.
The only benefit of one approach versus another is the simplicity of implementation.
In you case, I think you need the service only while there are running activities and fragments, then the easiest way, in my opinion, would be to make a bound service and make every activity bind to it. With that, you'll get a simple communication interface between you activities (and fragments, since they have access to containing activity) and your service.
The benefits of this approach are:
the service will stop itself if all activities unbind and start itself when first activity binds to it.
you won't need to track all running activities in the singleton and manually unbind
you won't need to maintain a singleton manager, less code -> less bugs
sometimes onDestroy can be skipped by the system and you can leak the service with the 2 approach.
Since you need your service to be running the correct option will be to use a started service and make each activity bind to it when needed. It's a common pattern.
Started service will run until you explicitly stop it or it stops itself, you can have a singleton manager that will be responsible for that.
But at the same time you can communicate with the service from your activity using binding.
So basically comparing with the first suggested approach, you'll need some instance that will start and stop the service, but the communication between activities and service will be the same - using binding.
Yes, using a bound service in Android is a much better option when communicating with Views like Activities/Fragments. This is because of the following reasons,
It runs synchronously.
You can have more control on the service data when to show on UI thread of the view. You can choose when to call it in async/sync way.
LocalBroadcastManager only runs Asynchronously.
I am curious to know why would you use Bound Service for 2 way interaction between Activity and Service when you can do the same kind of interaction with Started Service using local broadcasts and receivers defined in both Activity and Service
It will be helpful to know the pros and cons of each implementation.
I couldn't find any clear answer to this anywhere.
Using a bound Service is much more flexible. You can define methods on the Service (using AIDL) that return immediate results (synchronously), which you cannot do using LocalBroadcastManager. Using LocalBroadcastManager requires that you use your Service in a completely asynchronous way. This decouples the initiation of a service action with the return of the results (callback) which can make your code more complicated and more difficult to understand. Asynchronous use has some benefits, and there are places where you should use it, but if you have a bound Service you can choose exactly when to use synchronous calls and when to use asynchronous callbacks.
Also, the use of AIDL allows you to exactly describe the signatures of the services's method calls. If you use startService(Intent), you cannot guarantee that the caller will provide the correct arguments in the passed Intent, so you need to rely on the caller to "do the right thing" and/or you need to add a lot of additional argument verification.
Don't forget the comment from #CommonsWare about how LocalBroadcastManager only works if the Service is running in the same OS process as the rest of your app (which makes it unsuitable for programming things like system services, which are not running in your OS process).
A service is a component that runs in the background. It works to perform long-running operations without needing to interact with the user For example, a service might retrieve data over the network without stopping or blocking user interaction with an activity of an app or user may play music in the background while the user is in a different application. A service can be of two types or states:
Started: Once started, a service can run in the background indefinitely, even if the component which is started, is destroyed. A service is started when an app module or component, such as an activity, starts it by calling startService(). For example, retrieving contacts from phone book using service when Splash Screen starts.
Bound: A bound service offers a client-server interface that allows components to interact with the service, get results, send requests and even do so across processes with interprocess communication (IPC). A bound service is bound, when an app component binds to it by calling bindService().
I am trying to dive deep into service architecture.
Just for testing purpose I am creating music app.
Without doubts music should be played in service, but what kind of communication to use ?
As far as I know service can be bound or started (or both at the same time)
Firstly, I need to play/stop/pause/set source...
Secondly, I need to notify UI if music player is opened about events like progress, buffering...
Here my thoughts about this.
I think about mixing bound and started service.
As far as song can be set only from UI as well as paused/played/stopped/seek I choose communication with service from UI using binder. (Bound service inside activity and get binder back to communicate with service)
Notifications like track completed, next track , current track position coming from the service. I decided to use Broadcast receiver to send such notifications, because it can be multiple interested components.
So my question are
What is the best choice of communication (bound or broadcast) for music player service ?
How does System knows what kind of service is ? I mean that even if the service is bound I need to call startService method at first and than bind it. Does it mean that if even one bindService method was called it is considered as bound service and it would be killed when unbind method is called ?
Does LocalBroadcastManager make sense in communication with service ? As far as LocalBroadcastManager is local for each app, why not to use bound service than ? Global broadcast makes sense in case other apps are interested in events.
Please help to understand this mechanism.
Best choice of communication is using both LocalBroadcasts as well as Binder methods as per your requirement. If you want to do something in service from your bound component like activity then you should use binders. If you need to send result back to application you should use LocalBroadcast.
There is one more option available to use messaging. In this case both activity and service use same ibinder hence two way communication is easy. You can send message from activity to service and service to your activity
In this point you are wrong about starting bound service. You don't need to call startservice in this case. Just a call of bindservice method is required.
bindService(new Intent(this, MessengerService.class), mConnection,
Context.BIND_AUTO_CREATE);
you can bind multiple components to this service. When all of them are unbound then service will be stopped.
Yes LocalBroadcastmanagers make sense. Lets say you want to broadcast something in your app. There are 4-5 components that you want to update.How will you achieve this. Hence the use of localbroadcasts is a good feature.
For example lets say there are two services starting from different activities and second service starts its work when first service has done its work. So, only way to achieve this is send a broadcast and second service will be registering for this broadcast. Hence it will receive it.
Yeah. Services are pretty hard to understand.
There are some things that are easy. A bound service is always started with a bindService method. A started service is always started with a startService method. You do not need to start a bound service, or bind a started service.
A lot of what follows is gross generalization...
Started Service
A started service is nearly useless, unless it is an IntentService. IntentServices are pretty good tools for running asynchronous tasks. You send them a small bundle of parameters and they go off and do whatever those parameters indicate. They are like void methods in that one expects to use their side-effects, not a return value.
Bound Service
A bound service is harder to explain. Although the metaphor breaks down on careful examination, a bound service something like a singleton factory. It is, for instance, a way of providing a single object, with is single state, to all of the Activities in a application. Among its interesting features is that, as long as the service that provided the singleton object is bound, the hosting process is less likely to be killed off. Note that, the "singleton" object that the bound service provides has little to do with the service that provides it. Unbinding the service does not invalidate it.
Bound services are also the main means of doing inter-process communication, in Android.
What should you do?
Well, that's a pretty general question. Here's a thought. Putting your music player in a service makes a lot of sense. If the communication to it is mostly one way -- commands to the service -- there is a chance that you can do it with an IntentService. Until there's a specific reason to do something more complex, an IntentService has the advantage of being simpler.
I am developing an Android data entry app that saves the entered data to a file. A Service (let's call it FileIOService) is launched using the filename, and loads and saves data that is passed to it from each Activity that the user accesses.
I am trying to make the whole app as robust as possible, and at the moment I feel I need to pay particular attention to the interaction between each Activity and the Service. Here are the issues I can see:
If the Service is killed by the system, it needs to restart and open the file that it had open: I can handle this by using START_REDELIVER_INTENT.
If an Activity is destroyed, for instance by an orientation change, it needs to reconnect to the Service.
The thing is, once the Activity launches the Service, there's a while before the Service finishes opening the file and becomes ready for I/O requests. To address this, in my Activity, I have both:
an inner class subclassing ServiceConnection, with its onServiceConnected() method completed
a private reference to an anonymous inner subclass of BroadcastReceiver, with its handleMessage() method completed. This gets called when the Service sends out a broadcast to indicate it's finished opening its file.
Both of these methods then call a setUpActivity() method that pulls data from the Service. This is where it starts to get ugly. Because onServiceConnected() may run before the file is ready for I/O, and handleMessage() might be called while the Service is not bound to the Activity, I have to make both handleMessage() and onServiceConnected() set boolean flags that can later be checked in setUpActivity(), like this:
if ((fileLoaded && serviceConnected))
{
//access the file data
}
As I said, this feels ugly and awkward, and relies on setting extra boolean variables.
There's another problem - if my Activity launches an external Activity, like the Camera app, upon returning to my app the Service and Activity may both have been destroyed (especially with an orientation change) and the app crashes.
My feeling is that I may be missing some overall pattern that would define how each Activity should relate to the Service, and vice versa, while remaining robust and able to cope with unexpected terminations/restarts.
Let's ignore the fact that I am skeptical that this is a valid use case for a service (a service whose existence is simply to read and write files?).
If the Service is killed by the system, it needs to restart and open the file that it had open: I can handle this by using START_REDELIVER_INTENT.
The service is not "killed by the system". The process is killed by the system. This will eradicate your activities as well as your service.
The one possible exception to this is if the user manually stops the service (and only the service) from Settings, in which case I have no clue what the expected behavior would be. This should be fairly uncommon nowadays, particularly for an app that the user had just been using. Users will be more inclined to use a task manager, such as swiping your app off the recent-tasks list, which will get rid of the entire process, not just the service.
If an Activity is destroyed, for instance by an orientation change, it needs to reconnect to the Service.
Not necessarily:
Bind using the Application context (getApplicationContext()) instead of from the Activity directly
Use a retained fragment to maintain the binding across the configuration change
My feeling is that I may be missing some overall pattern that would define how each Activity should relate to the Service, and vice versa, while remaining robust and able to cope with unexpected terminations/restarts.
This is one of the many reasons why I try to avoid the binding pattern altogether. Use a service for processing commands, sent via startService(), with results (if any) delivered by LocalBroadcastManager, or Otto, or greenrobot's EventBus, or a "real" broadcast Intent, or maybe a Messenger. Particularly when the service is an IntentService, the service nicely cleans up after itself when there is no more work to be done.
I want to use a started (foreground) service to manage a network connection that should persist when the user leaves the application for a short time, and that the user should be aware of (so he can return to the app and maybe disconnect). This service will only ever be used locally by activities in the same process.
Maybe it's just because I am new to Android, but I find it unnecessarily difficult to bind to this service in every activity that uses it - in particular, the asynchronous nature of binding, which only really seems to be necessary for accessing services in a different process. Is there any indication against just accessing the started service through a static variable instead?
Maybe I'm understanding your question wrong, but there is no need to bind to the started Service from every Activity. Instead, you could simply start the Service from wherever you need to interact with it. This calls the onStartCommand() if the Service is already started. You could include an extra with the Intent that starts the Service to distinguish between the first start and subsequent ones.
Of course - this addresses the use case where you do not want to have a client-server mode of interaction between your activities and the Service - that scenario requires binding and if you really need binding, then you need to bind from every component that needs to be served by the Service.