隨筆 - 19, 文章 - 1, 評論 - 21, 引用 - 0
          數(shù)據(jù)加載中……

          OSGi中獲取Service的幾種方式

          在OSGi中,Service是動態(tài)管理的,OSGi容器提供的好幾種獲取和使用Service的方式,那么這幾種方式各有什么優(yōu)、缺點呢,下面我們就以org.osgi.service.log.LogService為例來分別講一講。


          一。最原始的方式:
           1         // 獲取Service引用
           2         ServiceReference ref = context.getServiceReference(LogService.class.getName());
           3         if (ref != null) {
           4             // 獲取Service實例
           5             LogService service = (LogService) context.getService(ref);
           6             if (service != null) {
           7                 // 調用Service方法
           8                 service.log(LogService.LOG_INFO, "ok");
           9                 // 釋放Service,在此之后不應該再繼續(xù)使用Service實例
          10                 context.ungetService(ref);
          11             }
          12         }
          優(yōu)點:很難說有什么優(yōu)點,硬要說幾句的話,那就是邏輯夠簡單,調用最少,適合一次性操作。
          缺點:需要判斷返回值是否為null,需要手動申請和釋放service,由于OSGi的動態(tài)性,請在獲取ref后盡快使用,無法保證ref長期有效。每次訪問都會有service獲取和釋放的開銷。
          用途:適合于不頻繁的調用service,且在service不可用時也能繼續(xù)執(zhí)行后續(xù)操作的場景。

          二。使用ServiceListener:
          在Service注冊時訪問:
           1         context.addServiceListener(new ServiceListener() {
           2             public void serviceChanged(ServiceEvent event) {
           3                 switch (event.getType()) {
           4                 case ServiceEvent.REGISTERED:
           5                     // 獲取Service引用
           6                     ServiceReference ref = event.getServiceReference();
           7                     // 獲取Service實例
           8                     LogService service = (LogService) context.getService(ref);
           9                     if (service != null) {
          10                         // 調用Service方法
          11                         service.log(LogService.LOG_INFO, "ok");
          12                         // 釋放Service,在此之后不應該再繼續(xù)使用Service實例
          13                         context.ungetService(ref);
          14                     }
          15                     break;
          16                 case ServiceEvent.UNREGISTERING:
          17 
          18                     break;
          19                 }
          20 
          21             }
          22         }, "(objectclass=org.osgi.service.log.LogService)");
          獨立于ServiceListener的訪問:類似于方式一,在Listener中獲取service并且保存到成員變量中,以供后續(xù)訪問:
           1         context.addServiceListener(new ServiceListener() {
           2             public void serviceChanged(ServiceEvent event) {
           3                 switch (event.getType()) {
           4                 case ServiceEvent.REGISTERED:
           5                     if (ref == null) {
           6                         ref = event.getServiceReference();
           7                         service = (LogService) context.getService(ref);//保存實例以備后續(xù)訪問
           8                     }
           9                     break;
          10                 case ServiceEvent.UNREGISTERING:
          11                     if (ref == event.getServiceReference()) {
          12                         context.ungetService(ref);//釋放實例
          13                         service = null;
          14                         ref = null;
          15                     }
          16                     break;
          17                 }
          18 
          19             }
          20         }, "(objectclass=org.osgi.service.log.LogService)");
          訪問Service:
          1         if (service != null) service.log(LogService.LOG_INFO, "ok");
          優(yōu)點:只在Service變更時產生一次service獲取開銷,動態(tài)感知service的注冊和注銷。
          缺點:在ServiceListener注冊之前已經存在的Service無法監(jiān)聽到。需要自己維護service的獲取和釋放。在需要監(jiān)聽多個Service實例時,使用并不方便。

          三、使用ServiceTracker
          ServiceTracker其實是對ServiceListener實現(xiàn)方式的封裝,使得對service的獲取更加簡潔,同時也解決了不能監(jiān)聽到已經存在的Service的問題(其實就是在增加ServiceListener的同時調用BundleContext.getAllServiceReferences方法以獲取現(xiàn)有的Service引用)。
          使用ServiceTracker使得獲取Service的代碼更加簡潔和一致,不必再考慮Service是否存在的問題,并且ServiceTracker也提供了更加有效的監(jiān)聽Service的方式。
          一次性訪問:
          1         ServiceTracker tracker = new ServiceTracker(context, LogService.class.getName(), null);
          2         tracker.open();
          3         LogService service = (LogService) tracker.getService();
          4         if (service != null) service.log(LogService.LOG_INFO, "ok");
          5         // 獲取多個Service
          6         Object[] services = tracker.getServices();
          7         // 獲取Service的數(shù)量
          8         int count = tracker.getTrackingCount();
          9         tracker.close();
          在Service注冊和注銷時訪問:
           1         ServiceTracker tracker = new ServiceTracker(context, LogService.class.getName(), null) {
           2             @Override
           3             public Object addingService(ServiceReference reference) {
           4                 LogService service = (LogService) super.addingService(reference);
           5                 if (service != null) service.log(LogService.LOG_INFO, "ok");
           6                 return service;
           7             }
           8 
           9             @Override
          10             public void removedService(ServiceReference reference, Object service) {
          11                 ((LogService) service).log(LogService.LOG_INFO, "removedService");
          12                 super.removedService(reference, service);
          13             }
          14         };
          15         tracker.open();
          16 
          17         // 在自身lifecycle結束時關閉tracker
          18         tracker.close();
          有一點需要注意的是,tracker需要調用open方法才能監(jiān)聽到Service,另外,在bundle stop以后,bundle內open的ServiceTracker不會自動關閉,所以一定不要忘記在bundle結束之前,關閉所有在bundle中open的ServiceTracker。

          四、使用Declarative Services
          在OSGi 4以后的規(guī)范中,增加了Declarative Services方式。Declarative Services 是一個面向服務的組件模型,它制訂的目的是更方便地在 OSGi 服務平臺上發(fā)布、查找、綁定服務,對服務進行動態(tài)管理,如監(jiān)控服務狀態(tài)以及解決服務之間的復雜的依賴關系等問題。Declarative Services 采用服務組件的延遲加載以及組件生命周期管理的方式來控制對于內存的占用以及啟動的快速,很好的解決了傳統(tǒng)的 OSGi 服務模型在開發(fā)和部署比較復雜應用時內存占用大、啟動慢等問題,并且對服務組件的描述采用XML來實現(xiàn),十分便于用戶理解和使用。
          在equinox-SDK-3.6M5開發(fā)包中,包含了一個DS的實現(xiàn):org.eclipse.equinox.ds_1.2.0.v20100125.jar,將這個jar和一個依賴的jar:org.eclipse.equinox.util_1.0.100.v20090520-1800.jar部署到OSGi容器中,就可以使用DS服務了。equinox中DS服務的實現(xiàn),是綜合使用了BundleListener,ServiceListener等相關OSGi API,將大量繁雜和冗長的代碼細節(jié)藏在了實現(xiàn)背后,開發(fā)者只需要了解簡單的xml語法和配置方式即可方便的使用。
          要使用DS,一般有以下幾個步驟:
          1.定義Component實現(xiàn)類:
           1 package org.dbstar.osgi.dstest;
           2 
           3 import org.osgi.service.component.ComponentContext;
           4 import org.osgi.service.log.LogService;
           5 
           6 public class TestComponent {
           7     public void activate(ComponentContext context) {
           8         System.out.println("activate(" + context + ")");
           9     }
          10 
          11     public void deactivate(ComponentContext context) {
          12         System.out.println("deactivate(" + context + ")");
          13     }
          14 
          15     public void modified(ComponentContext context) {
          16         System.out.println("modified(" + context + ")");
          17     }
          18 
          19     public void bind(LogService service) {
          20         service.log(LogService.LOG_INFO, "bind");
          21     }
          22 
          23     public void unbind(LogService service) {
          24         service.log(LogService.LOG_INFO, "unbind");
          25     }
          26 }
          2.編寫component.xml:
          1 <?xml version="1.0" encoding="UTF-8"?>
          2 <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0"
          3     activate="activate" deactivate="deactivate" modified="modified" name="test"
          4     xsi:schemaLocation="http://www.osgi.org/xmlns/scr/v1.1.0 http://www.osgi.org/xmlns/scr/v1.1.0">
          5     <implementation class="org.dbstar.osgi.dstest.TestComponent" />
          6     <reference bind="bind" cardinality="1..1"
          7         interface="org.osgi.service.log.LogService" name="LogService"
          8         policy="dynamic" unbind="unbind" />
          9 </scr:component>
          以上是有namespace的xml寫法,在equinox中也支持沒有namespace的寫法,Eclipse中有相應的插件來提供圖形化的界面來維護component xml。以下是沒有namespace的xml寫法:
          1 <?xml version="1.0" encoding="UTF-8"?>
          2 <component name="test">
          5     <implementation class="org.dbstar.osgi.dstest.TestComponent" />
          6     <reference bind="bind" cardinality="1..1"
          7         interface="org.osgi.service.log.LogService" name="LogService"
          8         policy="dynamic" unbind="unbind" />
          9 </component>
          3.將寫好的xml放置到bundle根目錄下的OSGI-INF下面
          4.在bundle的描述文件META-INF/MANIFEST.MF中增加component相關的header:
          1 Service-Component: OSGI-INF/component.xml
          注意xml的文件名不是絕對的,放置的目錄也不是絕對的,只要在Service-Component中包含正確的路徑就可以了。
          一個bundle可以注冊多個component,只要編寫多個xml文件即可,在Service-Component中以逗號分隔。
          Component的注冊并不依賴Activator,所以bundle的Activator不是必須的。
          另外在我的使用過程中,發(fā)現(xiàn)一個問題,如果xml中沒有使用namespace,那么component節(jié)點上的幾個callback類屬性都不能定義,例如activate屬性。如果使用了namespace,那么這些屬性都是可以正常使用的,不知道這算不算是bug。
          關于DS規(guī)范的詳細內容,可以參見:OSGi 中的 Declarative Services 規(guī)范簡介

          最后總結一下,綜上所述的四種獲取service的方法,使得service的獲取越來越簡單,開發(fā)者只需關注自己的邏輯,而不必糾纏于OSGi繁瑣的Service Lookup中去,同時還提供了更加方便使用的API,大家可以根據(jù)自己的需要,選擇最合適的使用方式。

          posted on 2010-03-26 18:09 dbstar 閱讀(13304) 評論(0)  編輯  收藏 所屬分類: OSGi

          主站蜘蛛池模板: 景泰县| 正蓝旗| 汝南县| 临颍县| 成武县| 滨州市| 新竹市| 建德市| 安阳市| 老河口市| 迁安市| 长兴县| 北安市| 湖南省| 炎陵县| 镇平县| 泗洪县| 苏尼特左旗| 额尔古纳市| 固安县| 江安县| 景德镇市| 宿松县| 湘西| 洪雅县| 梁山县| 敦化市| 乌什县| 金堂县| 萝北县| 会同县| 综艺| 南靖县| 凌云县| 富裕县| 郎溪县| 垦利县| 钟祥市| 大埔县| 蒲江县| 连州市|