最近做android瀏覽器外掛程式學到一些東西和大家分享:
需要瞭解的有以下幾個方面的知識:
1.外掛程式是什麼
2.android瀏覽器怎樣載入外掛程式和建立執行個體
3瀏覽器外掛程式和指令碼語言的互動
4外掛程式內部的資料流
一 瀏覽器外掛程式介紹:
1.1 概述
瀏覽外掛程式本質是一個功能模組,是瀏覽器功能的一種擴充。其載體是dll或則so檔案。它依附瀏覽器完成某一特定的功能。外掛程式需要實現瀏覽器規定的一些函數這些函數叫著NPAPI.正是外掛程式實現了這些函數才可以和瀏覽器互動。同時瀏覽器也為外掛程式提供一些函數。在android平台下還有一些專有的函數。他們的函數名字都有約定。外掛程式提供的方法以NPP_打頭。瀏覽器提供的方法是NPN_,android提供的函數是以ANP開頭的。
外掛程式作為一個共用庫那麼它什麼時候被載入。有匯出了什麼介面讓瀏覽器調用。
瀏覽器外掛程式是被瀏覽器載入的。在android下也是被webkit載入。通常一個共用庫被載入都是調用loadlibray函數,然後使用getentrypoint函數得到共用庫匯出函數的地址。同樣外掛程式也是這樣被瀏覽器載入的。既然要調用loadlibrary函數那麼我們必須知道共用庫在系統中的位置。對於windows,mac,linux系統都可以存放在固定的位置。在linux下還可以通過設定環境變數來設定路徑。但是在android系統下不是這樣的。在1.5的時候有個固定的路徑data/data/com.android.browser/plug-in.但是在2.0以後就沒有存到這個路徑。而是存放在外掛程式APK包安裝目錄下的lib檔案夾下。比如外掛程式的包名是com.android.plugin,那麼共用庫的儲存路徑應該是data/data/com.android.plugin/lib/。對於各種外掛程式路徑不一樣,那麼瀏覽器是如何找到這些lib的路徑,下面將詳細介紹。
1.2 android瀏覽器外掛程式
對於android瀏覽器外掛程式,是以apk包形式發布的。並且在該工程中我們有定義一個service。這個service可以響應PLUGIN_ACTION。這個是在AndroidMainefest.xml中設定的。而外掛程式的註冊也是通過service完成的。
二、android瀏覽器載入外掛程式
2.1 總述
1.1中提到了瀏覽器載入外掛程式是通過loadlibary方式載入。並且需要知道路徑。其實瀏覽器載入外掛程式總的分為三步:
1.瀏覽器尋求外掛程式路徑,這個是通過外掛程式apk包安裝時啟動並執行service來找到的。
2.瀏覽器擷取外掛程式的資訊。得到外掛程式的名字,描述和MIME資訊並儲存到自己的plugindatabase類執行個體中。
3.瀏覽器建立外掛程式執行個體。這個過程建立外掛程式執行個體,並對外掛程式的內部資料初始化。
下面詳細描述這三個過程:
2.2瀏覽器擷取外掛程式的路徑。
每次在瀏覽器啟動重新整理頁面時,便會重新整理自己的外掛程式資訊庫即更新自己的pluginDatabase。然後PluginDataBase的函數refresh便會調用plugin manager的函數得到外掛程式路徑。而plugin manager通過pack manager找到所有能夠響應PLUGIN-ACTION intent的service。然後通過每一個service資訊得到包的名字就可以找到外掛程式了。具體可以參考frameworks/base/core/java/android/webkit/ PluginManager.java。
圖1描述了瀏覽器得到外掛程式路徑的流程。詳細可以參考源碼。路徑:/frameworks/base/core/java/android/webkit
external/webkit/WebCore/plugins
external/webkit/WebKit/android/jni
2.3 瀏覽器擷取外掛程式資訊
得到外掛程式路徑後,我們可以得到外掛程式匯出的函數地址了,首先我們瞭解一下外掛程式匯出的函數和他們的功能。
圖2顯示了一個外掛程式共用庫匯出的函數。下面詳細說明每個函數的功能。
在瀏覽器調用refresh後將會調用NP_GetValue得到外掛程式的名字和描述資訊。然後調用NP_GetMIMEDescription得到外掛程式的MIME類型、支援檔案的副檔名和描述。將這些資訊儲存到pluginDatabase中。NP_Shutdown是在銷毀pluginview時會調用,作用釋放外掛程式的資源。關於NP_Initialize函數是在建立外掛程式執行個體時才會調用。具體過程3所示。
在找不到MIMEType時瀏覽器會根據資料檔案的副檔名來匹配外掛程式。
2.4瀏覽器建立外掛程式執行個體:
NP_Initialize函數比較重要。它是瀏覽器和外掛程式進行互動的關鍵。他的功能主要有三個:
1.得到瀏覽器定義的NPN_函數地址。
2.將外掛程式定義的NPP_函數地址返回給瀏覽器
3.得到Android提供的一些ANP_函數。
下面列出了這些函數:
//NPP函數,外掛程式和瀏覽器互動的主要函數
上面的函數都有一個NPP instance參數,其實它是一個指向pluginview成員變數的指標。而該成員變數又有兩個指標pdata和ndata。pdata指向我們的外掛程式NPOject對象。在NPP_New中可以看到這個附值。ndata存地則是pluginview自己的this指標
//android提供的函數
上面這些函數的初始化是通過NPN_Getvalue得到的。
//外掛程式提供的另外一些函數,這些函數主要是完成外掛程式和javasript的互動使用。
在調用完NP_Initialize函數後瀏覽器就知道NPP_New函數的地址了。這時瀏覽器調用該函數建立瀏覽器執行個體。該函數是在pluginview中被調用的。
在NPP_New中我們建立外掛程式的執行個體NPObject。NPObject的建立是呼叫瀏覽器的NPN_create函數建立的。在該create函數中又判斷NPObject自己提供create方法沒(creat方法的地址通過NPN_create第二個參數傳入的),如果沒有那麼瀏覽器自己調用malloc建立執行個體。並且將外掛程式提供的NPObject函數地址(上面列出的static函數)儲存在NPObject對象裡面。到此外掛程式的執行個體就建立完了。
三、瀏覽器外掛程式和指令碼語言的互動
瀏覽器提供了外掛程式和javascript互動的機制。
首先,看一下java script如何調用外掛程式的方法的。
瀏覽器首先會調用NPError NPP_GetValue(NPP instance, NPPVariable variable, void* value)取得NPObject對象的地址。Variable參數為NPPVpluginScriptableNPObject。在取得該對象後瀏覽器就可以調用外掛程式提供的NPClass函數。最主要的函數有下面幾個:
pluginHasMethod :詢問外掛程式是否支援某一js方法。
pluginHasProperty :詢問外掛程式是否具有某一屬性
pluginInvoke : 當外掛程式支援某一方法時,瀏覽器將會調用該函數執行外掛程式為js提供的這一方法。那麼對於提供的很多方法外掛程式如何在該函數內區分。
我們來分析一下該函數:
static bool pluginInvoke(NPObject *obj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result);
obj是外掛程式裡的NPObject對象地址。
Name表示外掛程式提供方法的名字,通過對比這個參數來區分外掛程式提供的不同方法。
Args和argcount分別表示js傳來的參數地址和參數個數。
Result得到的結果。
下來我們看一下外掛程式是如何調用js提供的方法:
Js可以通過2種方式為外掛程式設定回呼函數。虛擬碼如下:
<script language=javascript>
Plugin.Onfun = fun;//方式一 通過設定外掛程式屬性傳入回呼函數地址
Plugin.Onfun(fun);//方式二 通過調用外掛程式函數傳入回呼函數地址
Function fun(){}
</script>
在外掛程式內部,當js函數地址傳到外掛程式時,瀏覽器把它封裝為一個NPObject對象,裡面存有函數地址。
方式一: 在外掛程式內部,瀏覽器會調用pluginHasproperty確認外掛程式是否有該屬性。如果有然後瀏覽器調用pluginSetproperty函數。pluginSetProperty(NPObject *obj, NPIdentifier name, const NPVariant *variant)的第二個參數判斷是哪個屬性,第三個參數就是NPObject對象地址。
方式二:在外掛程式內部,瀏覽器會調用pluginHasmethod確定是否支援該方法。然後調用pluginInvoke,這裡面的args參數包含了回呼函數NPObject地址。
js設定完回呼函數後,外掛程式就可以調用該函數了。需要使用NPN_InvokeDefault,範例程式碼如下:
bool bret = gBrowser->invokeDefault(npp, callbackNPObject, &pV, 1, &result);
另外,外掛程式也可以直接調用js中的函數。在外掛程式內部呼叫瀏覽器的getUrl函數。具體格式如下:
gBrowser->geturl(inst(), “javascript:function()”, "_self");
如果想傳入整數參數,上面函數第二個參數應寫成: “javascript:function(“+num+”)”。
如果傳入字串參數,上面函數第二個參數為: “javascript:function(/’“+”string”+”/’)”。如果字串含有中文,需要進行url encode。
四、外掛程式資料流
外掛程式可以通過瀏覽器向伺服器請求資料,同時也可以向伺服器發送資料。
如果外掛程式需要向伺服器請求資料時可以呼叫瀏覽器函數NPN_Geturl向伺服器發送請求。裡面的target參數設定為NULL資料就可以傳給本頁面的外掛程式。請求成功瀏覽器會調用外掛程式的NPP_newstream函數,
通過NPP_Newstream建立流時,將傳遞一個流的模式參數,plug-in在它返回時設定這個參數,預設設定是NP_Normal;通過NPP_DestroyStream刪除流.Plug-in也可以調用NPN_DestroyStream刪除流.這三種模式分別如下:
正常模式.當參數設定為NP_Normal時採用該模式,當有資料可發送時Netscape就把資料發送給plug-in,這些資料可能是以非正常順序到達。瀏覽器通過調用一系列的NPP_WriteReady和NPP_Write來發送資料。瀏覽器通過len這個參數告訴plug-in它將發送多少資料,Netscape調用NPP_WriteReady來確定plug-in每次準備接收多少位元組的資料,再調用NPP_Write發送資料.此種模式的效率較高.
隨機存模數式.若調用NPP_NewStream時將其中的布爾型參數Seekable設為真時,就採用此種模式.此時,流中的資料先由plug-in調用NPN_RequestRead加以指明所要擷取的資料的範圍,然後瀏覽器調用NPP_WriteReady和NPP_Write把資料傳送給plug-in.這種模式需要遠程伺服器的支援或瀏覽器先將流資料存到本地的臨時檔案中.用這種模式時,使用者可以從伺服器的資料檔案中任意讀取自己想要的記錄,就如同從本地硬碟上讀取一個記錄一樣.
檔案模式.把參數設定為NP_AsFile即可.瀏覽器先將整個Url資料存到一個本地檔案中,然後通過NPP_StreamAsFile將檔案名稱傳給plug-in。Plug-in可以通過檔案操作獲得所要資料.
五,總結
對於瀏覽器外掛程式的開發可以參考源碼的執行個體,development/samples/browseplugin執行個體。相關知識可以參考https://developer.mozilla.org/en/Gecko_Plugin_API_Reference
轉載自:http://blog.csdn.net/ownerwu/article/details/6429072