What is the best practice for using an OSGi ServiceTracker
in a BundleActivator
? I have seen examples on the web that open a tracker in the start
method, store it in an instance variable, and close it in the stop
method, keeping it open for the life of the bundle. Is this appropriate? I have been writing code that opens the tracker whenever I need it, gets the service and uses it, then closes the tracker. Of course, this requires storing the BundleContext
itself in an instance variable if I want to use it at a later time. Since I have to store one or the other, it may not make much difference which I store.
In a related question, if I do open the tracker each time I need it, is it necessary to keep it open while I am using the service that I got from it, or can I open the tracker, get the service, close the tracker, then use the service? I see no reason that I could not close the tracker before I use the service.
Services come and go – your responsibility as an OSGi developer is to use services only when they have been published by their bundle, and more importantly to release them when they are unpublished. If you continue using a service after it has been unpublished, you risk unpredictable errors occurring. At the very least you will cause the heap space associated with that service instance to be pinned in memory, which undermines OSGi's ability to dynamically install and uninstall bundles.
So, you ask whether you should close the ServiceTracker before using the service: my answer is NO. ServiceTracker acts as a "smart pointer" to the current service, and it manages all the listeners etc in order to be notified when the service goes away. If you close the tracker, then you are no longer kept up to date with the state of the service, so how do you know if it is still valid?
The ideal pattern for using a ServiceTracker -- assuming that the tracker remains open throughout -- is as follows:
{
Service svc = tracker.getService();
svc.doSomething();
}
// 'svc' is now forgotten, and may be garbage collected
That is, when you call getService()
on the tracker you obtain an instance of the actual service, but you should use it quickly and then forget it as soon as you can. You absolutely must NOT store the result of getService()
in a field and hold on to it for a long time.
As for whether you should open and immediately close the tracker only when needed – NO, there is absolutely no need to do this. An open tracker does not consume any significant resources, it simply means it is registered as a listener so that it knows when services come and go. In fact it is inefficient to repeatedly open and close the tracker since each time you open it, it has to synch itself against the current state of the service registry. This is why the pattern you have seen in examples is usually to open the tracker during bundle activation and keep it open... it's the best pattern.
Incidentally there is also no need to explicitly close a tracker in your BundleActivator.stop
method. All resources associated with your tracker will be automatically cleaned up when your bundle stops. The only reason to explicitly close is if you have several trackers and/or service registrations, and you want to control the order in which you clean up.
Now having said all of the above, I'm going to chuck in a hand grenade: PLEASE STOP USING ServiceTracker
! It is a very low-level utility, that is only very rarely required, and then only in "plumbing" or infrastructural code. Most of your application should be built with a higher level of abstraction. I strongly recommend using Declarative Services, which is much, much easier than messing around with ServiceTrackers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With