Is there a potential issue using a Singleton Java class by both a Service and Activity in an Android application?
Example: Singleton is Singleton
Activity is A
Service S
Service S has a handle to Singleton. S requests Singleton to launch Activity. Activity calls operational methods defined in Singleton.
I have several Singletons in my application which encapsulate various operations for a functional area. So Activities will interact with various Singletons to perform various operations.
I don't think there is an issue with this approach. I use singletons quite often and never had any problems. You just need to keep in mind that android might remove the singleton instance when it needs the memory, so you need to check for null every time you get the singleton instance.
Usually it won't introduce the issue. The only case You could expect it - using service running in separate process (service has android:process attribute defined which starts with '.'). In that case You'll simply has two instances of the singleton - one for each process.
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 have a started service that handles a connection and that keep an array of objects. On the other hand, I have a singleton that should bind to that service in order to get one of the objects handled by the service. So, how could I bind the service from the singleton? Is it a good practice to bind the service when the singleton is initialized by using the application's context? Is there a better alternative?
Thanks in advance!
This is a perfectly good way to do it. Your singleton gets initialized and binds to the service using application context. The singleton will stay bound until the process hosting your singleton is killed by Android (or until you intentionally unbind). Be aware that if you intentionally unbind then you will need to intentionally bind again if your app starts again before Android has destroyed the hosting process (or you'll need to destroy your singleton so it gets reinitialized later).
In the case where Android kills your process and the user returns to the app, your singleton will get recreated and will rebind to the service.
Three questions concerning processes in Android. I am developing one app. If I declare a service to be running in another process in AndroidManifest.xml
<service android:name=".MyService" android:process=":MyProcess"/>
Does it mean there will be two JVM instances , one used by MyService while the other one used by other code?
If the answer of above question is YES, then does it also mean if I have a singleton class used by both Activity & MyService, then there will be two instances created for the singleton?
How to ensure only one instance be created & shared by two processes then? Better provide a sample :)
====UPDATE====
Thanks for all your comments and answer(s), unfortunately, my project needs to use separate service process as it has long running background tasks. According to your replies, I have a 4th question:
If I need to pass a non-parcelable object across MyService process and the other code's process. Is it possible? And how?
Does it mean there will be two JVM instances , one used by MyService while the other one used by other code?
yes (in a certain way, but not exactly, it's a dalvik VM, or in Lollipop ART), but yeah, you have two separate things running the service and rest of code.
If the answer of above question is YES, then does it also mean if I have a singleton class used by both Activity & MyService, then there
will be two instances created for the singleton?
yes
How to ensure only one instance be created & shared by two processes then? Better provide a sample :)
you can't! You just told the system to have separate processes. So it cannot have "the same" singleton.
An approach to that is have your service implement a binder or AIDL (in case you to direct calls methods) or implement a ContentProvider, which is the same across processes that you can read values out of it.
Or you can just make it all simpler and NOT use the process. 99.9% of use cases android:process is not advised. So re-evaluate your software. Do you really need it?
edit:
unfortunately, my project need to use separate service process as it
has long running background tasks
If your project need a long running background task, you need a Service for sure. But it doesn't mean it needs to be in a separate process. All the activities from your project can go to background and be garbage collected and your service still runs fine. It will keep running on the same process as the Activities were running but the Service and singletons will still be alive. Just remember to return START_STICKY from it.
If you still think you need to use it in a separate process (I don't think you need). Then you need to implement a bound service (or maybe AIDL, I'm not sure bound service will work across process), connect the activity to it and give the reference of the object using a normal method like public void takeThisObJect(Object reference);
links to bound and AIDL service guides:
http://developer.android.com/guide/components/bound-services.html
http://developer.android.com/guide/components/aidl.html
My Android app runs a service instance that is not accessible from other apps. I know that the service runs in the same process the app's Activity, because I can read and write to a static variable on the Service class from the activity and the Service sees the changes.
Communicating with the service via static variables/methods (or more properly singletons), is much, much simpler than communicating with it using a Handler or an Intent, which requires making all passed parameters Parcelable. It seems like these two communication methods are really designed for services running in a separate process, and are unnecessary overhead for an in-process service.
It seems like I must be missing something big. What is wrong with using a singleton to talk to a service if you know it is local to your app?
Communicating between a Service and an Activity is one of the main reasons to use a bound service: you can build a Binder class that defines the interface between your Service and Activity and pass any objects you want between them without having to worry about parcelling them (as binders require both to be on the same process).
I have a singleton which stores some prudent information about the user of my application. At the moment, it stores the user's login and the user's location.
1)
The location is found via a Service. At the moment, the Service references my singleton directly to stuff the longitude and latitude into it. I would like to use a BroadcastReceiver to send a broadcast that the singleton hears and uses to update the values, instead.
However, to register the BroadcastReceiver, I need a Context in my singleton. What is the slickest way to achieve what I'm wanting. Is BroadcastReceiver possibly not the appropriate object?
2)
Also, what problems am I looking at with using a singleton? I assume that Android will possibly reclaim this memory at any given time (which would obviously be bad); so how can I prevent that? Would passing in the application's Context and storing it in a member variable thwart this?
The Android documentation states: "But, the life cycle of a static is not well under your control; so to abide by the life-cycle model, the application class should initiate and tear down these static objects in the onCreate() and onTerminate() methods of the Application Class," but I'm not entirely sure how to accomplish this.
However, to register the BroadcastReceiver, I need a Context in my singleton. What is the
slickest way to achieve what I'm wanting. Is BroadcastReceiver possibly not the appropriate > object?
The "slickest way" is to not do what you are doing. Please only register a BroadcastReceiver from an Activity, Service, or maybe an Application. You must unregister this BroadcastReceiver when the Activity, Service, or Application is destroyed.
I assume that Android will possibly reclaim this memory at any given time (which would
obviously be bad); so how can I prevent that?
You don't. Android reserves the right to terminate your process at any time (e.g., to reclaim memory). Task killers on Android 2.1 and previous will terminate your process at any time. Once all components of your application are destroyed, Android may recycle your process at any time, clearing out your heap at the same time. And so on.
Only put things in memory that you don't mind losing.
It is best to think of your "application" as a basket of loosely-coupled components, not as a monolithic entity.
Would passing in the application's Context and storing it in a member variable thwart
this?
No.
The Android documentation states: "But, the life cycle of a static is not well under your
control; so to abide by the life-cycle model, the application class should initiate and
tear down these static objects in the onCreate() and onTerminate() methods of the
Application Class," but I'm not entirely sure how to accomplish this.
Create a subclass of Application and indicate in the manifest that Android should use it, via the android:name attribute on the <application> element.