<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

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

    OSGi中獲取Service的幾種方式

    在OSGi中,Service是動態管理的,OSGi容器提供的好幾種獲取和使用Service的方式,那么這幾種方式各有什么優、缺點呢,下面我們就以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,在此之后不應該再繼續使用Service實例
    10                 context.ungetService(ref);
    11             }
    12         }
    優點:很難說有什么優點,硬要說幾句的話,那就是邏輯夠簡單,調用最少,適合一次性操作。
    缺點:需要判斷返回值是否為null,需要手動申請和釋放service,由于OSGi的動態性,請在獲取ref后盡快使用,無法保證ref長期有效。每次訪問都會有service獲取和釋放的開銷。
    用途:適合于不頻繁的調用service,且在service不可用時也能繼續執行后續操作的場景。

    二。使用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,在此之后不應該再繼續使用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并且保存到成員變量中,以供后續訪問:
     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);//保存實例以備后續訪問
     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");
    優點:只在Service變更時產生一次service獲取開銷,動態感知service的注冊和注銷。
    缺點:在ServiceListener注冊之前已經存在的Service無法監聽到。需要自己維護service的獲取和釋放。在需要監聽多個Service實例時,使用并不方便。

    三、使用ServiceTracker
    ServiceTracker其實是對ServiceListener實現方式的封裝,使得對service的獲取更加簡潔,同時也解決了不能監聽到已經存在的Service的問題(其實就是在增加ServiceListener的同時調用BundleContext.getAllServiceReferences方法以獲取現有的Service引用)。
    使用ServiceTracker使得獲取Service的代碼更加簡潔和一致,不必再考慮Service是否存在的問題,并且ServiceTracker也提供了更加有效的監聽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的數量
    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方法才能監聽到Service,另外,在bundle stop以后,bundle內open的ServiceTracker不會自動關閉,所以一定不要忘記在bundle結束之前,關閉所有在bundle中open的ServiceTracker。

    四、使用Declarative Services
    在OSGi 4以后的規范中,增加了Declarative Services方式。Declarative Services 是一個面向服務的組件模型,它制訂的目的是更方便地在 OSGi 服務平臺上發布、查找、綁定服務,對服務進行動態管理,如監控服務狀態以及解決服務之間的復雜的依賴關系等問題。Declarative Services 采用服務組件的延遲加載以及組件生命周期管理的方式來控制對于內存的占用以及啟動的快速,很好的解決了傳統的 OSGi 服務模型在開發和部署比較復雜應用時內存占用大、啟動慢等問題,并且對服務組件的描述采用XML來實現,十分便于用戶理解和使用。
    在equinox-SDK-3.6M5開發包中,包含了一個DS的實現: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服務的實現,是綜合使用了BundleListener,ServiceListener等相關OSGi API,將大量繁雜和冗長的代碼細節藏在了實現背后,開發者只需要了解簡單的xml語法和配置方式即可方便的使用。
    要使用DS,一般有以下幾個步驟:
    1.定義Component實現類:
     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不是必須的。
    另外在我的使用過程中,發現一個問題,如果xml中沒有使用namespace,那么component節點上的幾個callback類屬性都不能定義,例如activate屬性。如果使用了namespace,那么這些屬性都是可以正常使用的,不知道這算不算是bug。
    關于DS規范的詳細內容,可以參見:OSGi 中的 Declarative Services 規范簡介

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

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

    主站蜘蛛池模板: 老司机亚洲精品影院| 亚洲精品无码久久一线| 亚洲xxxxxx| 成人免费福利视频| 亚洲最大黄色网站| 无限动漫网在线观看免费| 亚洲最大的视频网站| 美女视频黄是免费的网址| 亚洲性色高清完整版在线观看| 欧洲一级毛片免费| 国产亚洲国产bv网站在线| 在线看片人成视频免费无遮挡| 亚洲色偷精品一区二区三区| 在线免费观看国产视频| 九九九国产精品成人免费视频| 亚洲精品国产精品乱码不卡√| 日本一卡精品视频免费| 亚洲免费在线视频播放| 国产精品自在自线免费观看| 一级黄色免费大片| 亚洲人成影院在线| 女人被男人躁的女爽免费视频| 免费人妻精品一区二区三区| 亚洲大成色www永久网站| www.免费在线观看| 蜜臀亚洲AV无码精品国产午夜.| 亚洲欧洲中文日韩av乱码| a级毛片无码免费真人久久| 亚洲国产成人资源在线软件| 免费的一级片网站| 成人免费无码H在线观看不卡| 777亚洲精品乱码久久久久久 | 久久久久亚洲AV成人网人人网站| 国内永久免费crm系统z在线| 亚洲免费电影网站| 亚洲一级特黄无码片| 免费福利视频导航| 一个人免费观看视频在线中文| 亚洲综合久久久久久中文字幕| 免费成人黄色大片| 亚洲免费视频播放|