Chasing an mobile web vision

          闖蕩在移動互聯(lián)網(wǎng)的世界中

          2006年2月14日 #

          移動互聯(lián)網(wǎng)時代--Android上的一個例子

          我們來演示一個獲取聯(lián)系人,并用網(wǎng)頁展現(xiàn)出來的簡單例子。

           首先,我們在eclipse環(huán)境中創(chuàng)建一個Android project,我們的Activity名稱是com.example.RIAExample,并且修改界面的layout文件如下:

          <?xml version="1.0" encoding="utf-8"?>
          <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation
          ="vertical"
              android:layout_width
          ="fill_parent"
              android:layout_height
          ="fill_parent"
              
          >
          <WebView android:id="@+id/web"
           android:layout_width
          ="fill_parent" android:layout_height="fill_parent">
          </WebView>
          </LinearLayout>

          可以看到,界面中僅僅包含一個WebView控件。

           接下來,創(chuàng)建一個簡單的java類來描述一個聯(lián)系人的信息,它包含聯(lián)系人姓名和號碼。

           

          package com.example;

          import java.util.Vector;

          import android.app.Activity;
          import android.os.Bundle;
          import android.webkit.WebView;

          public class RIAExample extends Activity {
              
              
          private WebView web;
              
              
          //模擬號碼簿
              private Vector<Person> phonebook = new Vector<Person>();
              
          /** Called when the activity is first created. */
              @Override
              
          public void onCreate(Bundle savedInstanceState) {
                  
          super.onCreate(savedInstanceState);
                  setContentView(R.layout.main);
                  
          this.initContacts();
                  web 
          = (WebView)this.findViewById(R.id.web);
                  web.getSettings().setJavaScriptEnabled(
          true);//開啟javascript設(shè)置,否則WebView不執(zhí)行js腳本
                  web.addJavascriptInterface(this"phonebook");//把RIAExample的一個實例添加到j(luò)s的全局對象window中,
                                                                  
          //這樣就可以使用window.phonebook來調(diào)用它的方法
                  web.loadUrl("file:///android_asset/phonebook.html");//加載網(wǎng)頁
               
              }

              
              
          /**
               * 該方法將在js腳本中,通過window.phonebook.getContacts()進(jìn)行調(diào)用
               * 返回的JavaArrayJSWrapper對象可以使得在js中訪問Java數(shù)組
               * 
          @return
               
          */

              
          public JavaArrayJSWrapper getContacts(){
                  System.out.println(
          "fetching contacts data");
                  Person[] a 
          = new Person[this.phonebook.size()];
                  a 
          = this.phonebook.toArray(a);
                  
          return new JavaArrayJSWrapper(a);
              
              }

              
              
          /**
               * 初始化電話號碼簿
               
          */

              
          public void initContacts(){
                  Person p 
          = new Person();
                  p.name 
          = "Perter";
                  p.phone_number 
          = "8888888";
                  phonebook.add(p);
                  p 
          = new Person();
                  p.name 
          = "Jack";
                  p.phone_number 
          = "777777";
                  phonebook.add(p);
                 
              }

              
              
          /**
               * 通過window.phonebook.debugout來輸出js調(diào)試信息。
               * 
          @param info
               
          */

              
          public void debugout(String info){
                  
                  System.out.println(info);
              }

          }

          下面是html文件,它非常簡單。
          <html>
              
          <head>
                  
          <script type="text/javascript" src="fetchcontacts.js"/>
          </head>
          <body>
              
          <div id = "contacts">
                  
          <p> this is a demo </p>
              
          </div>
          </body>
          </html>

          而主角就是我們的javascript腳本fetchcontacts.js
          window.onload= function(){
              window.phonebook.debugout(
          "inside js onload");//調(diào)用RIAExample.debugout
              var persons = window.phonebook.getContacts();//調(diào)用RIAExample.getContacts()
              if(persons){//persons實際上是JavaArrayJSWrapper對象
                  window.phonebook.debugout(persons.length() + " of contact entries are fetched");
                  
          var contactsE = document.getElementById("contacts");
                  
          var i = 0
                  
          while(i < persons.length()){//persons.length()調(diào)用JavaArrayJSWrapper.length()方法
                      
                      pnode 
          = document.createElement("p");
                      tnode 
          = document.createTextNode("name : " + persons.get(i).getName() + " number : " + persons.get(i).getNumber());//persons.get(i)獲得Person對象,然后在js里面直接調(diào)用getName()和getNumber()獲取姓名和號碼
                      pnode.appendChild(tnode);
                      contactsE.appendChild(pnode);
                      i 
          ++;
                  }

              }
          else{
                  window.phonebook.debugout(
          "persons is undefined");
              }

              
          }


           例子很簡單,我加了注釋希望有助大家理解,其他我就不深入解釋了。
          我把例子的源代碼放上來,你可以下來試一試。

          RIADemo

          這個例子說明通過WebView.addJavascriptInterface方法,我們可以擴(kuò)展JavaScript的API,獲取Android的數(shù)據(jù)。這樣,JS的粉絲就可以使用Dojo,JQuery,Prototy等這些知名的js框架來搭建android應(yīng)用程序來展現(xiàn)它們很酷很玄的效果!但是,目前addJavascriptInterface還不夠靈活強(qiáng)大,為什么呢?敬請關(guān)注第四篇“what next?”


          posted @ 2009-03-14 16:16 勤勞的蜜蜂 閱讀(5490) | 評論 (6)編輯 收藏

          移動互聯(lián)網(wǎng)時代--忽如一夜春風(fēng)來,web花開各終端

          自從Apple在safari上使用webkit并移植到iphone后,webkit就仿佛是獲得了選秀第一名似的,吸引了眾多眼球。
          其實,我最早聽說webkit是在iphone出來前一兩年,當(dāng)時有報道說Nokia正在采用webkit為它下一代的Symbian平臺做一個引擎,而且還有專門的社區(qū),但比較封閉,很難進(jìn)入那個圈子,讓人一點感覺沒有。不過,至少說明Nokia很早就盯上這一塊了。
          iphone發(fā)布后,Apple似乎就接管webkit了,從此,webkit社區(qū)就有了巨大變化。Apple把能開放的東西都放到了webkit社區(qū)里面,速度還相當(dāng)?shù)目欤ㄓ肁pple自己的話說,他們貢獻(xiàn)了81%的力量,看看webkit社區(qū)的主力開發(fā)人員,有多少來自Apple?。┈F(xiàn)在,我們時不時的可以看到webkit的更新,比如對html5的跟進(jìn),css的特效等新功能!其中不得不重點提出的是2008年6月2日,webkit社區(qū)發(fā)布了高性能javascript引擎SquirrelFish!9月3日google chrome發(fā)布,采用了V8 javascript引擎,號稱比SquirrelFish還快,兩個禮拜后webkit就發(fā)布了SquirrelFish Extreme給予正面還擊。巨頭們牟足了勁爭先把javascript引擎油門踩到底!我不想猜測他們賣力的真正原因,但是,我覺得隨著移動終端能力加強(qiáng),javascript大面積攻進(jìn)終端已經(jīng)指日可待了,iphone和android已經(jīng)實現(xiàn)了。
          這期間,由于Apple的快速組合拳,Nokia經(jīng)營的webkit專區(qū)很快就倒閉了。但他并沒有放棄對webkit的投入,2008年1月28日Nokia宣布收購了Trolltech公司?,F(xiàn)在Trolltech lab作為Nokia的代表仍然活躍在webkit社區(qū)之中。就前兩天,他們還在http://planet.webkit.org/發(fā)布了一篇標(biāo)題極其引人的博文“Creating a Google chat client in 15 minutes”,為QWebView做了一把廣告,相信qt的粉絲肯定不會錯過。而本月初發(fā)布的qt4.5中,明確指出qt加強(qiáng)了對webkit諸如SquirrelFish等新功能的整合。那么就讓我們期待一下Nokia的新款機(jī)器吧。
          到此,我們已經(jīng)看到Google, Apple, Nokia, Palm等移動終端的領(lǐng)頭羊們所采取的行動,然而,不要忘記軟件廠商!Adobe就是杰出的代表。作為RIA的強(qiáng)烈倡導(dǎo)者,他也選擇把webkit整合到其AIR平臺,你可以想象這個webkit+flash的威力有多強(qiáng)大!而Adobe發(fā)起的openscreen項目,更是表明了他要在RIA上爭奪王位的野心!
          好了,不啰嗦那么多了,總之呢,webkit已經(jīng)成為移動互聯(lián)網(wǎng)這出大戲的主角了,回歸到我在上一篇中提到的主題,讓我們來研究一下android.webkit.WebView的功能,來看android上的RIA。
          敬請關(guān)注下一篇--使用WebView的小例子。

          posted @ 2009-03-10 22:39 勤勞的蜜蜂 閱讀(2292) | 評論 (0)編輯 收藏

          移動互聯(lián)網(wǎng)時代的終端--暫時遺忘OSGi,讓我們?nèi)テ肺兑槐璦ndroid磨出的移動互聯(lián)網(wǎng)咖啡吧

          1年多前,揣著在移動終端推廣OSGi的夢想,我離開了原來的公司,來到了一個自認(rèn)為更利于osgi的地方,在我看來osgi擁有eRCP這樣的粉絲,應(yīng)該會給它在移動終端上提供廣闊的舞臺。然而半路殺出個程咬金,google android!第一次見到它時,我不相信osgi會輸,于是就廢寢忘食的研究它,結(jié)果我沒有進(jìn)行太多的抵抗,很快就被Android收編了。我得承認(rèn)它在某些關(guān)鍵的因素,確實比osgi更適合移動終端。對此,我只能感嘆google開源的偉大,以及摒棄jcp的雷厲風(fēng)行!但是,更為重要的是,在移動互聯(lián)網(wǎng)時代即將到來的時刻,Android為我們打開了一扇方便之門,這到底為何?

          通過對Android的(java)源碼進(jìn)行掃描后,我看到了像jsr211,MVM這些老朋友的影子,嗯,我可不想再炒這些舊飯了。還有啥?網(wǎng)上熱炒android用了webkit作為其瀏覽器的引擎,和iphone的safari使用的引擎一樣,這個東西應(yīng)該不錯,因為我一直覺得eRCP沒有很好的web控件,使得它在移動平臺上遜色不少,于是我翻到了android.webkit這個java包,wow! Google給webkit封裝了很多java的接口,這真是java開發(fā)人員的福氣,(聽說很多java開發(fā)人員對sun的javafx期待度最高的就是一個傳說中的JWebView控件,但是不知道出來沒,算了,我懶得查證了,因為俺現(xiàn)在不想浪費太多在sun java上,呵呵),不過我覺得這更是廣大互聯(lián)網(wǎng)應(yīng)用開發(fā)人員的福氣!想想!用html + css + javascript就能編寫android的類似電話簿,日歷甚至手機(jī)桌面的應(yīng)用,這難道不讓您浮想聯(lián)翩嗎?所以,如果說Android為移動終端進(jìn)入互聯(lián)網(wǎng)開了一扇門,那么android.webkit.WebView就是這扇門的金鑰匙,而webkit就是通向互聯(lián)網(wǎng)的康莊大道!

          其實android的webview是一個極端復(fù)雜的控件,而我個人認(rèn)為它是可以實現(xiàn)現(xiàn)在熱炒的RIA/webos等概念的基石!雖然我進(jìn)入這個領(lǐng)域不長,但覺得它非常有意思,很有前途,所以我打算寫一個系列,分享對它的理解,強(qiáng)烈歡迎各位高手指點!

          怎么開始說呢?不知道大家有沒有注意,年初Palm公司在CES上palm pre的基于webkit的webos驚艷之秀,十足掉起了大家的胃口,這是我聽到第一個冠以webos的移動終端,這是不是意味著目前由palm來唱這個webos的獨角戲呢?答案當(dāng)然不是,其實很多終端廠家以及平臺廠商很早就率領(lǐng)大部隊兵臨webos的城下了,相信很快一場硝煙彌漫的戰(zhàn)爭就要開始了......

          欲知端的,敬請關(guān)注移本系列第二篇--忽如一夜春風(fēng)來,web花開各終端

          posted @ 2009-03-09 22:53 勤勞的蜜蜂 閱讀(3455) | 評論 (7)編輯 收藏

          OSGi介紹(七)bundle和service(續(xù))

          接上一篇的例子,為了更具體一點,我們考慮這樣的case,
          假設(shè)房地產(chǎn)開發(fā)商construction A采納了規(guī)劃公司design A的方案,打算建造公寓類型的房子CityApartment
          然后客戶A買了一套房子

          用ooa方式分析這個case,
          我抽象這幾個實體:規(guī)劃公司,圖紙類型,開發(fā)商,房子,買房人
          然后描述這幾件事情:開發(fā)商選擇設(shè)計圖紙,蓋樓然后銷售;買房人根據(jù)圖紙買房并使用房子

          下面是我用java語言來簡單描述它:

          design A 公司

          package design.a;
          interface Apartment {
          ..
          //方法省略先
          }

           

          開發(fā)商construction A

          package construction.a

          import design.a.*;//開發(fā)商要按照圖紙蓋樓

          class CityApartment implements Apartment {
          .
          //方法省略先
          }


          class Construction implements BundleActivator{

          }



          客戶 costumer A

          package customer.a

          import design.a.*;//客戶要按照圖紙選擇房子

          class Customer implements BundleActivator {
          .
          //方法省略先
          }



          然后我們把他們分別做成bundle
          Bundle A : design A
          其manifest中這樣描述

          BundleSymbolicName: design A
          Export
          -Package: design.a


          Bundle B : construction A
          其manifest中這樣描述

          BundleSymbolicName: construction A
          Import
          -Package: design.a
          Bundle
          -Activator: construction.a.Construction


          Bundle C : customer C
          其manifest中這樣描述

          BundleSymbolicName: customer C
          Import
          -Package: design.a
          Bundle
          -Activator: customer.c.Customer



          這樣,裝入到framework后,framework就會把BundlB和C與BundleA關(guān)聯(lián)起來,正好描述開發(fā)商A選擇design A的圖紙,客戶A也不得不選擇design A的圖紙啦
          可是,單從這里,我們看不出來,開發(fā)商和客戶拿同一份圖紙干什么。那我們得必須在BundleB和C的實現(xiàn)里面寫點東西來說明。

          這里給出開發(fā)商construction.a.Construction的偽代碼:

          class Construction implements BundleActivator {

           
          public void start(BundleContext context){
            CityApartment apartment 
          = null;
            Hashtable properties 
          = null;
            
          for(int i = 0; i < 100; i ++){
             properties 
          = new Hashtable();
             properties.put(
          "price",new Integer(1000 + i*5));//開發(fā)商為房子定價
             apartment = new CityApartment();//一套房子蓋好
             
          //把房子按照公寓注冊出去并打廣告,等待客戶來購買,framework就相當(dāng)于一個售樓處兼房屋中介
             context.registerService(Apartment.class.getName()/*公寓類型*/,apartment/*房子作為服務(wù)對象*/,properties/*與房子相關(guān)的附帶信息*/);
             
            }

            
          //這樣開發(fā)商一共注冊一百套房子
           }

          }



          而客戶的代碼可以如下:

          class Customer implements BundleActivator {
           
          public void start(BundleContext context){
            Apartment apartment 
          = null;
            ServiceReference ref 
          = context.getServiceReference(Apartment.class.getName,"(price=1050)");//先簽署購房合同,而且指明選擇Apartment類型,價格為1050的房子。
            apartment = (Apartment)context.getService(ref);//然后買到房子
            
          //買房人就可以使用房子apartment對象進(jìn)行日常生活了
           }

          }


          這樣,我們就很清楚的看出,Design A為Construction A和Customer A提供了共同的Apartment定義,才使得他們有交易的可能。于此同時,Construction A和Customer A之間的耦合是非常松的,因為,如果有另外一個開發(fā)商onstruction B加入進(jìn)來也構(gòu)造了Apartment的對象,Customer就可以通過改變選擇條件,輕易的獲得B的房子,而客戶本身不關(guān)心房子是A還是B蓋的,這個是典型的面向?qū)ο蟮亩鄳B(tài)應(yīng)用。

          總的說來,Bundle在framework的幫助下,使得其他bundle使用其類型定義成為可能。service就是在這些共享的類型定義基礎(chǔ)上產(chǎn)生的具體對象,而使用這些service對象的bundle,必然也是對應(yīng)共享類型的使用者。
          這種類型共享,在osgi里面叫做"class space". framework運(yùn)行時通過關(guān)聯(lián)bundle之間的類型定義,可以構(gòu)成一個或多個"class space",而某個bundle在framework里面,只能處在一個"class space"里面,不能同時出現(xiàn)在多個"class space"中。
          怎么理解這個話呢?請看下一篇,外星人入侵了。

           

          posted @ 2007-07-21 22:28 勤勞的蜜蜂 閱讀(1737) | 評論 (1)編輯 收藏

          OSGi 介紹(七)bundle和service的關(guān)系

          osgi系列已經(jīng)發(fā)表了有將近2年的時間了,很高興這期間得到了許多朋友的關(guān)注,你們和我的討論切磋都讓我興奮無比。而過去很長的時間里,由于靈感枯竭外加精力有限,不能給大家分享更多osgi的精彩,實在辜負(fù)大家的期望,還請諒解。不過,根據(jù)這段時間大家和我私下的討論,發(fā)現(xiàn)很多人都苦惱于分不清楚bundle和service的關(guān)系,而我的osgi 5-6又臭又長,而且還非常依賴技術(shù),實在是坑害入門者的必備武器。這個不足,猶如頭上方圓半尺盤旋的蒼蠅,困擾著我,解釋的郵件也不知寫多少,估計收效甚微。于是我決定再次出山,寫一個驚世駭俗的分析文章,力圖讓更多讀者都能夠把這兩個瘋馬牛理清楚。

          開講之前,還是用老辦法,給大家舉個例子。放心,這個例子一點都不技術(shù),而且我相信你可能比我還清楚。大家都知道房地產(chǎn)商是怎么運(yùn)作樓盤的吧。據(jù)我淺顯的認(rèn)識,他們都會先進(jìn)行一些圖紙上的規(guī)劃,里面包含小區(qū)的整體規(guī)模,樓房外觀以及廣大疾苦民眾最為關(guān)心和渴求的戶型圖。據(jù)說,很多房地產(chǎn)商在搞定政府某些關(guān)鍵部門(個人意見僅供參考)拿到一塊地后,根本不用費心思去自己規(guī)劃這些自己都一竅不通的東西,只要請一個有資質(zhì)的第三方公司寫寫畫畫,甚至照搬某西方發(fā)達(dá)國家的某社區(qū)概念,冠于中國特色云云,就能使廣大民眾趨之若鶩,傾囊搶購。ok,你可能受不了我又憤世嫉俗了,這個到底和osgi有啥關(guān)系?別著急,天色已經(jīng)很晚,下次某個時間請繼續(xù)關(guān)注osgi(七)續(xù),其間,請大家先考慮一下,如何用ooa的方式來描述人們?nèi)绾钨彿?,以及在房子里進(jìn)行日常生活這樣的簡單場景。

          posted @ 2007-07-16 23:31 勤勞的蜜蜂 閱讀(2495) | 評論 (2)編輯 收藏

          OSGi on mobile phone !

          如果你一直關(guān)注osgi在embedded的發(fā)展,并且還能到舊金山參加正在舉行的java one,那我真是羨慕死你了.

          Nokia在她的N800上demo運(yùn)行了osgi(此消息來源于david beers對BJ在osgi alliance blog上的評論,關(guān)于demo可以看這個link http://thehereweb.googlepages.com/)
          同時美國運(yùn)行商Sprint也宣布采用osgi的手機(jī)平臺將會在年底面世.多么值得期待的事情!

          posted @ 2007-05-14 10:11 勤勞的蜜蜂 閱讀(1799) | 評論 (0)編輯 收藏

          framework implementation updated

          瞎整了半年多,克服了重重困難,終于把原來framework做了更新,如果有興趣,還可以按照原來的Link下載。
          framework下載后,解壓并運(yùn)行startframework.bat就可以啟動framework了。
          目前該framework只支持在內(nèi)存中存儲bundle.

          另外,多增加了一個管理bundle,它為framework提供了簡單的圖形化操作界面,可以在這里下載。

          http://www.aygfsteel.com/Files/Ferrari4000/bundlemanagement.zip

          下載后,最好先把名字改為bundlemanagement.jar
          然后可以這樣安裝,假設(shè)該文件下載到d:\bundles下,則可以在framework的shell下輸入
          in file:d:/bundles/bundlemanagement.jar
          安裝該bundle,安裝成功后輸入stt 1(注意:1是數(shù)字一),啟動該bundle就可以出現(xiàn)圖形操作界面了。

          圖形界面提供bundle的安裝(只支持本地安裝),啟動,停止,升級和刪除等簡單功能。
          其他功能會在后面陸續(xù)增加。目前這個bundle只能運(yùn)行在j2se環(huán)境下。

          給的源代碼沒有很好的build文件,等我有時間了,再寫一個。

          感謝畢嘉兄弟的支持,他幫助設(shè)計了bundle存儲模塊以及實現(xiàn)了bundle在內(nèi)存存儲的第一版。

          有啥問題,可以直接給我發(fā)郵件,jerrylee.li@gmail.com

          posted @ 2006-09-16 23:40 勤勞的蜜蜂 閱讀(1566) | 評論 (0)編輯 收藏

          OSGi的曙光?

          前兩天,看到Peter的blog里說,由ibm牽頭發(fā)起了jsr291(http://www.jcp.org/en/jsr/detail?id=291),
          要把osgi的core應(yīng)用到目前的j2se上,以填補(bǔ)jsr277發(fā)布前的需求空白。

          osgi和277的pk開始了!我投osgi一票,呵呵,你買誰?

          posted @ 2006-03-03 13:51 勤勞的蜜蜂 閱讀(1742) | 評論 (1)編輯 收藏

          OSGi Alliance開設(shè)面向公眾的郵件列表

          繼開設(shè)Blog后(http://www.osgi.org/blog/index.html),昨天OSGi Alliance又宣布開設(shè)一個public的mail list,每個對OSGi感興趣的人都可以加入到這個列表中,通過這個列表可以訊問OSGi的相關(guān)的各種問題以及訂閱列表的郵件。
          請到http://bundles.osgi.org/mailman/listinfo/osgi-dev這里注冊。

          下面是BJ Hargrave的代表OSGi Alliance發(fā)的announcement


          Hello,

          As part of the OSGi evangelism work, I would like to announce that OSGi now has a new public mail list for OSGi technical questions and discussion. This mail list is a public list and is open to anyone to participate. This new list is mainly for non-members to discuss OSGi technology and ask question about the technology. ......

          The new mail list address is:

          osgi-dev@bundles.osgi.org

          You can subscribe to the list here:
          http://bundles.osgi.org/mailman/listinfo/osgi-dev or by sending an e-mail to osgi-dev-subscribe@bundles.osgi.org.

          So please go ahead and subscribe to the new osgi-dev mail list. Your participation there can help educate others about the OSGi technology.

          BJ Hargrave
          Senior Technical Staff Member, IBM
          OSGi Fellow and CTO of the OSGi Alliance

          posted @ 2006-02-16 10:20 勤勞的蜜蜂 閱讀(1302) | 評論 (0)編輯 收藏

          framework implementation

          這里是我目前的成果。
          http://www.aygfsteel.com/Files/Ferrari4000/framework.zip包含了framework的jar文件。
          解壓后,直接運(yùn)行.bat文件就可以了。這個framework實現(xiàn)了r4core的大部分api(除一些支持local和安全的外,secrurity admin和conditional permission這兩個服務(wù)也沒有實現(xiàn),url service也沒有實現(xiàn)),輸入help可以看到可以運(yùn)行的命令(不過全是英文的,還是我寫的,將就一下吧)。

          http://www.aygfsteel.com/Files/Ferrari4000/src.zip包含所有源碼。代碼有點亂,hoho

          要成功編譯代碼,您還需要下載這個http://www.aygfsteel.com/Files/Ferrari4000/osgi.rar
          解壓并后得到4個jar,并把他們放入到build path中,import順序保證它們優(yōu)先于jre就可以了。

          我打算重新編寫了。把framework的實現(xiàn)重新規(guī)劃一下,然后實現(xiàn)core的所有內(nèi)容,進(jìn)而再實現(xiàn)cmpn的所有服務(wù)。
          如果你對開發(fā)osgi framework感興趣,一起來吧!

          給我發(fā)信:jerrylee.li@gmail.com

          posted @ 2006-02-15 09:15 勤勞的蜜蜂 閱讀(1805) | 評論 (8)編輯 收藏

          OSGi介紹(六)OSGi的service

          在給出采用service方式實現(xiàn)的“扶貧助手”之前,我們稍微回顧一下上一篇的成果。
          在(五)中,我們看到程序被分成多個bundle后,程序的模塊程度得到提高,而控制模塊間的耦合度由Import-Package和Export-Package來控制,相對比較靈活。另一方面程序的更新和升級的粒度變小了。誰都知道只更新部分要比全部更新強(qiáng),尤其當(dāng)更新發(fā)生在一些需要建立昂貴的連接時,細(xì)粒度會節(jié)省不少花銷。除了這些,我們看不到其他新鮮的東西。說白了,也就是挖空心思想一些design pattern來劃分程序模塊。
           
          好了,馬上就新鮮了。下面你會看到通過采用service方式來改造(五)中的程序,gui bundle在某些情況下不用重新啟動,就能直接某些適應(yīng)需求的變更!
          先給出model bundle的代碼,該bundle包含兩個java package,分別是:
          com.bajie.test.family.model
          com.bajie.test.family.model.impl
          在com.bajie.test.family.model這個package中包含如下的class和interface:
          package com.bajie.test.family.model;
          import java.util.List;
          import javax.swing.table.AbstractTableModel;
          public abstract class FamilyInfoDatabase extends AbstractTableModel{
             
              public abstract void sort(SortingFamilyInfoCriteria sortField) throws IllegalArgumentException;
             
              public abstract void addEntry(List columns, List values) throws IllegalArgumentException;
              public abstract void deleteEntry(String familyName);
              public abstract void update(String familyName,List columns, List values)throws IllegalArgumentException;
          }

          這是database的model,與(五)定義成interface不同,我們直接讓它繼承了AbstractTableModel,這是因為我們希望當(dāng)數(shù)據(jù)或顯示需求變化時,gui上的JTable能獲得通知,并顯示更新的結(jié)果。SortingFamilyInfoCriteria這個類型下文會給出說明。
           
          package com.bajie.test.family.model;
          public class FamilyInfoEntry {
              private String familyName;
              private int population;
              private int incomePerYear;
             
              public FamilyInfoEntry(String familyName,int population,int income){
                  this.familyName = familyName;
                  this.population = population;
                  this.incomePerYear = income;
              }
             
              public String getFamilyName() {
                  return familyName;
              }
              public int getIncomePerYear() {
                  return incomePerYear;
              }
              public int getPopulation() {
                  return population;
              }
          }

          這個類的結(jié)構(gòu)和在(五)中完全一樣,用來紀(jì)錄一條家庭信息。唯一不同的是,在(五)中我們把它放入了實現(xiàn)(.impl)package中,在后面給出bundle的manifest文件時,我將解釋為什么要這樣改。
           
          package com.bajie.test.family.model;
          public interface FamilyInfoColumn {
              public Object getColumnValue(FamilyInfoEntry entry);
             
              public String getColumnName();
          }
          這個類用來描述table中的某個列。
          package com.bajie.test.family.model;
          import java.util.Comparator;
          public interface SortingFamilyInfoCriteria extends Comparator{
              public String getSortFieldString();
          }
          這個類將用于對家庭紀(jì)錄按某一列的值進(jìn)行排序。
          在com.bajie.test.family.model.impl這個package中包含上面抽象類和interface的實現(xiàn):
          package com.bajie.test.family.model.impl;
          import java.util.Arrays;
          import java.util.LinkedList;
          import java.util.List;
          import org.osgi.framework.BundleActivator;
          import org.osgi.framework.BundleContext;
          import org.osgi.framework.Constants;
          import org.osgi.framework.ServiceEvent;
          import org.osgi.framework.ServiceListener;
          import org.osgi.framework.ServiceReference;
          import com.bajie.test.family.model.FamilyInfoColumn;
          import com.bajie.test.family.model.FamilyInfoDatabase;
          import com.bajie.test.family.model.FamilyInfoEntry;
          import com.bajie.test.family.model.SortingFamilyInfoCriteria;
          public class FamilyDatabase extends FamilyInfoDatabase implements  BundleActivator,
                  ServiceListener {
              private LinkedList familyEntryList = new LinkedList();
              private Object[] sortedValues = null;
              private LinkedList columns = new LinkedList();
              private BundleContext context;
              public int getColumnCount() {
                  return this.columns.size();
              }
              public String getColumnName(int index) {
                  return ((FamilyInfoColumn)columns.get(index)).getColumnName();
              }
             
              public Object getValueAt(int row, int column) {
                  FamilyInfoEntry entry = (FamilyInfoEntry) this.sortedValues[row];
                  if(column >= this.familyEntryList.size()){
                      return null;
                  }
                  return ((FamilyInfoColumn) this.columns.get(column))
                          .getColumnValue(entry);
              }
              public int getRowCount() {
                  return this.familyEntryList.size();
              }
              public void addEntry(List columns, List values)
                      throws IllegalArgumentException {
              }
              public void deleteEntry(String familyName) {
              }
              public void update(String familyName, List columns, List values)
                      throws IllegalArgumentException {
              }
              public void sort(SortingFamilyInfoCriteria sortField) {
                  Arrays.sort(this.sortedValues, sortField);
              }
              public void start(BundleContext context) throws Exception {
                  this.context = context;
                  this.familyEntryList.add(new FamilyInfoEntry("Zhang", 3, 1200));
                  this.familyEntryList.add(new FamilyInfoEntry("Li", 6, 1800));
                  this.familyEntryList.add(new FamilyInfoEntry("Liu", 5, 1500));
                  this.familyEntryList.add(new FamilyInfoEntry("Wang", 4, 1300));
                 
                  this.sortedValues = this.familyEntryList.toArray();
           //向framework注冊一個類型為FamilyInfoDatabase的服務(wù)
                  context.registerService(FamilyInfoDatabase.class.getName(),this,null);
           //向framework注冊三個服務(wù),每個服務(wù)的類型既為FamilyInfoColumn,也是SortingFamilyInfoCriteria
                  String[] clazzes = new String[] {FamilyInfoColumn.class.getName(),SortingFamilyInfoCriteria.class.getName()};
                  context.registerService(clazzes,new FamilyNameColumn(),null);
                  context.registerService(clazzes,new FamilyPopulationColumn(),null);
                  context.registerService(clazzes,new FamilyIncomeColumn(),null);
                 
           //向framework查找所有注冊類型為FamilyInfoColumn的服務(wù)
           //先獲得服務(wù)的引用
                  ServiceReference[] columnRefs = context.getServiceReferences(
                          FamilyInfoColumn.class.getName(), null);
                  FamilyInfoColumn column = null;
                  for (int i = 0; i < columnRefs.length; i++) {
                      System.out.println(i + ":" + ((String[])(columnRefs[i].getProperty(Constants.OBJECTCLASS)))[0]);
               //通過引用獲得具體的服務(wù)對象,每一個對象都將轉(zhuǎn)化成gui中table的一列
                      column = (FamilyInfoColumn) context.getService(columnRefs[i]);
                      if (column != null) {
                          this.columns.add(column);
                      }else{
                          System.out.println("null service object.");
                      }
                  }

           //注冊服務(wù)偵聽器,該偵聽器專門偵聽FamilyInfoColumn服務(wù)對象的動態(tài)(主要是增加和刪除)
                  context.addServiceListener(this,"(" + Constants.OBJECTCLASS + "="
                          + FamilyInfoColumn.class.getName() + ")");
              }
              public void stop(BundleContext context) throws Exception {
              }
              public void serviceChanged(ServiceEvent event) {
                  switch (event.getType()) {
                  case ServiceEvent.MODIFIED:
                      return;
                  case ServiceEvent.REGISTERED://表明有新的列產(chǎn)生了。
                      ServiceReference ref = event.getServiceReference();
                      Object service = this.context.getService(ref);
                      this.columns.add(service);
                      this.fireTableStructureChanged();//通知gui,表結(jié)構(gòu)發(fā)生變化
                      return;
                  case ServiceEvent.UNREGISTERING://表明有些列將被刪除
                      ref = event.getServiceReference();
                      service = this.context.getService(ref);
                      this.columns.remove(service);
                      this.fireTableStructureChanged();//通知gui,表結(jié)構(gòu)發(fā)生變化
                      return;
                  }
              }

              //這個類定義一個“Family Name”這個列,以及如何按這個列的值進(jìn)行排序
              class FamilyNameColumn implements FamilyInfoColumn,SortingFamilyInfoCriteria {
                  private static final String COLUMNNAME = "Family Name";
                 
                  public Object getColumnValue(FamilyInfoEntry entry) {
                      return entry.getFamilyName();
                  }
                 
                 
                  public String getColumnName() {
                      return FamilyNameColumn.COLUMNNAME;
                  }
                 
                  public String getSortFieldString() {
                      return FamilyNameColumn.COLUMNNAME;
                  }
                  public int compare(Object obj1, Object obj2) {
                      if (obj1 == obj2) {
                          return 0;
                      }
                      FamilyInfoEntry en1 = (FamilyInfoEntry)obj1;
                      FamilyInfoEntry en2 = (FamilyInfoEntry)obj2;
                     
                      return en1.getFamilyName().compareTo(en2.getFamilyName());
                  }
                 
              }
              //這個類定義一個“Family Population”這個列,以及如何按這個列的值進(jìn)行排序
              class FamilyPopulationColumn implements FamilyInfoColumn, SortingFamilyInfoCriteria {
                  private static final String COLUMNNAME = "Family Population";
                  public Object getColumnValue(FamilyInfoEntry entry) {
                      return new Integer(entry.getPopulation());
                  }
                  public String getColumnName() {
                      return FamilyPopulationColumn.COLUMNNAME;
                  }
                 
                  public String getSortFieldString() {
                      return FamilyPopulationColumn.COLUMNNAME;
                  }
                 
                  public int compare(Object obj1, Object obj2) {
                      if (obj1 == obj2) {
                          return 0;
                      }
                      FamilyInfoEntry en1 = (FamilyInfoEntry)obj1;
                      FamilyInfoEntry en2 = (FamilyInfoEntry)obj2;
                     
                      return en1.getPopulation() - en2.getPopulation();
                  }
              }
             
              //這個類定義一個“Family Income”這個列,以及如何按這個列的值進(jìn)行排序
              class FamilyIncomeColumn implements FamilyInfoColumn, SortingFamilyInfoCriteria {
                  private static final String COLUMNNAME = "Family Income";
                  public Object getColumnValue(FamilyInfoEntry entry) {
                      return new Integer(entry.getIncomePerYear());
                  }
                  public String getColumnName() {
                      return FamilyIncomeColumn.COLUMNNAME;
                  }
                 
                 
                  public String getSortFieldString() {
                      return FamilyIncomeColumn.COLUMNNAME;
                  }
                  public int compare(Object obj1, Object obj2) {
                      if (obj1 == obj2) {
                          return 0;
                      }
                      FamilyInfoEntry en1 = (FamilyInfoEntry)obj1;
                      FamilyInfoEntry en2 = (FamilyInfoEntry)obj2;
                     
                      return en1.getIncomePerYear() - en2.getIncomePerYear();
                  }
                 
              }
          }
           
          與(五)相比,最大的不同就是表結(jié)構(gòu)的“列”是通過查找所有類型為FamilyInfoColumn的服務(wù)對象而組成的。而通過framework提供的服務(wù)偵聽機(jī)制(即實現(xiàn)ServiceListener接口并注冊到framework中),bundle能夠獲得該類服務(wù)對象的動態(tài)事件通知,如果該事件是新服務(wù)注冊,則添加一個顯示列,如果是服務(wù)被注銷,則刪除對應(yīng)的顯示列。
           
          下面是bundle的manifest文件
          Manifest-Version: 1.0
          Bundle-SymbolicName: com.bajie.test.family.model
          Bundle-Name: family model
          Bundle-Version: 1.0
          Bundle-Vendor: LiMing
          Bundle-Activator: com.bajie.test.family.model.impl.FamilyDatabase
          Import-Package: org.osgi.framework;version=1.3,com.bajie.test.family.model
          Export-Package: com.bajie.test.family.model;version=1.0
          從中我們看到com.bajie.test.family.model這個package被export出來,這樣其他bundle就能夠import這個package,并根據(jù)FamilyInfoEntry所提供的基本內(nèi)容提供一些額外的處理結(jié)果,從而產(chǎn)生新列(FamilyInfoColumn)以及排序方法(SortingFamilyInfoCriteria),比如家庭人均年收入。

          下面來看看gui bundle,它只包含一個package
          package com.bajie.test.family.gui;
          import java.awt.BorderLayout;
          import java.awt.Dimension;
          import java.awt.GridLayout;
          import java.awt.event.ActionEvent;
          import java.awt.event.ActionListener;
          import java.awt.event.ItemEvent;
          import java.awt.event.ItemListener;
          import java.util.Hashtable;
          import javax.swing.JButton;
          import javax.swing.JComboBox;
          import javax.swing.JFrame;
          import javax.swing.JLabel;
          import javax.swing.JPanel;
          import javax.swing.JScrollPane;
          import javax.swing.JTable;
          import org.osgi.framework.BundleActivator;
          import org.osgi.framework.BundleContext;
          import org.osgi.framework.Constants;
          import org.osgi.framework.ServiceEvent;
          import org.osgi.framework.ServiceListener;
          import org.osgi.framework.ServiceReference;
          import com.bajie.test.family.model.FamilyInfoDatabase;
          import com.bajie.test.family.model.SortingFamilyInfoCriteria;
          public class FamilyInfoGui implements BundleActivator, ActionListener,
                  ItemListener, ServiceListener {
              private JFrame mainFrame;
              private JPanel contentPanel;
              private JTable familiesTable;
              private JScrollPane familiesTableScrollPane;
              private JPanel sortedByPanel = new JPanel(new GridLayout(1, 2));
              private JLabel sortedByLabel = new JLabel("Sorted By: ");
              private JComboBox sortedByList = null;
              private JPanel commandPanel = new JPanel(new GridLayout(1, 3));
              private JButton addEntry = new JButton("Add");
              private JButton deleteEntry = new JButton("Delete");
              private JButton updateEntry = new JButton("Update");
              private Hashtable sortingFields = new Hashtable();
              private BundleContext context;
              FamilyInfoDatabase database = null;
              public void start(BundleContext context) throws Exception {
                  this.context = context;
                  //查找所有注冊類型為FamilyInfoDatabase的服務(wù)對象。在我們這個例子,它是由上面給出的model bundle注冊的
                  ServiceReference databaseServiceRef = context
                          .getServiceReference(FamilyInfoDatabase.class.getName());
                  if (databaseServiceRef == null) {
                      System.out.println("No database service is registered.");
                      return;
                  }
           //這個服務(wù)對象將成為JTable的數(shù)據(jù)model
                  this.database = (FamilyInfoDatabase) context
                          .getService(databaseServiceRef);
                  if (this.database == null) {
                      System.out.println("Can not get database object");
                      return;
                  }
                  //查找所有注冊類型為SortingFamilyInfoCriteria的服務(wù)對象。
                  ServiceReference[] sortingCriteria = context.getServiceReferences(
                          SortingFamilyInfoCriteria.class.getName(), null);
                  sortedByList = new JComboBox();
                  SortingFamilyInfoCriteria criterion = null;
                  if (sortingCriteria != null) {
                      for (int i = 0; i < sortingCriteria.length; i++) {
                          criterion = (SortingFamilyInfoCriteria) context
                                  .getService(sortingCriteria[i]);
                          if (criterion != null) {
                //每個服務(wù)對象將對應(yīng)一種排序方法,并加入到下拉列表中
                              sortedByList.addItem(criterion.getSortFieldString());
                              this.sortingFields.put(criterion.getSortFieldString(),
                                      criterion);
                          }
                      }
                  }
           //注冊服務(wù)偵聽器,該偵聽器專門偵聽SortingFamilyInfoCriteria服務(wù)對象的動態(tài)(主要是增加和刪除)
                  context.addServiceListener(this, "(" + Constants.OBJECTCLASS + "="
                          + SortingFamilyInfoCriteria.class.getName() + ")");
                  sortedByList.addItemListener(FamilyInfoGui.this);
                  //construct gui
                  Runnable r = new Runnable() {
                      public void run() {
                          contentPanel = new JPanel();
                          familiesTableScrollPane = new JScrollPane();
            //獲得的FamilyInfoDatabase對象成為gui中JTable的model
                          familiesTable = new JTable(database);
                          familiesTableScrollPane.setViewportView(familiesTable);
                          sortedByPanel.add(sortedByLabel);
                          sortedByPanel.add(sortedByList);
                          commandPanel.add(addEntry);
                          commandPanel.add(deleteEntry);
                          commandPanel.add(updateEntry);
                          contentPanel.add(sortedByPanel, BorderLayout.NORTH);
                          contentPanel.add(familiesTableScrollPane, BorderLayout.CENTER);
                          contentPanel.add(commandPanel, BorderLayout.SOUTH);
                          mainFrame = new JFrame();
                          mainFrame.setContentPane(contentPanel);
                          mainFrame.setSize(new Dimension(500, 600));
                          mainFrame.show();
                      }
                  };
                  Thread t = new Thread(r);
                  t.start();
              }
              public void stop(BundleContext context) throws Exception {
                  if (this.mainFrame != null)
                      this.mainFrame.dispose();
              }
              public void actionPerformed(ActionEvent event) {
              }
              public void itemStateChanged(ItemEvent event) {
                  if (event.getSource() == this.sortedByList) {
                      SortingFamilyInfoCriteria criterion = (SortingFamilyInfoCriteria) this.sortingFields
                              .get(event.getItem());
                      if (criterion == null)
                          return;
                      this.database.sort(criterion);
                      this.familiesTable.repaint();
                  }
              }
              public void serviceChanged(ServiceEvent event) {
                  switch (event.getType()) {
                  case ServiceEvent.MODIFIED:
                      return;
                  case ServiceEvent.REGISTERED://有新的排序方法注冊到framework當(dāng)中
                      ServiceReference ref = event.getServiceReference();
                      SortingFamilyInfoCriteria criterion = (SortingFamilyInfoCriteria) this.context
                              .getService(ref);
                      if (criterion != null) {
            //把新的排序方法加入到下拉列表中
                          sortedByList.addItem(criterion.getSortFieldString());
                          this.sortingFields.put(criterion.getSortFieldString(),
                                  criterion);
                      }
                      return;
                  case ServiceEvent.UNREGISTERING://一個現(xiàn)有的排序方法將被從framework被取消
                      ref = event.getServiceReference();
                      criterion = (SortingFamilyInfoCriteria) this.context
                              .getService(ref);
                      if (criterion != null) {
            //把該排序方法從下拉列表中刪除
                          sortedByList.removeItem(criterion.getSortFieldString());
                          this.sortingFields.remove(criterion);
                      }
                      return;
                  }
              }
          }
           
          與(五)相比不同的地方是,這個gui的table model以及排序的方法,都是通過查詢service對象獲得。
           
          manifest文件如下:
          Manifest-Version: 1.0
          Bundle-SymbolicName: com.bajie.test.family.gui
          Bundle-Name: family gui
          Bundle-Version: 1.0
          Bundle-Vendor: LiMing
          Bundle-Activator: com.bajie.test.family.gui.FamilyInfoGui
          Import-Package: org.osgi.framework;version=1.3,com.bajie.test.family.model
           
          然后我們生成bundle的jar文件。分別為familymodel.jar和familygui.jar,之后我們用“in”命令把兩個bundle裝入framework。
          接著我們先啟動model bundle,然后再啟動gui bundle,我們會看到JTable中有3列,而排序方法列表中也有3個選項,完全和程序的邏輯符合。
           
          接下來,我們假設(shè)客戶需要添加顯示每個家庭的人均年收入并按其排列紀(jì)錄。要滿足這個需求,我們可以參考在(五)中做法,就是在model bundle里面再添加一個同時實現(xiàn)了FamilyInfoColumn和SortingFamilyInfoCriteria的類,并在bundle的啟動中作為服務(wù)注冊到framework中?不過這樣就得更新model bundle然后調(diào)用rfr命令來刷新。為什么不再裝一個補(bǔ)丁bundle,在這個bundle中包含了同時實現(xiàn)FamilyInfoColumn和SortingFamilyInfoCriteria的類,并在這個新bunle啟動時注冊產(chǎn)生該類的新對象作為服務(wù)注冊到framework中,這樣gui和model bundle都能偵聽到該新服務(wù)的到來(他們都實現(xiàn)了服務(wù)偵聽接口ServiceListener),gui上馬上就能有所體現(xiàn)。

          這個新bundle的代碼如下:
          package com.bajie.test.family.model.impladd;
          import org.osgi.framework.BundleActivator;
          import org.osgi.framework.BundleContext;
          import com.bajie.test.family.model.FamilyInfoColumn;
          import com.bajie.test.family.model.FamilyInfoEntry;
          import com.bajie.test.family.model.SortingFamilyInfoCriteria;
          public class FamilyIncomePerPerson implements BundleActivator {
              public void start(BundleContext context) throws Exception {
           //注冊一個新的服務(wù),服務(wù)的類型既為FamilyInfoColumn,也是SortingFamilyInfoCriteria
                  String[] clazzes = new String[] {FamilyInfoColumn.class.getName(),SortingFamilyInfoCriteria.class.getName()};
                  context.registerService(clazzes,new FamilyIncomePerPersonColumn(),null);
                 
              }
              public void stop(BundleContext context) throws Exception {
              }
              //這個類實現(xiàn)了“Income Per Person”這個列以及按該列排序的方法。
              class FamilyIncomePerPersonColumn implements FamilyInfoColumn,SortingFamilyInfoCriteria {
                  private static final String COLUMNNAME = "Income Per Person";
                 
                  public Object getColumnValue(FamilyInfoEntry entry) {
                      return new Integer(entry.getIncomePerYear()/entry.getPopulation());
                  }
                 
                 
                  public String getColumnName() {
                      return FamilyIncomePerPersonColumn.COLUMNNAME;
                  }
                 
                  public String getSortFieldString() {
                      return FamilyIncomePerPersonColumn.COLUMNNAME;
                  }
                  public int compare(Object obj1, Object obj2) {
                      if (obj1 == obj2) {
                          return 0;
                      }
                      FamilyInfoEntry en1 = (FamilyInfoEntry)obj1;
                      FamilyInfoEntry en2 = (FamilyInfoEntry)obj2;
                     
                      return en1.getIncomePerYear()/en1.getPopulation() - en2.getIncomePerYear()/en2.getPopulation();
                  }
                 
              }
          }
           
          manifest文件如下:
          Manifest-Version: 1.0
          Bundle-SymbolicName: com.bajie.test.family.modeladd
          Bundle-Name: family model add
          Bundle-Version: 1.0
          Bundle-Vendor: LiMing
          Bundle-Activator: com.bajie.test.family.model.impladd.FamilyIncomePerPerson
          Import-Package: org.osgi.framework;version=1.3,com.bajie.test.family.model
           
          打包安裝到framework后,啟動該bundle,我們就會在gui上看到新的列已經(jīng)被添加,而且排序列表中增加了一個新的排序選項。
          這個結(jié)果,完全符合需求的意圖。
          如果我們用stp命令停止這個bundle,我們在gui上就會發(fā)現(xiàn),新列消失,而且排序列表中對應(yīng)選項也沒有了。這就是service帶來的動態(tài)效果。不過,如果我們的model發(fā)生了一些實質(zhì)的變化,比如FamilyInfoEntry需要添加一個“地址”列,那么model bundle就要更新,進(jìn)而gui bundle以及使用到這個類型的bundle都需要通過rfr命令刷新。
           
          好了,對扶貧助手的分析就此打住,我們總結(jié)一下,通過程序可以看到注冊服務(wù)一點都不復(fù)雜。最簡單的情況我們只需要提供一個java類型名稱,以及實現(xiàn)這個類型的一個java對象就可以了,
          不需要提供復(fù)雜的類型描述,比如xml描述文件。而使用服務(wù)的bundle通過類型名稱就輕而易舉的查找到相關(guān)的服務(wù)對象。
           
          到此,osig介紹系列就要結(jié)束了,只希望這個系列能夠把你引入到osgi的門口,其后面的精彩世界就看你的興趣了。
          就我個人的關(guān)注和理解,今年是osgi很重要的一年。JSR249今年應(yīng)該投票,如果osgi入選,那么osgi將成為高端手機(jī)中java體系結(jié)構(gòu)的重要組成部分。
          在汽車領(lǐng)域,siemensVDO已經(jīng)推出了基于osgi的解決方案,聽說已經(jīng)配備在BMW serials 5里面了。應(yīng)該還會有更多的應(yīng)用......
           
          如果你是osgi的粉絲,歡迎你來信jerrylee.li@gmail.com拍磚交流。

          posted @ 2006-02-14 16:08 勤勞的蜜蜂 閱讀(5966) | 評論 (13)編輯 收藏

          OSGi介紹(五)兩個bundle

               摘要: (四)中提到的直接型改造法實際上和一個傳統(tǒng)的java應(yīng)用程序沒有區(qū)別。因此客戶的需求發(fā)生變化,通常是牽一發(fā)而動全身。那么我們現(xiàn)在就看看如果在osgi framework中,用多個bundle來實現(xiàn)的效果吧。 我的想法是用兩個bundle來配合實現(xiàn)“扶貧助手”的功能。一個bundle專門負(fù)責(zé)錄入和顯示紀(jì)錄,一個bundle專門負(fù)責(zé)紀(jì)錄的數(shù)據(jù)結(jié)構(gòu)和對數(shù)據(jù)的處理,用時下時髦的說法就是使用了mvc,只是...  閱讀全文

          posted @ 2006-02-14 16:02 勤勞的蜜蜂 閱讀(4393) | 評論 (3)編輯 收藏

          OSGi介紹(四)第一個bundle

          先給出“扶貧助手”的第一種改造,我稱之為“直接型”,請看:

          package aa.bb.cc;
          //需要import osgi的核心package
          import org.osgi.framework.BundleActivator;
          import org.osgi.framework.BundleContext;
          //實現(xiàn)了BundleActivator
          public class FamilyInfo implements BundleActivator {
           
          private String familyName;
           
          private int population;
           
          private int incomePerYear;
           省略了getter和setter方法 
           
          public String toString() {
            
            
          return "Family: " + this.familyName + ", population: " + this.population + ", income: " + this.incomePerYear;
           }

           
           
          public int getIncomePerMember(){
            
          return (int)(this.incomePerYear/this.population);
           }

           
          public static void sortByIncomePerYear(FamilyInfo[] families){
            FamilyInfo temp 
          = null;
            
          for(int i = 0; i < families.length -1; i ++){
             
          for(int j = i + 1; j < families.length; j ++){
              
              
          if(families[i].getIncomePerYear() > families[j].getIncomePerYear()){
               temp 
          = families[i];
               families[i] 
          = families[j];
               families[j] 
          = temp;
              }

             }

            }

            
           }

           
          public static void sortByIncomePerMember(FamilyInfo[] families){
            FamilyInfo temp 
          = null;
            
          for(int i = 0; i < families.length -1; i ++){
             
          for(int j = i + 1; j < families.length; j ++){
              
              
          if(families[i].getIncomePerMember() > families[j].getIncomePerMember()){
               temp 
          = families[i];
               families[i] 
          = families[j];
               families[j] 
          = temp;
              }

             }

            }

            
            
           }

           
          //在framework每次啟動該bundle的時候該方法會被framework調(diào)用執(zhí)行。
           public void start(BundleContext context) throws Exception {
            FamilyInfo[] families 
          = new FamilyInfo[3];
            families[
          0= new FamilyInfo();
            families[
          0].setFamilyName("Zhang");
            families[
          0].setPopulation(3);
            families[
          0].setIncomePerYear(1200);
            families[
          1= new FamilyInfo();
            families[
          1].setFamilyName("Li");
            families[
          1].setPopulation(6);
            families[
          1].setIncomePerYear(1800);
            families[
          2= new FamilyInfo();
            families[
          2].setFamilyName("Liu");
            families[
          2].setPopulation(4);
            families[
          2].setIncomePerYear(1500);
            FamilyInfo.sortByIncomePerYear(families);
            
          for(int i = 0; i < families.length; i ++){
             System.out.println(families[i].toString());
            }

            FamilyInfo.sortByIncomePerMember(families);
            
          for(int i = 0; i < families.length; i ++){
             System.out.println(families[i].toString());
            }

           }

           
          //在framework停止該bundle時,該方法將被framework調(diào)用
           public void stop(BundleContext context) throws Exception {
           }

          }


          看到代碼的區(qū)別了嗎?我在不同之處都標(biāo)注了注釋。其實,從說白了,就是實現(xiàn)了org.osgi.framework.BundleActivator這個接口。
          當(dāng)然,細(xì)心的話,你會發(fā)現(xiàn)這個bundle沒有public static void main(String[] args)方法了。那么它怎么被啟動呢?這個就是bundle的奧秘所在。不過,如果你了解java的class loading機(jī)制以及reflection技術(shù),你立馬會明白這個bundle的運(yùn)行機(jī)制。這兩項技術(shù)廣泛應(yīng)用于j2ee(對吧?我得承認(rèn),j2ee的經(jīng)驗不多,呵呵)以及java的plugin機(jī)制。
          簡單說來,java.lang.Class這個類有一個方法:
          public Object newInstance()throws InstantiationException,IllegalAccessException
          針對上面的“扶貧助手”bundle而言,framework只要通過ClassLoader找到aa.bb.cc.FamilyInfo.class并加載后,就可以通過newInstance()方法創(chuàng)建一個BundleActivator的實例,然后調(diào)用public void start(BundleContext context)方法,就完成了啟動bundle的動作了。之后,調(diào)用public
          void stop(BundleContext context)方法來停止bundle
          如果你接著問,framework怎么知道這個bundle里面的BundleActivator是哪個類呢?嗯,問到點子上了。這就涉及到下面我們要講的bundle的部署了。在上一篇給出的bundle定義中指出,Jar文件是bundle的唯一格式,也就是說,我們要運(yùn)行bundle,必須把代碼打成jar文件。而jar文件可以帶有manifest文件,這個文件對bundle是不可缺少的。OSGi規(guī)范里面,通過定義一系列適用于bundle的manifest關(guān)鍵字(bundle manifest header)來擴(kuò)展manifest文件。
          比如,開發(fā)人員在manifest中添加下面一行:
          Bundle-Activator: aa.bb.cc.FamilyInfo
          這樣,在bundle被部署到framework后,framework就可以通過讀取manifest的關(guān)鍵字來獲得BundleActivator的具體實現(xiàn)類名,并通過reflection機(jī)制產(chǎn)生BundleActivator的實例。
          這里就給出扶貧助手的manifest的一個例子:

          Manifest-Version: 1.0  
          Bundle-SymbolicName: aa.bb.cc.family //osgi specification 4強(qiáng)制要求的關(guān)鍵字,每個bundle都必須有唯一的symbolic name
          Bundle-Name: Family Info Manager        //bundle的名稱
          Bundle-Version: 
          1.0   //bundle的版本號
          Bundle-Activator: aa.bb.cc.FamilyInfo   //指明BundleActivator的實現(xiàn)類名
          Import-Package: org.osgi.framework
          ;version=1.3   //列出該bundle需要從其他bundle所引入的
                                                                               //package(s)(提供該package的bundle必須在其
                                                                               //manifest中有Export-Package: 
                                                                               //org.osgi.framework
          ;version=1.3)

          然后我們用jdk自帶的jar工具,來生成bundle jar文件。這樣,第一個bundle就完成了,您可以下載一個開源的framework安裝這個bundle試一試。在framework上嘗試對該bundle的啟動和停止,輸出的結(jié)果應(yīng)該和原先的java application是一樣的,然后您還可以在那個start(context)的方法中,再增加一條記錄,重新打包,然后通過framework的update功能,就能夠在不重新啟動framework的情況下升級該bundle,我就暫時偷懶不針對具體framework來給出操作的方法了,先給您自己先摸索了(當(dāng)然您也可以偷懶,因為后面我會結(jié)合具體framework深入講述的)。
          好了,說完代碼的改造,再看看改造所帶來的程序設(shè)計結(jié)構(gòu)變化:那~~~就~~~~是~~~~沒變化!因此我把這種原封不動的改造方法稱為“直接型”,用這種直接法,我們可以輕易的把一個java應(yīng)用程序改造成bundle。而這種改造目前能看到的好處就是bundle的“熱”升級。那怎樣能更漂亮些呢?在下一篇中,我會進(jìn)一步改造這個扶貧助手成為兩個bundle,看看bundle的合作將會帶來怎樣的精彩效果

          posted @ 2006-02-14 15:46 勤勞的蜜蜂 閱讀(5538) | 評論 (4)編輯 收藏

          OSGi介紹(三)OSGi service platform的體系結(jié)構(gòu)

          先讓我們來看看OSGi service platform的體系結(jié)構(gòu)。另外要說明的是,我在后面的文章中,將采用framework來代替OSGi service platfrom,這樣比較簡便。
          下面這張圖來自O(shè)SGi Alliance的主頁(http://www.osgi.org/
           
          OSGi Service Platform Architecture

          層次很分明吧。放到我們假想的案例中,OS&Hardware可以對應(yīng)為PDA的硬件和操作系統(tǒng),您可以想象它是Intel xscacle + Microsoft window mobile,或者是Arm + embedded Linux
          而Execution Environment當(dāng)然是我們上次提到的CVM + CDC + FP + PP,有這個jvm的配置運(yùn)行framework就綽綽有余了。而再往上,就是我們要重點學(xué)習(xí)和分析的OSGi framework了。而Modules, Life Cycle, Service Registry, Services和Security是從不同的5個角度來劃分framework所具備的功能,后面我將會從除了Security外的四個方面分別結(jié)合我們的假設(shè)場景來分析。而體系結(jié)構(gòu)的最上層是符合OSGi framework接口標(biāo)準(zhǔn)的應(yīng)用程序,也就是OSGi世界中有名的“bundle”。

          下面來看看OSGi規(guī)范是如何定義一個bundle的。在r4規(guī)范的第27頁中大致這樣描述到:Framework定義了模塊(modularization)的單元,叫作bundle。Bundle實際就是一個具有jar(Java ARchive)格式的文件,其中包含了java的class文件和其他資源文件(比如圖標(biāo),配置文件等等)。Bundle可以在自己的manifest文件中說明自己能夠提供哪些java包,其他bundle如果在自己的manifest文件中指定了它需要這個包,那他們之間就可能產(chǎn)生java包的依賴關(guān)系,這樣多個bundle之間就可以共享java包。值得注意的是,bundle是能夠在framework中部署的唯一格式。下面給出原文的描述:
          A bundle is a JAR file that:
          ? Contains the resources necessary to provide some functionality. These resources may be class files for the Java programming language, as well as other data such as HTML files, help files, icons, and so on. A bundle JAR file can also embed additional JAR files that are available as resources and classes. This is however not recursive.
          ? Contains a manifest file describing the contents of the JAR file and providing information about the bundle. This file uses headers to specify information that the Framework needs to install correctly and activate a bundle. For example, it states dependencies on other resources, such as Java packages, that must be available to the bundle before it can run.
          ? Can contain optional documentation in the OSGI-OPT directory of the JAR file or one of its sub-directories. Any information in this directory is optional. For example, the OSGI-OPT directory is useful to store the source code of a bundle. Management systems may remove this information to save storage space in the OSGi Service Platform.

          framework的modules這一方面功能將主要負(fù)責(zé)bundle的安裝部署,更新和卸載,以及bundle在設(shè)備的物理存儲(如果有的話)。在這個層次,每個bundle都是獨立的,它的安裝,升級和卸載完全不依賴任何其他bundle,這點framework提供了強(qiáng)大的隔離性。Life Cycle專門負(fù)責(zé)對bundle的解析(比如關(guān)聯(lián)兩個有相互依賴關(guān)系的bundle),啟動(相當(dāng)于運(yùn)行應(yīng)用程序)和停止(相當(dāng)于停止應(yīng)用程序)。這個層次中,bundle間的邏輯關(guān)系被創(chuàng)建起來,這些關(guān)系能否成功的創(chuàng)建,將會直接影響bundle的成功解析和啟動。Service Registry可以認(rèn)為是一個數(shù)據(jù)庫,bundle啟動后,可以向這個數(shù)據(jù)庫注冊它動態(tài)提供的服務(wù)。只要bundle不被停止,且bundle不主動撤銷注冊的服務(wù),這個服務(wù)將一直保存在這個數(shù)據(jù)庫中供其它bundle來查詢和使用。而Services就是由bundle運(yùn)行時提供的具體服務(wù)對象,這些服務(wù)對象的存在,使得framework具有極其動態(tài)的特征,并為framework運(yùn)行時提供靈活強(qiáng)大的功能。
          另外,根據(jù)OSGi Alliance的說法,OSGi的運(yùn)行平臺包括了j2me(kvm + cldc + midp, cvm + cdc+fp), j2se, j2ee。不過,我個人還是覺得目前的midp規(guī)范還太弱,OSGi要想運(yùn)行在上面,很多功能實現(xiàn)起來都比較不爽。

          好,有了對framework結(jié)構(gòu)層次的皮毛認(rèn)識,下面我們就開始著手改造那個“扶貧助手”的程序,使其變成OSGi的bundle(s),然后從上面提到的4個方面來分析framework的機(jī)制。
          這里,我先給出“扶貧助手”的java application模擬程序代碼:

          package aa.bb.cc;

          public class FamilyInfo {
           
          private String familyName; //家庭名稱
           private int population; //人口數(shù)量
           private int incomePerYear; //家庭年收入

            …..
          //省略了getter和setter方法

          //獲得家庭人均年收入
           public int getIncomePerMember(){
            
          return (int)(this.incomePerYear/this.population);
           }


           
          public String toString() {
            
            
          return "Family: " + this.familyName + ", population: " + this.population + ", income: " + this.incomePerYear;
           }

           
          //按家庭年收入又低到高排序
           public static void sortByIncomePerYear(FamilyInfo[] families){
            FamilyInfo temp 
          = null;
            
          for(int i = 0; i < families.length -1; i ++){
             
          for(int j = i + 1; j < families.length; j ++){
              
              
          if(families[i].getIncomePerYear() > families[j].getIncomePerYear()){
               temp 
          = families[i];
               families[i] 
          = families[j];
               families[j] 
          = temp;
              }

             }

            }

            
           }


           
          //按家庭人均年收入由低到高排序
           public static void sortByIncomePerMember(FamilyInfo[] families){
            FamilyInfo temp 
          = null;
            
          for(int i = 0; i < families.length -1; i ++){
             
          for(int j = i + 1; j < families.length; j ++){
              
              
          if(families[i].getIncomePerMember() > families[j].getIncomePerMember()){
               temp 
          = families[i];
               families[i] 
          = families[j];
               families[j] 
          = temp;
              }

             }

            }

            
           }


           
          public static void main(String[] args){
            FamilyInfo[] families 
          = new FamilyInfo[3];
            families[
          0= new FamilyInfo();
            families[
          0].setFamilyName("Zhang");
            families[
          0].setPopulation(3);
            families[
          0].setIncomePerYear(1200);
            families[
          1= new FamilyInfo();
            families[
          1].setFamilyName("Li");
            families[
          1].setPopulation(6);
            families[
          1].setIncomePerYear(1800);
            families[
          2= new FamilyInfo();
            families[
          2].setFamilyName("Liu");
            families[
          2].setPopulation(4);
            families[
          2].setIncomePerYear(1500);
            FamilyInfo.sortByIncomePerYear(families);
            
          for(int i = 0; i < families.length; i ++){
             System.out.println(families[i].toString());
            }

            FamilyInfo.sortByIncomePerMember(families);
            
          for(int i = 0; i < families.length; i ++){
             System.out.println(families[i].toString());
            }

            
           }

          }


           

          posted @ 2006-02-14 15:42 勤勞的蜜蜂 閱讀(5178) | 評論 (3)編輯 收藏

          osgi介紹(二)一個假想的實例

          如何分析OSGi service platform的機(jī)制?給出幾個硬生生的例子,然后分析一下
          代碼?那還不如你自己看書看規(guī)范好了。因此,我覺得還是結(jié)合一個應(yīng)用實例來分析會更
          容易理解,當(dāng)然,是一個假想的應(yīng)用實例。用怎樣一個實例呢?嗯......

          幾個月前,一個中學(xué)同學(xué)打電話給我說他們要在PDA上開發(fā)一個簡單的應(yīng)用程序來臨時紀(jì)
          錄工作的結(jié)果,并向我咨詢,這種開發(fā)的難度和周期。這事啟發(fā)我了,就以PDA的應(yīng)用為
          背景,讓我們來假想一個場景,從而來比較傳統(tǒng)的應(yīng)用模型與采用OSGi的應(yīng)用模型有怎樣
          的區(qū)別。

          我這樣想象:
          小李是一個軟件工程師,在一家專門為PDA開發(fā)應(yīng)用程序和解決方案的公司工作。最近,
          他剛為公司的一個客戶開發(fā)完成了一套運(yùn)行在PDA的JAVA應(yīng)用程序,我們不要關(guān)心PDA是
          什么硬件配置,只要知道它配備了JVM(cvm) + CDC以及PP和文件系統(tǒng)(呵呵設(shè)備還是比較
          強(qiáng)勁的)。而這個客戶是一個慈善機(jī)構(gòu),該機(jī)構(gòu)人員攜帶PDA進(jìn)入偏遠(yuǎn)山區(qū)收集生活困難
          家庭的信息,以準(zhǔn)備進(jìn)行資助。而這套程序?qū)簳r把家庭信息保存在PDA中,并隨時供
          用戶查詢修改。用戶使用一個月后,反饋非常好,但是,他們有新需求了,說原來只是想紀(jì)錄
          信息就成了,現(xiàn)在希望能給出一些排序功能,比如按家庭年收入對紀(jì)錄進(jìn)行排序.

          接到這個需求,小李一看,這個簡單,只要增加一個排序方法就可以了,讓我們假設(shè)他使用了如下
          數(shù)據(jù)結(jié)構(gòu)來紀(jì)錄家庭信息:

          Class FamilyInfo {

           
          private String familyName;//家庭名稱

           
          private int population; //人口數(shù)量

           
          private int incomePerYear; //年收入

           .(省略Getter和Setter方法)
          }


           

          為了滿足這個需求,小李決定添加一個靜態(tài)的排序方法:

          public static FamilyInfo[] sortByIncomePerYear(FamilyInfo[] familyInfos){
           
          //根據(jù)incomePerYear的值進(jìn)行冒泡排序。
          }



          把相關(guān)連部分修改完畢后,小李重新制作了安裝包和啟動腳本,發(fā)送給客戶,不管客戶如何操作
          總之,原來的PDA程序必須卸載,新程序必須拷貝到PDA上再次執(zhí)行安裝,重新啟動運(yùn)行。

          又過了一陣,客戶說,要求提供按人均年收入進(jìn)行排序,然后同樣的事情又發(fā)生了......

          幾個輪次下來,小李發(fā)現(xiàn),客戶的需求還在增加,他們可能要求增加一個字段,記錄目前該
          家庭得到的資助額,還可能添加按收入范圍查詢紀(jì)錄等等,事情還遠(yuǎn)沒有結(jié)束。

          如何改進(jìn)這個情況呢?當(dāng)然,改進(jìn)涉及多方面,比如從軟件本身出發(fā),可以使用合適的design
          pattern重新設(shè)計程序的體系結(jié)構(gòu),使得程序更易于擴(kuò)展,關(guān)于這一點,有太多的討論了,我就不
          摻和了。還有從部署方面說,配置,安裝和卸載程序,對最終用戶往往是一項mission impossible,
          能否讓應(yīng)用程序自己升級,而用戶只要點擊一個"升級"來觸發(fā)這個過程而已......

          我想你當(dāng)然知道我給的答案:OSGi,OSGi,OSGi!!!!

          posted @ 2006-02-14 15:39 勤勞的蜜蜂 閱讀(4688) | 評論 (3)編輯 收藏

          osgi介紹(一)什么是osgi

          過于的一年多,在和很多it屆的同學(xué)及朋友見面時,他們總會問我最近在做什么?!癘SGi!”,我不加思索的回答。到目前為止,對這個單詞得到的反應(yīng)都沒有超出“這是什么?”,“我沒有聽說過”,“噢,能具體點嗎?”等等。而我的回答更讓他們糊涂,最后,大家干脆放棄這個話題,轉(zhuǎn)到買房,運(yùn)動等等更能體現(xiàn)聚會實質(zhì)的問題上。不過最近,我一直在思考這個問題,下次再遇到這種情況時,該如何去表達(dá)才能讓也是it屆的哥們姐們能迅速的理解這個領(lǐng)域的范圍呢?要知道,技術(shù)人員往往不善于表達(dá),我們已經(jīng)習(xí)慣了和業(yè)內(nèi)人士用行話交流。

          關(guān)于這個問題,我訪問了OSGi Alliance的網(wǎng)站,在里面的faqs中,找到了我想要的東西。實際上,正如faqs中所解答的,OSGi涵蓋了太多的范圍,簡單的兩三句話是無法說清楚的。而我這里指的OSGi從技術(shù)的角度,應(yīng)該說是“OSGi service platform ”,faqs中這樣解釋OSGi service platform(http://www.osgi.org/about/faqs.asp?section=1#q19) :
          The OSGi service platform delivers an open, common architecture for service providers, developers, software vendors, gateway operators and equipment vendors to develop, deploy and manage services in a coordinated fashion. .......(以下省略上千英文單詞)

          好長!不過第一句話就已經(jīng)能總結(jié)陳詞了,“OSGi service platform是一個開放并且提供統(tǒng)一接口標(biāo)準(zhǔn)的體系框架,基于這個體系框架,服務(wù)提供商,程序開發(fā)人員,軟件提供商,服務(wù)網(wǎng)管運(yùn)營商,設(shè)備提供商能夠協(xié)調(diào)地聯(lián)合起來開發(fā),部署以及管理向用戶提供的各種服務(wù)。”還需要提到的是OSGi service platform是一個基于Java的platform。

          OSGi的提出和誕生之初,其目的主要是能夠靈活方便并遠(yuǎn)程管理互聯(lián)的網(wǎng)絡(luò)嵌入設(shè)備(聽說是1997年左右提出,與Jini有深厚淵源)。隨著硬件設(shè)備的能力不斷提高,java技術(shù)的日益普及,尤其J2ME的壯大,現(xiàn)實應(yīng)用的需求也不斷擴(kuò)大和推進(jìn),一個統(tǒng)一的標(biāo)準(zhǔn)變得非常的必要。OSGi Alliance就在這樣的背景下成立了。從1999年成立以來,OSGi Alliance已經(jīng)針對這個service platform發(fā)布了4版規(guī)范,其中r4是2005年10月份剛剛發(fā)布。

          目前有不少公司對OSGi service platform推出了自己的實現(xiàn),象ibm的smf(Service Management Framework,嗯,多好的名字,在那么多的platform實現(xiàn)中,我個人最喜歡這個名字,言簡意賅)。

          德國的ProSyst公司(http://www.prosyst.com)是OSGi Alliance中非常活躍的推動者,看看他們的產(chǎn)品列表吧http://www.prosyst.com/products/osgi.html(他們甚至提供了kvm + cldc的OSGi framework)

          開源的Oscar(http://oscar.objectweb.org/),Knopflerfish(http://www.knopflerfish.org/)

          對于OSGi的成功應(yīng)用,最有名的應(yīng)該是eclipse了,它就是基于OSGi service platform的產(chǎn)品。還有Apache,據(jù)說OSGi將被應(yīng)用于其新一代的build工具中。這些都是j2se和j2ee的應(yīng)用,而基于j2me的,手機(jī)(對應(yīng)OSGi Alliance的MEG)和車載設(shè)備(對應(yīng)OSGi Alliance的VEG)是OSGi的主要領(lǐng)域,OSGi Alliance已經(jīng)有相應(yīng)的規(guī)范,這些領(lǐng)域的應(yīng)用相信會更加精彩,讓我們拭目以待吧。

          posted @ 2006-02-14 15:32 勤勞的蜜蜂 閱讀(23153) | 評論 (10)編輯 收藏

          主站蜘蛛池模板: 贵南县| 陇川县| 邓州市| 界首市| 白水县| 株洲县| 武平县| 永州市| 玉溪市| 龙泉市| 扎囊县| 海宁市| 金山区| 田东县| 平远县| 社旗县| 安仁县| 嘉义县| 巍山| 安塞县| 铜梁县| 禹州市| 高唐县| 淮南市| 大兴区| 曲靖市| 凌源市| 镇安县| 铁岭县| 威信县| 鲁山县| 阿坝县| 承德县| 稻城县| 晋州市| 定州市| 海宁市| 灌云县| 珠海市| 徐闻县| 施秉县|