摘 要: 為了說(shuō)明BREW中如何實(shí)現(xiàn)用C語(yǔ)言來(lái)模擬C++中的面向?qū)ο?/a>的特性,,實(shí)現(xiàn)接口的聲明與實(shí)現(xiàn)的分離,、對(duì)多個(gè)接口的支持和接口的易擴(kuò)展性。通過(guò)實(shí)例,,闡述了BREW通過(guò)虛擬函數(shù)表將接口與實(shí)現(xiàn)分離,,使用ISHELL接口對(duì)多個(gè)接口支持及擴(kuò)展。該方法與普通的C語(yǔ)言實(shí)現(xiàn)的接口相比較,,修改接口而不會(huì)影響到應(yīng)用程序,,而接口有更好的可擴(kuò)展性,更容易管理,。
關(guān)鍵詞: BREW,;接口;面向?qū)ο?/p>
如今,,手機(jī)已不是簡(jiǎn)單的語(yǔ)音通信工具,,它已逐漸發(fā)展成為數(shù)據(jù)業(yè)務(wù)開(kāi)發(fā)應(yīng)用的平臺(tái)。在這種情況下,,高通公司推出了一個(gè)新的BREW(Binary Runtime Environment for Wireless)平臺(tái),。BREW平臺(tái)的出現(xiàn)使手機(jī)像電腦一樣,可以應(yīng)用更多的第三方軟件,,滿足人們不同的需求,,為用戶提供更多的服務(wù)。
在手機(jī)軟件開(kāi)發(fā)中,許多問(wèn)題都需要使用C++中面向?qū)ο蟮姆椒▉?lái)實(shí)現(xiàn),,以提高代碼的可重用性,、程序的模塊化及健壯性;為了滿足用戶對(duì)新數(shù)據(jù)應(yīng)用的需求,,移動(dòng)設(shè)備制造商也希望不重新開(kāi)發(fā)專有的軟件平臺(tái),,就可以快速提供新業(yè)務(wù),以降低移動(dòng)設(shè)備的技術(shù)門(mén)檻和產(chǎn)品上市門(mén)檻,。
高通公司推出的BREW平臺(tái),,為無(wú)線設(shè)備設(shè)計(jì)專門(mén)提供了一個(gè)高效的應(yīng)用程序執(zhí)行環(huán)境及開(kāi)發(fā)平臺(tái)。在BREW平臺(tái),,用C語(yǔ)言開(kāi)發(fā)應(yīng)用程序可以達(dá)到C++的設(shè)計(jì)效果,,而不需要開(kāi)發(fā)專用的軟件平臺(tái),又提高了程序開(kāi)發(fā)的效率和新業(yè)務(wù)開(kāi)發(fā)的速度,。
本文通過(guò)一個(gè)例子,,闡述BREW中如何實(shí)現(xiàn)接口的聲明和實(shí)現(xiàn)的分離來(lái)達(dá)到面向?qū)ο蟮奶匦裕岣叽a的可重用性,、健壯性以及其可擴(kuò)展性和易于管理的特性,。
1 BREW接口的實(shí)現(xiàn)
1.1 BREW平臺(tái)簡(jiǎn)介
BREW 的全稱是無(wú)線二進(jìn)制運(yùn)行環(huán)境。從基本的層面而言,,BREW 平臺(tái)就是手持設(shè)備上嵌入式芯片操作系統(tǒng)的接口或抽象層,,可以將它看作是 PC 環(huán)境下 Microsoft Windows 的 Win32 API,。BREW 平臺(tái)是一組用于本地執(zhí)行而編譯并鏈接的二進(jìn)制庫(kù),,優(yōu)化后能使應(yīng)用程序利用無(wú)線服務(wù)和資源,控制流出或流入應(yīng)用程序的事件流,,能根據(jù)相應(yīng)的事件啟動(dòng),、停止,、中止或恢復(fù)應(yīng)用程序。BREW 執(zhí)行環(huán)境在運(yùn)行時(shí)可以發(fā)現(xiàn)應(yīng)用程序和任何相關(guān)的擴(kuò)展[1],。
1.2 嵌入式系統(tǒng)結(jié)構(gòu)
圖1是分散式系統(tǒng)結(jié)構(gòu)圖。從圖1中可以看出,,SDK需要使用運(yùn)行平臺(tái)的接口聲明來(lái)開(kāi)發(fā)應(yīng)用程序,,運(yùn)行平臺(tái)負(fù)責(zé)根據(jù)用戶的輸入啟動(dòng)應(yīng)用程序,而應(yīng)用程序則通過(guò)運(yùn)行平臺(tái)的接口調(diào)用運(yùn)行平臺(tái)的函數(shù)庫(kù)來(lái)實(shí)現(xiàn)功能[2],。
假設(shè)程序運(yùn)行到了需要調(diào)用平臺(tái)函數(shù)的時(shí)候,,由于當(dāng)前的應(yīng)用程序是開(kāi)發(fā)者使用SDK開(kāi)發(fā)的,就像平臺(tái)不知道應(yīng)用程序的地址一樣,,應(yīng)用程序也不知道平臺(tái)函數(shù)的地址,,因此,所面臨的問(wèn)題就是怎樣能夠知道應(yīng)用程序中所調(diào)用的平臺(tái)函數(shù)的地址。雖然SDK中可以提供運(yùn)行平臺(tái)中每個(gè)函數(shù)的地址,,但是因?yàn)槠脚_(tái)會(huì)經(jīng)常升級(jí),,導(dǎo)致每個(gè)函數(shù)的鏈接地址不固定,因此在平臺(tái)升級(jí)時(shí),,SDK和應(yīng)用程序都需要同時(shí)升級(jí),。這樣就不能實(shí)現(xiàn)“分散式”的升級(jí)了,這種程序的運(yùn)行方式也就沒(méi)有任何意義了,。為解決分散式系統(tǒng)分散式升級(jí)的問(wèn)題,,BREW提供了一個(gè)機(jī)制來(lái)解決應(yīng)用程序調(diào)用函數(shù)的問(wèn)題。
如果在開(kāi)發(fā)過(guò)程中使用運(yùn)行平臺(tái)的接口聲明,,而在運(yùn)行時(shí)應(yīng)用程序使用真正的二進(jìn)制接口,,并在二進(jìn)制層面調(diào)用接口函數(shù)[3]。則無(wú)論是SDK還是應(yīng)用程序都與接口相關(guān),,解決這個(gè)問(wèn)題的方式就是讓接口和接口的實(shí)現(xiàn)之間分離,。下面介紹BREW中的接口是如何實(shí)現(xiàn)的。
1.3 軟件開(kāi)發(fā)和C語(yǔ)言
在C語(yǔ)言中,, C語(yǔ)言庫(kù)的開(kāi)發(fā)商開(kāi)發(fā)了一個(gè)算法來(lái)實(shí)現(xiàn)字符串的搜索,,為了實(shí)現(xiàn)這個(gè)功能,軟件廠商生成了一個(gè)頭文件FastString.h[4],,內(nèi)容如下:
typedef struct _IFastString
{
char *m_pString,;
}IFastString;
void IFastString_CreateObject(IFastString *IFastString,, char *pStr),;//創(chuàng)建目標(biāo)字符串對(duì)象
void IFastString_Release(IFastString *IFastString);
//釋放目標(biāo)字符串對(duì)象
int IFastString_GetLength(IFastString *pIFastString),;
//獲取目標(biāo)字符串長(zhǎng)度
int IFastString_Search(IFastString *IFastString,, char *pSearchStr);//查找字符串,,返回偏移量
在BREW接口中共有4個(gè)接口:CreateObject,、Release、GetLength,、和Search,。CreateObject用來(lái)創(chuàng)建IFastString接口,Release用來(lái)釋放接口的資源,,GetLength用來(lái)獲取字符串長(zhǎng)度,,Search用來(lái)查找字符串。一般地,,這個(gè)庫(kù)的使用者會(huì)將.lib庫(kù)鏈接到自己的工程中,,通過(guò)接口聲明的頭文件來(lái)使用庫(kù)中的函數(shù)。這樣,庫(kù)函數(shù)將成為客戶應(yīng)用程序中的一部分,。
假設(shè)FastString庫(kù)占用了1 MB的空間,,如果4個(gè)程序中都調(diào)用了這個(gè)接口,則FastString接口將會(huì)占用4 MB的空間,,也就是說(shuō)有3 MB的空間浪費(fèi)掉子,。圖2是多個(gè)程序調(diào)用FastString庫(kù)的示意圖。另外,,如果庫(kù)廠商發(fā)現(xiàn)接口有缺陷,,又沒(méi)有辦法替換已經(jīng)存在的缺陷代碼,一旦FastString接口鏈接到代碼中,,就不可能在用戶設(shè)備上替換這部分代碼,。因此,庫(kù)廠商不得不重新為每個(gè)應(yīng)用程序的開(kāi)發(fā)者廣播發(fā)布新的庫(kù)文件,,并希望他們重新編譯程序來(lái)使用新的代碼,。這在嵌入式系統(tǒng)中是不可能的。因?yàn)樵谶@里FastString的角色就是運(yùn)行平臺(tái),,不可能每個(gè)應(yīng)用程序都包含一個(gè)運(yùn)行平臺(tái),。解決這個(gè)問(wèn)題的一種技術(shù)是使用動(dòng)態(tài)鏈接庫(kù)技術(shù)將FastString包含起來(lái)。
1.4 動(dòng)態(tài)鏈接庫(kù)
動(dòng)態(tài)鏈接庫(kù)技術(shù)的典型應(yīng)用是Windows操作系統(tǒng)中的動(dòng)態(tài)鏈接庫(kù)[4],。這種方法是將FastString源文件編譯成特殊的二進(jìn)制文件,,并強(qiáng)迫FastString將所有的接口從二進(jìn)制文件中引用出去,建立相應(yīng)的引出表,,以便于在運(yùn)行時(shí)把每個(gè)接口的名字映射到對(duì)應(yīng)的二進(jìn)制接口地址上,。同時(shí)還需要為使用者生成相應(yīng)的引入庫(kù),使用者通過(guò)引入庫(kù)可以獲取每個(gè)接口的符號(hào),。當(dāng)客戶鏈接引入庫(kù)時(shí),,這些符號(hào)信息會(huì)加入到當(dāng)前的可執(zhí)行文件中,運(yùn)行時(shí)動(dòng)態(tài)加載二進(jìn)制庫(kù)文件,,并在執(zhí)行時(shí)調(diào)用相應(yīng)的程序,。這樣,即使多個(gè)程序調(diào)用FastString接口,,F(xiàn)astString代碼也只需要一份,,而且如果發(fā)現(xiàn)FastString代碼有缺陷時(shí),可以更新FastString二進(jìn)制組件而不影響應(yīng)用程序,。圖3是使用動(dòng)態(tài)鏈接庫(kù)時(shí)多個(gè)程序調(diào)用FastString庫(kù)的示意圖。
1.5 虛擬函數(shù)表
但是在嵌入式系統(tǒng)中一般不支持動(dòng)態(tài)鏈接庫(kù)技術(shù),,因此在BREW中,,采用虛擬函數(shù)表(VTBL)技術(shù)[4]實(shí)現(xiàn)接口和接口的分離。
下面是新版本的FastString的頭文件:
typedef struct _IFastString IFastString;
typedef struct _IFastStringVtbl IFastStringVtbl,;
typedef struct (*PFNCreateObject)
(IFastString **ppIFastString,, char *pStr);
struct _IFastString
{
struct IFastStringVtbl *pvt,;
},;
struct IFastStringVtbl
{
void(*Release)(IFastString*pIFastString);
int(*GetLength)(IFastString*pIFastString),;
int(*Search)(IFastString*pIFastString,,char *pSearchStr);
},;
#define IFASTSTRING_Release(p)((IFastString*)p->pvt)->Release(p) //釋放目標(biāo)字符串對(duì)象
#define IFASTSTRING_GetLength(p)((IFastString*)p->pvt)->GetLength(p) //獲取目標(biāo)字符串長(zhǎng)度
#define IFASTSTRING_Search(p)((IFastString*)p->pvt)->Search(p) //查找字符串,,返回偏移量
首先對(duì)FastString程序作一說(shuō)明:在FastString頭文件里定義了IFastString和IFastStringVtbl兩個(gè)類型。IFastStringVtbl類型是虛擬函數(shù)表類型,,IFastString中包含了指向虛擬函數(shù)表類型的指針,。在接口定義時(shí),使用((IFastString)p->pvt)調(diào)用虛擬函數(shù)表中的函數(shù)指針,,這說(shuō)明了如果要使用接口就必須先要提供IFastString的指針類型,。可以看出,,Release,、GetLength和Find已經(jīng)實(shí)現(xiàn)了在C語(yǔ)言定義的接口和實(shí)現(xiàn)函數(shù)之間的分離。
源文件FastString.c如下:
#include“FastString.h”
#include<string.h>
typedef struct _CFastString
{
IFastStringVtbl *pvt,;//指向虛擬函數(shù)表的指針
char *m_pString,;//指向字符串的指針
int m_Len;//存儲(chǔ)字符串的長(zhǎng)度
}CFastString,;
//函數(shù)聲明
static void IFASTSTRING_Release(IFastString*pIFastString),;
static void IFASTSTRING_GetLength(IFastString*pIFastString);
static void IFASTSTRING_Search(IFastString*pIFastString,,char*pSearchStr),;
IFastStringVtbl gvtFastString = {IFASTSTRING_Release,
IFASTSTRING_GetLength,,
IFASTSTRING_Search
},;
void IFastString_CreateObject(...)
{...}
...
CFastString結(jié)構(gòu)體中有IFastStringVtbl類型的指針,而且這個(gè)指針在結(jié)構(gòu)體的最頂部,。另外還發(fā)現(xiàn)在IFastString結(jié)構(gòu)體的最頂部也包含了IFastStringVtbl的指針,,即CFastString是IFastString的超集。從而可以看出,,CreateObject函數(shù)中返回的IFastString指針其實(shí)是指向CFastString的指針,。在FastString.C源文件中還定義了一個(gè)IFastStringVtbl的變量gvtFastString,,并為這個(gè)變量初始化成各個(gè)對(duì)應(yīng)的函數(shù),這個(gè)變量就是虛擬函數(shù)表,。虛擬函數(shù)表的示意圖如圖4所示,。
可以發(fā)現(xiàn),除了CreateObject成員之外,,其余的三個(gè)成員函數(shù)(IFastString_Release,、IFastString_GetLength、IFastString_Find)都添加到了虛擬函數(shù)表中,,而且這個(gè)虛擬函數(shù)表還可以隨著需求的增加而進(jìn)行無(wú)限擴(kuò)大,,這樣只用了一個(gè)函數(shù)CreateObject就實(shí)現(xiàn)了無(wú)限多個(gè)接口與實(shí)現(xiàn)之間的分離。
由于用戶需要使用CreateObject來(lái)獲取IFastString指針,,因此將其與實(shí)現(xiàn)分離的方法是:因?yàn)閼?yīng)用程序的啟動(dòng)過(guò)程,,對(duì)于一個(gè)程序,無(wú)論是由main函數(shù)或者其他函數(shù)作為啟動(dòng)函數(shù),,都允許啟動(dòng)時(shí)傳遞參數(shù),,因此把這個(gè)CreateObject函數(shù)作為參數(shù)傳遞給應(yīng)用程序就可以了。至此應(yīng)用程序,、接口和實(shí)現(xiàn)之間已經(jīng)分離了,。
1.6 支持多個(gè)接口和接口的擴(kuò)展性
實(shí)現(xiàn)了接口和實(shí)現(xiàn)之間的分離,但一個(gè)平臺(tái)不會(huì)只有一個(gè)接口,,還包含了其他用途的接口,。但又不能把所有接口的CreateObject作為參數(shù)傳遞給應(yīng)用程序啟動(dòng)函數(shù),所以要對(duì)現(xiàn)有接口進(jìn)行擴(kuò)展來(lái)實(shí)現(xiàn)只要傳遞一個(gè)參數(shù)就能創(chuàng)建多個(gè)接口的功能,。就像設(shè)計(jì)模式里面的工廠模式,,創(chuàng)建一個(gè)專門(mén)的類用來(lái)創(chuàng)建別的類。因此,,本設(shè)計(jì)采用增加一個(gè)稱為Shell的接口來(lái)管理其他接口,。
在Shell接口中,定義了CreateInstance接口函數(shù),,其代碼如下:
static void IShell_CreateInstance(IShell *pIShell,, int nClassID, void **ppObj,, unsigned int nUserData)
{
...
switch(nClassID)
{
case CLASSID_FASTATRING:
IFastString_CreateObject((IFastString**)ppObj,,(char *)nUserData);
break,;
case ...
...
...
}
}
其作用是通過(guò)參數(shù)nClassID來(lái)創(chuàng)建指定的接口實(shí)例,。IShell_CreateObject函數(shù)用來(lái)創(chuàng)建Shell接口本身,然后再通過(guò)Shell接口來(lái)創(chuàng)建其他接口,。也就是說(shuō),,在應(yīng)用程序啟動(dòng)時(shí),,先創(chuàng)建Shell接口,然后通過(guò)IShell_CreateInstance來(lái)創(chuàng)建其他接口,。這樣,不但實(shí)現(xiàn)了接口的管理工作,,而且方便了接口的擴(kuò)展,。
本文通過(guò)一個(gè)例子介紹了BREW中接口的實(shí)現(xiàn),說(shuō)明使用虛擬函數(shù)表能夠?qū)崿F(xiàn)接口的聲明與實(shí)現(xiàn)的分離,,從而當(dāng)平臺(tái)升級(jí)或修改接口時(shí)不會(huì)影響應(yīng)用程序,,提高了CPU利用率,并使程序更加健壯,。一個(gè)應(yīng)用程序會(huì)使用很多接口,,而Shell接口可以用來(lái)創(chuàng)建其他接口,使BREW接口有了很大的擴(kuò)展性,。使用C語(yǔ)言開(kāi)發(fā)的BREW平臺(tái)具有面向?qū)ο蟮姆庋b性,、繼承性和多態(tài)性。
參考文獻(xiàn)
[1] 陳秀寓.基于brew平臺(tái)的多態(tài)機(jī)制實(shí)現(xiàn)[J].軟件工程師,,2010(2):104-106.
[2] 趙建祥,,高禮中.基于BREW的手機(jī)軟件模塊設(shè)計(jì)[J].儀儀器儀表用戶,2009(5):47-49.
[3] 費(fèi)寧.BREW實(shí)現(xiàn)機(jī)制深入分析[J].江蘇通信技術(shù),,2006(4):15-17.
[4] 焦玉海.深入BREW開(kāi)發(fā)[EB/OL](2005-10-04)[2011-10-01]http://down.51cto.com/data/250317.