OSGI中ServiceTracker的应用
前文讲了ServiceListener,ServiceListener用于监听各种服务,但是如果要监听某一种服务的话,可能要花费一番功夫才能完成,但是我们可以使用osgi中另外一个监听服务的方式,就是ServiceTracker,这个在本质上是对ServiceListener的一种封装,对service的获取比ServiceListener比起来又简单许多,并且可以解决不能监听到已经存在的service的问题,先简单的看一下ServiceTracker的源码,然后在调试过程中再对具体进行讲解,部分源码如下:
/**
* Create a {@code ServiceTracker} on the specified class name.
*
* <p>
* Services registered under the specified class name will be tracked by
* this {@code ServiceTracker}.
*
* @param context The {@code BundleContext} against which the tracking is
* done.
* @param clazz The class name of the services to be tracked.
* @param customizer The customizer object to call when services are added,
* modified, or removed in this {@code ServiceTracker}. If customizer
* is {@code null}, then this {@code ServiceTracker} will be used as
* the {@code ServiceTrackerCustomizer} and this
* {@code ServiceTracker} will call the
* {@code ServiceTrackerCustomizer} methods on itself.
*/
public ServiceTracker(final BundleContext context, final String clazz, final ServiceTrackerCustomizer<S, T> customizer) {
this.context = context;
this.trackReference = null;
this.trackClass = clazz;
this.customizer = (customizer == null) ? this : customizer;
// we call clazz.toString to verify clazz is non-null!
this.listenerFilter = "(" + Constants.OBJECTCLASS + "=" + clazz.toString() + ")";
try {
this.filter = context.createFilter(listenerFilter);
} catch (InvalidSyntaxException e) {
/*
* we could only get this exception if the clazz argument was
* malformed
*/
IllegalArgumentException iae = new IllegalArgumentException("unexpected InvalidSyntaxException: " + e.getMessage());
iae.initCause(e);
throw iae;
}
}
在上述源码中可以看到在ServiceTracker的构造方法中,这一行代码确定了监听那个服务:
this.listenerFilter = "(" + Constants.OBJECTCLASS + "=" + clazz.toString() + ")";
下一行则创建了相应的监听器,并对服务进行了监听,如下:
this.filter = context.createFilter(listenerFilter);
在我们进行相应服务的使用的时候,在进行使用ServiceTracker的时候,这个时候就对相应的服务进行了监听,在稍后演示过程中再进行程序的调试的时候,再来对具体进行分析。
ServiceTracker源码
HelloService:
package cn.com.example;
/**
* Created by xiaxuan on 16/7/12.
*/
public interface HelloService {
String hello(String name);
}
HelloServiceImpl:
package cn.com.example;
/**
* Created by xiaxuan on 16/7/12.
*/
public class HelloServiceImpl implements HelloService {
public String hello(String name) {
return "hello " + name;
}
}
HelloServiceTracker:
package cn.com.example;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;
/**
* Created by xiaxuan on 16/7/12.
*/
public class HelloServiceTracker extends ServiceTracker {
public HelloServiceTracker(BundleContext context) {
super(context, HelloService.class, null);
}
@Override
public Object addingService(ServiceReference reference) {
System.out.println("adding service: " + reference.getBundle().getSymbolicName());
return super.addingService(reference);
}
@Override
public void removedService(ServiceReference reference, Object service) {
System.out.println("removeing service: " + reference.getBundle().getSymbolicName());
super.removedService(reference, service);
}
}
Activator:
package cn.com.example;
import cn.com.ds.Test;
import cn.com.ds.TestComponent;
import org.osgi.framework.*;
/**
* Created by Administrator on 2016/6/18.
*/
public class Activator implements BundleActivator {
HelloServiceTracker serviceTracker;
ServiceRegistration serviceRegistration;
public void start(BundleContext bundleContext) throws Exception {
System.out.println("service registered...");
//开启服务跟踪器
serviceTracker = new HelloServiceTracker(bundleContext);
serviceTracker.open();
//注册服务
serviceRegistration = bundleContext.registerService(HelloService.class, new HelloServiceImpl(), null);
//获取b被跟踪的服务
HelloService helloService = (HelloService) serviceTracker.getService();
if (helloService != null) {
System.out.println(helloService.hello("xiaxuan"));
}
ServiceReference serviceReference = bundleContext.getServiceReference(Test.class);
Test testComponent = (Test) bundleContext.getService(serviceReference);
System.out.println(testComponent.hello("bingwen"));
}
public void stop(BundleContext bundleContext) throws Exception {
System.out.println("Stopping service");
//关闭服务跟踪器
if (serviceTracker != null) {
serviceTracker.close();
}
//注销服务
serviceRegistration.unregister();
}
}
演示
本次的演示还是和之前的一样,先启动bundle,然后再stop bundle后,再启动Bundle进入断点进行调试,查看断点时相关变量的状况,首先启动bundle,在karaf控制台中观察如下:
id为10的bundle,example启动成功,dankaraf控制台中,可以看到还有id为18的一个Bundle在命名风格上与example差不多,这个将会是在后续中将要讲到的,到时候将会在ds中提供ds服务和在Bundle之间进行event通信,这个在这里暂且提一下,后续两篇博文将会讲到这些。
现在停止这个bundle,然后再次start,进入断点查看我们在ServiceTracker中大的断点的情况,如下所示:
此时我将断点打到ServiceTracker中的构造方法中的listenerFilter的初始化处,此时观察listenerFilter监听的class,如下图:
可以看到clazz就是我们先前传入的LogService,在随后创建的filter中,将会对这个filter进行监听,在程序中的ServiceTracker的对监听方法的获取中,如下图所示:
获取服务的时候先从缓存中获取,然后cacheService为null,这里应该是在第一次中获取服务之后,边将对应service的引用存储在缓存中,随后在其后使用相应service的时候直接从cache中获取就可以了,没有从cache中拿到的时候,从getServiceReference()方法中获取,这个方法就不再讲解了,比较简单,就是首先cache中获取,如果获取到然后得到所有的ServiceReference后遍历,得到所需。
总结
- 使用ServiceTracker使得获取Service的代码更加简洁和一致,不必再考虑Service是否存在的问题,并且ServiceTracker也提供了更加有效的监听Service的方式。
- 有一点需要注意的是,tracker需要调用open方法才能监听到Service,另外,在bundle stop以后,bundle内open的ServiceTracker不会自动关闭,所以一定不要忘记在bundle结束之前,关闭所有在bundle中open的ServiceTracker。
- 下一篇文章将开始讲解Declarative Services,即所谓的声明式服务。