Titanium架構分析
一、分析的目標
- 了解Titanium產品的基本框架結構和特點
- 了解Titanium產品如何擴展本地API以及訪問方式
- 了解Titanium產品中的動態語言之間如何相互調用
二、Titanium概述
2.1 Titanium介紹
Titanium是一個Web應用程序運行環境,它支持不同的系統平臺(Windows、Linux、Mac),并且支持Web應用程序對本地APIs的訪問。在基于Titanium平臺上,用戶可以快速開發和方便的部署應用程序,并且這些應用程序可以使用本地APIs實現許多普通Web應用程序無法完成的功能和特性。
2.2 Titanium特點
Titanium框架具有如下幾個方面的特點:
- 支持多平臺(Linux、Mac、Windows、移動設備)
- 使用Web技術加快軟件開發速度
- 支持Web中內嵌多種編程語言
- 支持對本地APIs的訪問
- 通過Appcelerator網絡云服務,基于Titanium的應用可以更容易的打包、測試和部署
- 本地功能的模塊化,可動態加載指定的功能模塊
- 強大靈活的語言擴展,用戶在Titanium框架中可以很方便的擴展多種動態語言
2.3 Titanium 框架結構

上圖來自于Appcelerator官網,該圖以iPhone和Android兩個移動平臺為例,描述了Titanium的總體框架結構。在Titanium框架中,Web應用程序可以很方便的訪問設備UI組件。比如,可以在頁面中使用Titanium提供的API控制導航條、工具欄、菜單,以及可以動態的向用戶彈出對話框、警告框等。除此,之外Titanium API還支持本地功能模塊的訪問,即用戶可以使用Titanium提供的APIs接口訪問數據庫、定位功能、文件系統功能、網絡功能、媒體功能等。
不過該框架圖,并沒有將Titanium中對多種腳本語言的相互訪問機制很好的表現出來。但是,這一機制卻又是Titanium框架的一個比較重要的功能特性。
三、Titanium構建
Titanium的構建過程使用scons管理(http://www.scons.org/)。scons是一個開源的軟件構建工具,使用Python語言來描述軟件構建規則。通過Titanium的源碼級構建和Titanium的構建規則兩個方面,可以了解Titanium運行環境由那些部分組成、這些模塊和模塊之間的關系是什么。
[注]以下所有的測試和分析內容均是以Linux平臺上Desktop版本的Titanium代碼為基礎。
構建Titanium所依賴的庫和環境
- Ruby 1.8.x 開發包
- Python 2.5.x開發包
- scons構建工具
- git 版本管理工具
Ubuntu 9.04上構建Titanium所需的支持包
- sudo apt-get install build-essential ruby rubygems libzip-ruby \
- scons libxml2-dev libgtk2.0-dev python-dev ruby-dev \
- libdbus-glib-1-dev libnotify-dev libgstreamer0.10-dev \
- libxss-dev libcurl4-openssl-dev
- sudo apt-get install git-core
獲取Titanium源碼
- git clone git://github.com/marshall/titanium
- cd titanium
獲取Kroll源碼
- git submodule init
- git submodule update
- cd kroll
- git checkout master
構建Titanium測試程序
- cd ..
- scons debug=1
運行
- scons testapp debug=1 run=1
有關Titanium構建相關的信息,可以訪問以下頁面獲得:
http://wiki.github.com/marshall/titanium/build-instructions
3.2 Titanium構建規則分析
3.2.1 版本需求
構建過程所需的庫/程序版本 | |
Python | 2.5 |
Ruby | 1.8 |
Scons | 1.2 |
kroll 源碼版本 | 12/30/99 |
titanium_desktop 源碼版本 | 12/30/99 |
WebKit版本 | libwebkittitanium-1.0.so.2.7.0 |
3.2.2 默認配置項
默認配置項 | ||
配置 | 值 | 備注 |
PRODUCT_VERSION | 0.7.0 | |
INSTALL_PREFIX | /usr/local | |
PRODUCT_NAME | Titanium | |
CONFIG_FILENAME | tiapp.xml | |
BUILD_DIR | build | |
THIRD_PARTY_DIR | kroll/thirdparty | |
DISTRIBUTION_URL | api.appcelerator.net | |
CRASH_REPORT_URL | api.appcelerator.net/p/v1/app-crash-report | |
GLOBAL_NS_VARNAME | Titanium | 定義了全局Titanium對象名稱 |
3.2.3 scons編譯參數
Scons編譯參數 | |
debug | 0表示release版本,1表示debug版本 |
clean | 清除構建的工程 |
qclean | 清除構建的工程 |
run | 運行TestApp |
run_with | 帶參數運行TestApp,好像Linux平臺上沒用 |
3.2.4 構建規則文件
構建規則文件 | |
kroll/SConscript.thirdparty | Titanium所需的第三方支持文件規則 |
installation/SConscript | Titanium安裝器構建規則 |
kroll/SConscript | 構建kroll庫規則 |
modules/SConscript | 構建語言支持模塊規則 |
apps/SConscript | 構建TestApp規則 |
SConscript.dist | 構建SDK規則 |
SConscript.docs | 構建APIs文檔規則 |
SConscript.test | 構建測試程序規則 |
3.2.5 核心庫和程序構建規則
庫/程序 | 規則 |
build/linux/runtime/template/kboot | kroll/boot/breakpad/common/*.c
kroll/boot/breakpad/common/*.cc kroll/boot/breakpad/client/*.cc kroll/boot/breakpad/processor/*.cc kroll/boot/breakpad/client/linux/handler/*.cc kroll/boot/breakpad/common/linux/*.cc |
build/linux/runtime/libkroll.so | kroll/api/*.cpp
kroll/api/config/*.cpp kroll/api/binding/*.cpp kroll/api/utils/*.cpp kroll/api/utils/poco/*.cpp kroll/api/utils/linux/*.cpp kroll/api/net/proxy_config.cpp kroll/api/net/*_linux.cpp |
build/linux/runtime/libkhost.so | kroll/host/linux/host.cpp
kroll/host/linux/linux_job.cpp |
/linux/modules/api/libapimodule.so | poco third library(http://pocoproject.org/)
kroll/modules/api/*.cpp |
build/linux/modules/javascript/libjavascriptmodule.so | poco third library(http://pocoproject.org/)
webkittitanium-1.0 third library kroll/modules/javascript/*.cpp |
build/linux/modules/ruby/librubymodule.so | poco third library(http://pocoproject.org/)
libruby third library kroll/modules/ruby/*.cpp |
build/linux/modules/php/libphpmodule.so | poco third library(http://pocoproject.org/)
kroll/modules/php/*.cpp |
四、Titanium靜態分析
該部分主要是說明整個Titanium的閱讀工作量、弄清楚Titanium中定義的核心對象的功能作用,以及各個模塊之間的關系是什么。
4.1 代碼統計
這里,將Titanium項目代碼分成kroll和功能模塊擴展兩部分代碼來統計,數據如下兩表所示:
Kroll模塊代碼量統計 | ||||||
Language | Files | Blank | Comment | Code | Scale | Equiv |
C/C++ Header | 1168 | 35490 |
63506 |
111461 |
1.00 |
111461 |
HTML | 386 | 1252 | 16112 | 51375 | 1.9 | 97612.5 |
C++ | 162 | 6401 | 7046 | 33133 | 1.51 | 50030.83 |
Javascript | 47 | 3273 | 1598 | 13214 | 1.48 | 19556.72 |
CSS | 3 | 554 | 41 | 2720 | 1 | 2720 |
Object C | 6 | 359 | 312 | 1400 | 2.96 | 4144 |
Python | 10 | 260 | 185 | 1206 | 4.2 | 5065.2 |
Shell | 11 | 56 | 157 | 234 | 3.81 | 891.54 |
Make | 3 | 30 | 29 | 93 | 2.5 | 232.5 |
Assembly | 1 | 15 | 39 | 57 | 0.25 | 14.25 |
Ruby | 1 | 10 | 0 | 54 | 4.2 | 226.8 |
Yaml | 1 | 0 | 0 | 12 | 0.9 | 10.8 |
SUM | 1802 | 47938 | 89263 | 217012 | 1.35 | 293546.95 |
titanium_desktop模塊(排除Kroll模塊) | ||||||
Language | Files | Blank | Comment | Code | Scale | Equiv |
Javascript | 118 | 5801 | 3276 | 28678 | 1.48 | 42443.44 |
C++ | 125 | 4690 | 5169 | 27320 | 1.51 | 41253.2 |
C/C++ Header | 159 | 1647 | 3443 | 7682 | 1 | 7682 |
HTML | 49 | 347 | 39 | 3715 | 1.8 | 7058.5 |
Ruby | 29 | 673 | 643 | 3227 | 4.2 | 13553.4 |
CSS | 5 | 542 | 41 | 2655 | 1 | 2655 |
Python | 45 | 601 | 664 | 2632 | 4.2 | 11054.4 |
C | 1 | 167 | 237 | 1925 | 0.77 | 1482.25 |
Shell | 13 | 60 | 158 | 251 | 3.81 | 956.31 |
PHP | 5 | 37 | 1 | 179 | 3.5 | 626.5 |
XML | 5 | 0 | 8 | 151 | 1.9 | 286.9 |
Object C | 2 | 31 | 15 | 119 | 2.96 | 352.24 |
SUM | 556 | 14596 | 13694 | 78534 | 1.65 | 129404.14 |
4.2 核心對象的介紹
對象 | 基類 | 說明 |
AccessorBoundObject | StaticBoundObject | 對setter和getter的封裝,當用戶訪問想訪問XXX屬性時,該對象會調用setXXX方法或者getXXX方法。目前Titanium中主要是JS的Titanium對象使用AccessortBoundObject封裝 |
AccessorBoundMethod | StaticBoundMethod | 用于通過屬性的方式訪問方法,由該對象封裝的方法,會自動的導出setter和getter方法 |
AccessorBoundList | StaticBoundList | 用于以屬性的方式訪問list對象,由該對象封裝的list,會自動導出setter和getter |
ArgList | 對參數列表對象的封裝 | |
Blob | 對數據封裝,可以描述任何數據 | |
Tuplex | 對元組對象的封裝 | |
DelegateStaticBoundObject | KObject | 用于對全局訪問對象的封裝,目前Titanium中只有UI和Titanium JS對象使用該對象封裝 |
KList | KObject | 封裝List對象 |
KMethod | KObject | 對方法的封裝,所有擴展語言的函數,都需要用該對象封裝 |
KEventObject | AccessorBoundObject | 描述事件對象,JS中可以通過該對象,向主線程發送事件。比如重新載入頁面、彈出對話框。 |
KEventMethod | KEventObject | 對事件方法的封裝,目前只有ti.Process模塊使用該對象 |
KObject | ReferenceCounted | 所有的其他類型語言對象和方法都是繼承該類,這樣可以按照相同的方法處理不同語言對象和方法 |
StaticBoundList | KList | 靜態列表,使用內部map綁定屬性 |
StaticBoundMethod | KMethod | 靜態方法 |
StaticBoundObject | KObject | 靜態對象,繼承該對象可以很方便的設置對象的屬性、方法。
每個StaticBoundObject內部,都保存著一個String到ShareValue的map成員屬性。 |
Value | ReferenceCounted | 描述對象類型 |
4.3 模塊之間的關系
從整體框架結構上來看,可以將Titanium分成三個部分,最上層是WebKit以及針對WebKit的擴展(修改很少),中間層是kroll可以將其看成是一個中間件,最下層是個個模塊的擴展。模塊之間的關系如圖所示:

點擊查看大圖
以下從WebKit、Kroll和模塊擴展三個部分來說明
1、WebKit: 當WebKit引擎解析頁面數據發現
首先,WebCore引擎會解析HTML頁面數據,當發現有標簽,或者當用戶觸發了頁面中某個與腳本函數相關的控件時,WebCore會將相應的腳本代碼片段傳遞給JavascriptCore解析執行。如果對比Tinanium修改的WebKit代碼和原始的WebKit代碼(http://www.webkit.org)會發現,tinanium對WebKit的修改是及小的。主要是作了兩個方面的工作:首先,tinanium擴展了KURL的處理,增加了ti://, app://等私有協議的支持。再者,在WebKit/gtk/webkit/目錄中,添加了幾個接口函數(主要是用來處理擴展的協議和注冊解析器),其中最重要的是webkit_titanium_add_script_evaluator,該接口在Kroll模塊的script類中會被調用,用來向WebKit引擎注冊一個Evaluator Proxy。
2、Kroll和Base Module:這部分主要的職責是負責Javascript的方法、對象和Python、Ruby、PHP等語言之間相互轉換、事件處理,以及模塊動態加載。Kroll模塊中,定義了一個host對象,這個對象是整個TestApp的主線程,UI初始化、WebKit初始化和事件處理都是在host中完成的。host對象中保存了一個全局對象表,該表會在WebKit引擎、Python引擎、Ruby引擎之間以KObject中間對象形式相互傳遞,最終達到不同語言之間的相互調用。
3、API Extension:這里擴展了大量的與系統平臺功能相關的API共Web應用使用。其中最重要的一個對象是ti.UI,該模塊負責UI相關的資源、事件處理、GTK主界面的創建、Tininum JS對象的創建。
五、Titanium動態分析
下面從6個方面以TestApp為例,來分析Titanium的主要特性和功能。
5.1 TestApp初始化
TestApp的啟動過程有個自啟動過程。首先,TestApp啟動后會創建一個Application對象,該對象會從Mainifest文件中獲取App相關的資源,并且保存在一個全局變量中。然后,TestApp會設置幾個系統環境變量:
- KR_BOOTSTRAPPED: 描述是否已經初始化環境變量以及構建Application對象
- KR_HOME:描述運行程序的HOME路徑
- KR_RUNTIME:描述運行時資源路徑
- KR_MODULES:描述需加載模塊信息
- LD_LIBRARY_PATH:描述模塊所在的文件夾路徑
***,TestApp會使用exec系統調用將自己自啟,然后通過之前設置 KR_BOOTSTRAPPED環境變量判斷是否進入下一階段的初始化過程。如果 KR_BOOTSTRAPPED設置為YES,則會首先將其unset,然后啟動LinuxHost。
在titanium框架中,使用動態庫的方式將模塊之間的關系解耦合。在TestApp啟動的第二階段中,StartHost(kroll/boot/boot_linux.cpp)會根據之前設置的 KR_RUNTIME路徑信息,找到libkhost.so動態庫,然后從libkhost.so中獲取Execute函數指針,并且調用(這里有個問題,如果多個實列同時運行,有可能KR_RUNTIME尚未unset就啟動第二個應用,則會出現TestApp異常)。
libkhost.so動態庫中的Execute方法,首先創建一個Host實例,在這里是LinuxHost對象,然后調用該對象的Run方法進入一個循環。這個循環是整個TestApp的主循環,主要負責模塊的動態發現和加載,事件處理。在LinuxHost的實現中,會維護一個job隊列,通過定時器的方式,每隔250ms的時間會去檢測該job隊列中是否有job存在(LinuxJob描述)。如果,事件存在則會一次性將所有的事件取出,并且清空事件隊列,然后一個個的執行job對象的Execute方法。
TestApp的兩次初始化過程如下圖所示:

點擊查看大圖
5.2 模塊初始化
TestApp程序創建LinuxHost對象,并且執行Run方法之后,會首先掃描KR_MODULES環境變量中指定的模塊,并且從 LD_LIBRARY_PATH定義的路徑信息中尋找到這些動態庫模塊,并且加載(調用相應模塊的Initialize方法)。LinuxHost首先加載的是基本模塊(API,PythonModule、RubyModule、PHPModule和JavascriptModule)。
以pythonModule為例,描述一個完整的加載過程:
- Host::LoadModules從環境變量中獲取到python動態庫的路徑信息
- 調用Host::FindBasicModules方法,將libpythonmodule.so文件加載進來,然后調用PythonModule的Initialize方法
- PythonModule::Initialize首先會向全局屬性表中創建一個Python和PythonEaluator對象的關聯。前面也說到,Host對象會保存一個全局屬性表,這個表中使用KObject中間對象形式,將JAVASCRIPT、PYTHON、RUBY、PHP等語言定義的對象封裝,并且保存在該表中。運行時,可以使用Host對象的GetGlobalObject方法獲取。
- 調用Script::AddScriptEvaluator靜態方法將PythonEvaluator對象放入Script對象中維護的一個Ealuator列表中。當JavascriptCore引擎發現<script>標簽會遍歷這個evaluator鏈表,通過MIME類型找到相應的解析器實例,然后將代碼片段傳遞給相應的解析器處理(這樣就可以支持HTML代碼中內嵌多種語言)。
這里還有一個模塊比較特殊需要仔細說明,即ti.UI模塊。該模塊負責WebKit引擎初始化,GTK窗口創建以及UI事件的處理。加載過程類似PythonModule,首先Host對象找到libtiuimodule.so動態庫,然后調用Initialize方法初始化,ti.UI模塊中的Initialize方法只做了兩件事情,創建了一個APIBinding對象,然后將“API”屬性和APIBinding關聯起來,保存在全局屬性表中。接下來,當Host::LoadModules方法加載完畢所有的動態庫后,會調用Host::StartModules來啟動模塊(在整個TestApp運行中,只有ti.UI模塊的Start方法被重載了,而其他模塊在StartModules方法被執行時,什么事情都沒有做)。UIModule::Start方法做了三個非常重要的操作:1、創建GtkUIBinding對象,并且將“UI”和該對象綁定,存放在全局屬性表中。2、調用ScriptEvaluator::Initialzie()使用WebKit擴展中導出的webkit_titanium_add_script_evaluator函數,將自己注冊到JavascriptCore中。3、創建初始化WebView。
至此,WebKit引擎已經初始化完畢、UI界面已經初始化完畢、相應語言的解析器以及JAVACRIPT API擴展對象已經添加到全局屬性表中。但是至此,頁面中是無法正常訪問Titanium對象的。
整個過程如下圖所示:

點擊查看大圖
5.3 Titanium對象的注冊
Javascript中的Titanium并沒有通過硬編碼的方式定義該名稱,而是在構建的過程中通過變量的方式指定的。在構建規則中有GLOBAL_NS_VARNAME變量,該變量名稱會作為編譯參數傳遞,代碼通過改變量的定義,來確定Javascript可見的Titanium主對象的名稱是什么。
當webView構建完畢后,Titanium Js主對象并不存在,只有當***次WebKit遇到腳本代碼時,這個對象才被創建。當WebKit引擎碰到腳本對象時,會調用JavascriptCore的initScipt方法,初始化Javascript引擎。在此JavascriptCore會將我們之前調用webkit_titanium_add_script_evaluator增加的Evaluator代理和JavascriptCore解析關聯起來。當引擎和資源都初始化完畢,會向FrameClient發送object avaliable通知,會調用FrameLoader::dispatchWindowObjectAvaliable()方法。這個方法會導致UserWindow::RegisterJSContext()方法的調用。然后UserWindow對象會創建一個DelegateStaticBoundObject對象來描述Titanium對象,并且將之前初始化完畢的Titanium API對象(KObject)與Titanium對象關聯起來,然后將其放入到全局屬性表中。這樣,之后Web應用程序就可以從全局屬性表中訪問到Titanium對象了。但是Titanium對象中有哪些子屬性是不知道的,這是運行時才去確定。
這個初始化過程如下圖所示:

點擊查看大圖
5.4 事件系統
Titanium的事件系統分成兩個部分來說明,一部分是如何獲取事件,另一部分是如何向事件系統中增加新事件。
當linuxHost::RunLoop循環被調用后,立即會注冊一個定時器,每隔250ms調用一次main_thread_job_handler函數。該函數首先通過GetJobs方法獲取當前系統中未處理的事件,并且依次的調用事件對象(LinuxJob)的Execute方法執行。如果系統沒有未決的事件存在,則立即返回。
事件的添加和觸發均通過LinuxHost::InvokeMethodOnMainThread()方法完成。該函數會創建一個LinuxJob對象,并且插入到事件隊列尾中。事件的觸發有多種可能性,可以是由Javascript代碼觸發,也可以是內部事件觸發,同樣也有可能是UI事件觸發。
由于Titanium擴展的JS API在C層上都會與相應的C方法對應,當Web應用程序調用相應的JS方法,對應的C方法會被調用,該方法則會使用LinuxHost::InvokeMethodOnMainThread()方法,向主線程發送事件處理請求。
對于UI來說,在Linux平臺上所有的UI相關的事件首先是被GTK的應用框架截獲,當有UI事件到來時,ti.UI模塊會創建對應事件的KEvent對象(在event.h中定義),然后調用KEvent對象的Fire方法,觸發事件。該方法會間接的使用LinuxHost::InvokeMethodOnMainThread方法向LinuxHost事件隊列注冊事件。
這個過程如下圖所示:

點擊查看大圖
5.5 訪問Titanium對象屬性和方法
比如,有一段Javascript腳本中調用Titanium.API獲取Titanium對象的API屬性,當JavascriptCore解析到這部分代碼時,并不知道這個對象是什么類型的,完全當作一個抽象的JSValue對象來對待,因為對于Javascript引擎來說,并不需要時刻知道對象是什么,有那些屬性和方法,只有到運行時才會去用查詢該對象中是否存在指定的屬性或者方法(JavascriptCore內部維護了一張屬性表,通過查詢方式獲取相應屬性或者函數的處理函數指針。這點V8做的就比較高明,用類的方式描述,動態將對應JS的方法和屬性轉換成C++的成員函數和成員變量,大量的減少了訪問時間)。由于API這個對象是由Kroll創建的,是一個KObject對象,在掛載到Javascript 全局屬性表之前,Titanium會調用KObjectToJSValue方法,將KObject對象轉換成一個JS的Object對象,并且設置了幾個比較重要的回調函數:
- HasPropertyCallback: 當查詢屬性時候被調用
- GetPropertyCallback: 當獲取指定屬性時被調用
- SetPropertyCallback: 當設置指定屬性時被調用
當javascript訪問用Titanium對象的API屬性時,通過JSValue.Get方法會調用到GetPropertyCallback函數,該函數會查詢KObject對象中(這里是說的Titanium)是否有API這個屬性,如果有則轉換成JSValue對象,并且返回給Javascript引擎。
如果這里訪問的是一個屬性的方法,過程和訪問對象是一樣的,只不過***創建的是一個Function Js對象,而非Object對象。當Javascript訪問這個返回的Function對象時,Javascript引擎會調用callAsFunction方法,而該方法會引發CallAsFunctionCallback回調函數被調用(該函數是靜態函數在KMethodToJSValue函數中注冊)。這樣通過CallAsFunctionCallback這個回調接口,調用實際的KObject的Call方法,執行實際的處理函數。
屬性訪問過程如下圖所示:

點擊查看大圖
5.6 Javascript、Python、Ruby動態語言間的相互調用
Titanium框架中引入了一個比較有意思的特性,即支持多種語言之間的相互調用。從實現技術角度來說,Titanium的多語言支持的設計思想,是在學習了WebKit的Binding機制而發展過來的。主要用到了JavascriptCore引擎可以動態注冊Evaluator的機制。HTML語言中定義了<script>標簽,用于內嵌腳本語言,該標簽有個子屬性type,通過該屬性可以讓瀏覽器引擎區分是什么類型的腳本。加入我們有如下的腳本代碼:
- <script type=”text/python” src=”xxx.py”></script>
首先,WebCore引擎會解析HTML頁面數據,當發現有<script>標簽出現,則會創建HTMLScriptElement,對于script有兩種處理情況,一種是如上代碼通過src包含一個腳本路徑,還有一種情況是定義一段代碼,通過控件或者超連接的方式以事件方式觸發。如果是***種情況,則會在創建HTMLScriptElement的時引發ScriptElementData::requestScript方法的調用。如果是第二種情況,則會在觸發相應事件時候調用FrameLoader::executeScript方法執行腳本。最終都會調用JavascriptCore中的EvaluatorAdapter::evaluate()。由于在初始化ti.UI模塊時,我們已經注冊了自己的evaluator(ScriptEvaluator),因此會將獲取的腳本信息傳遞給ScriptEvaluator,在該對象中,會通過Script::Evaluate()方法,根據傳遞下來腳本的MIME類型(也就是script中text字段定義的類型)派發給注冊的不同解析器去執行。
這里以Javascript調用Python代碼,并且Python代碼中又調用了ruby代碼為例子說明其調用過程。
Python代碼:
- def abc():
- ruby_fun()
Ruby代碼:
- def ruby_func()
- …
- end
當pythonEvaluator::Evaluate()被調用后,首先將保存的全局JS對象表轉換成Python可識別的對象字典(KMethodToPyObject完成,轉換成PyKMethodType的Python對象)。這樣在之后編譯的Python代碼中就可以訪問到這些對象。然后將Python代碼使用Python編譯器編譯,并且將編譯后的函數對象轉換成KObject對象,插入到全局的JS對象表中(abc)。這樣,Javscript,和其他語言都可以識別該對象。同樣,對Ruby函數的處理,也會首先將全局JS對象表中的KObject對象轉換成Ruby的對象,然后對Ruby函數進行編譯,將新生成的Ruby函數對象(ruby_fun)轉換成KObject對象,然后從新更新JS全局對象轉換表。至此,全局JS對象表中就新增了兩個KObject對象:ruby_fun, abc。
當javascript訪問該abc函數對象時,按照通常方式首先調用CaAsFunctionCallback,該函數會調用KObject的Call方法,由于該KObject實際上就是一個KPythonMethod對象,因此KPythonMethod對象的Call方法會被調用。之前我們注冊的Python方法是一個PyKMethodType,該類型中定義了一個方法回調函數,當Python方法被調用時,該回調函數(PyKMethod_call)會被調用。這個例子中,在Python代碼里調用了ruby_fun,因此當PyKMethod_call被調用時,首先將調用的KObject對象(實際上是一個對Ruby函數對象的封裝)轉換成KMethod對象,然后調用Call方法。這樣就通過間接調用,調用到KRubyMethod的Call方法,使得Ruby函數得到執行。
整個過程如下圖所示:

點擊查看大圖
六、參考資源
http://www.appcelerator.com/