LuaView初識

前言

作為一名iOS開發(fā)工程師,App的動態(tài)化是一種趨勢,畢竟需求的增多,頻繁的提交版本、更新版本對用戶體驗上肯定會有影響。當然動態(tài)化的方案有很多種:RN,Weex,LuaView等。對于一個對H5、React 零基礎(chǔ)的小白,我準備還是從LuaView入手。最后還想說一句,沒想到在簡書寫的第一篇文章是關(guān)于LuaView的。好吧,我承認我比較懶!

什么是LuaView?

LuaView是一種運行在一個ViewController/Activity中,可以靈活加載Lua腳本,并能夠按照Native的方式運行的一種面向業(yè)務(wù)的開發(fā)技術(shù)方案。

LuaViewSDK使用lua虛擬機進行腳本解析,通過構(gòu)建lua與native之間的一系列基礎(chǔ)bridge功能,從另一個角度實現(xiàn)了動態(tài)化的native能力。

而對于為何選用Lua,其最大的優(yōu)勢就是:lua語法精煉直觀,lua虛擬機輕量高效,使用Native編程模式,Native開發(fā)人員容易上手。

以上很不要臉的取自其官方文檔的描述:?https://alibaba.github.io/LuaViewSDK/guide.html

LuaViewSDK 是阿里開源的一個實現(xiàn)動態(tài)化方案的框架。開源地址:?https://github.com/alibaba/LuaViewSDK

目前其SDK由阿里的一個團隊來維護。個人感覺推廣力沒有Weex高。官方文檔也很久沒有更新了。不過提供了一個官方技術(shù)交流群:539262083 。

LuaViewSDK的整體架構(gòu)

上圖是LuaViewSDK的架構(gòu):(由下往上)

Native? & Framework :表示了Android、iOS及其對應(yīng)的框架層。

Lua Engine:即Lua虛擬機,Android對應(yīng)LuaJ,iOS對應(yīng)LuaC。作為lua腳本和nati語言之間的橋梁,將lua腳本翻譯成native能夠識別的目標語言。

Lua-Native UI Lib:LuaView的核心組件。其實LuaView對Native的各種UI組件進行了再次封裝,并且注冊到了Lua環(huán)境中,Lua腳本可以直接創(chuàng)建和操作這些組件,來達到創(chuàng)建和控制Native組件。(其實查看SDK源碼,會發(fā)現(xiàn),不僅封裝了UI組件,還有一些方法類,如Timer,Gesture等)。

Script Manager:Lua腳本管理器,用于腳本的解壓、驗證、加解密、解壓縮等工作。

Security:Lua腳本的校驗工作(完整性和安全性的校驗)。

Lua Script & Lua UI Lib:Lua 業(yè)務(wù)腳本以及 Lua 層的 UI 庫。

LuaView的基本用法

LuaView

第一種方式,直接創(chuàng)建LuaView對象,添加到你想渲染的View上,運行腳本進行界面渲染。

//1、創(chuàng)建LuaView,LView為LuaView子類(SDK封裝的)

self.lv= [[LView alloc]initWithFrame:lvRect];

self.lv.viewController= self;

[self.view addSubview:self.lv];

//2. 加載并運行腳本

[self.lv runFile:scriptFileName];

....

//3、LuaView對象被回收之前必須清理內(nèi)存

[luaview releaseLuaView];

第二種方式,創(chuàng)建LViewController的控制器對象,其屬性 lv 就是一個LuaView 對象,故運行腳本一樣實現(xiàn)了界面渲染。其已經(jīng)做好了各種生命周期和內(nèi)存管理的處理,所以不用主動去釋放。

//1. 創(chuàng)建LuaView VC

LViewController*luaVC=[[LViewController alloc]init];

//2. 加載并運行腳本

[luaVC.lv runFile:scriptFileName];

此處遇到一坑:

由于我初次使用lua,對其語法不熟,自己創(chuàng)建demo運行腳本時,用了別人寫的一個簡單demo的腳本:繪制一個label。可是運行后,發(fā)現(xiàn)沒報錯,但是也沒繪制,界面白板,也沒用返回錯誤提示。百思不得其解!最后對比了下別人 demo 和我的 demo 的 LuaViewSDK,發(fā)現(xiàn)版本不一致,別人的是 2.5.xx.x,而我的版本是0.5.1(最新的)。而原先 LuaViewSDK 語法和 lua 標準語法有區(qū)別 :‘.’ 和 ':' 互換了。最新SDK支持的lua標準語法(冒號調(diào)用方法,點調(diào)用屬性),所以我用最新SDK 運行原來語法寫成的腳本,是有問題的,語法不一致。最新的SDK中,LuaView 的子類 LView 有一屬性 changeGrammar(默認為NO),設(shè)置為YES會進行語法轉(zhuǎn)換。若新SDK 運行老語法的lua腳本,則需要將此屬性設(shè)置為 YES 。

而由于我項目中既有自己使用lua標準語法寫的腳本,也有從別人demo拷貝過來的老語法lua腳本。故我想當然的講 changeGrammar 設(shè)置為 YES,結(jié)果發(fā)現(xiàn)老語法lua腳本正常渲染界面,標準語法腳本卻渲染失敗,沒有錯誤提示,白板。后來發(fā)現(xiàn) changeGrammar 設(shè)置為YES,并非將lua語法轉(zhuǎn)換成標準語法,而是遍歷腳本后,將 ‘.’ 和 ':' 進行互換,所以標準語法寫的lua腳本又被轉(zhuǎn)換了。

所以標準語法的lua腳本,changeGrammar 千萬別設(shè)置為 YES。

LuaViewCore

LuaViewCore其實就是Lua的虛擬機,負責(zé)實現(xiàn)了Lua腳本到Native語言的映射。查看LuaView.h/m源碼,會發(fā)現(xiàn)LuaView初始化時,會創(chuàng)建一個?LuaViewCore 的對象,即一個?LuaView?對應(yīng)一個 LuaViewCore。

當業(yè)務(wù)需要要求一個頁面有多個子View都需要lua控制渲染時,若通過創(chuàng)建多個LuaView方式來渲染,則會創(chuàng)建多個LuaViewCore,這樣或多或少會影響性能。那么如何實現(xiàn)共享一個Lua虛擬機,即共享LuaViewCore,來渲染多個界面。

//1、初始化LuaViewCore

self.lvCore = [[LuaViewCore alloc]init];

//2、運行腳本

[self.lvCore runFile:@”luaName.lua”];

//? ? [self.lvCore loadFile:@”luaName.lua”];

//3、調(diào)用腳本里的方法 topViewUI/bottomViewUI ,在指定的 self.topView/self.bottomView 進行UI渲染

//str:成功則返回nil,失敗則返回失敗原因

NSString *str0 = [self.lvCore callLua:@"topViewUI" environment:self.topView args:nil];

NSLog(@"%@",str0?str0:@"topViewUI-sucessed");

NSString *str1 = [self.lvCore callLua:@"bottomViewUI" environment:self.bottomView args:nil];

NSLog(@"%@",str1?str1:@"bottomViewUI-sucessed");

對應(yīng)的腳本 luaName.lua 如下:

function topViewUI( )

aLabel = Label();

aLabel:text("aaaa");

aLabel:frame(0, 0, 100, 30);

end

function bottomViewUI()

aLabel = Label();

aLabel:text("cccc");

aLabel:frame(0, 0, 100, 30);

end

此處遇到一坑:

正如我前面所述,其官方文檔很久沒有更新,可能維護也很少。其官方描述?LuaViewCore 用法是這樣的:LuaViewCore初始化后,load 腳本,然后就可以調(diào)用腳本的方法。但是按照這個流程,調(diào)用腳本方法,會返回錯誤信息“function is nil error”,即方法找不到。原因是,在lua中,方法的定義是放在腳本運行時的,而非編譯時。故僅僅編譯腳本,是無法調(diào)用腳本方法的。正確的流程是:LuaViewCore初始化后,run 腳本,然后就可以調(diào)用腳本的方法。(此處已和其官方團隊聯(lián)系確認,是其文檔有誤)

Native自定義功能橋接

源碼解析

在實現(xiàn)自定義功能橋接到Lua層之前,首先要從源碼入手,了解LuaView是如何封裝Native控件,并且注冊到Lua環(huán)境中,Lua腳本可以任意創(chuàng)建和操作的!

正如上面所言,LuaView 初始化時,會初始化一個 LuaViewCore,然后就沒有其他什么特別的代碼。LuaViewCore 對象作為Lua虛擬機,所以密碼就在他這里。LuaViewCore 的初始化方法如下:

myInit 方法實現(xiàn)了屬性的初值賦值等。關(guān)鍵在于 registeLibs 方法。

此方法將所有LuaViewSDK封裝的NativeUI進行了遍歷注冊到Lua環(huán)境中。

而LVClassProtocal 協(xié)議的 +(int) lvClassDefine:(lua_State *)L globalName:(NSString*) globalName 方法,即每個封裝的UI類需要實現(xiàn)的,完成類及其方法注冊到Lua環(huán)境。比如LVImage:

lua是一種嵌入式的語言,可以作為c的擴展,也可以用c來編寫模塊了擴展lua。而在進行數(shù)據(jù)交互的時候,存在這這么一個棧,這個棧的作用是存儲lua和c交互的參數(shù),返回值等。如lua調(diào)用c函數(shù)并傳入?yún)?shù),所有參數(shù)會先壓入這個棧,c函數(shù)執(zhí)行時從棧中獲取參數(shù),執(zhí)行完后,也會把返回值壓入此棧,lua從此棧中獲取返回值。(我個人理解是這樣的,如有誤,煩請指出)

所以,上面LVImage的注冊,首先是將類名和其初始化方法壓棧,通過 lua_setglobal 方法,將棧頂?shù)念惷秃瘮?shù)注冊到lua環(huán)境中,并通過globalName(此處是 "Image")進行標注。如此lua腳本中就可以通過 Image() 來創(chuàng)建LVImage對象。

而下面的 luaL_Reg 結(jié)構(gòu)體,則包含了一組 keyStr -- 方法。則是將這組函數(shù)注冊到Lua環(huán)境中,作為全局函數(shù)。Lua腳本中LVImage對象就可以調(diào)用這些方法。

以上就實現(xiàn)了一個Native控件注入到lua環(huán)境中進行使用。

現(xiàn)有LuaView控件的擴展

上面已經(jīng)解讀了LVImage是如何注冊到Lua環(huán)境中進行使用。當Lua腳本里setImage 設(shè)置圖片,傳入?yún)?shù)是url時,圖片是沒有顯示的,查看LVImage的方法會發(fā)現(xiàn),因為LVImage 的 setWebImageUrl 是沒有實現(xiàn)的。故考慮通過繼承的方式擴展LVImage的功能,讓其支持網(wǎng)絡(luò)圖片的加載。

#import "XQImage.h"

#import "LVHeads.h"

#import <SDWebImage/UIImageView+WebCache.h>

@implementation XQImage

-(void) setWebImageUrl:(NSURL*) url finished:(LVLoadFinished) finished{

[self sd_setImageWithURL:url];

}

@end

XQImage 繼承自LVImage,并且重寫了父類的方法 -(void) setWebImageUrl:(NSURL*) url finished:(LVLoadFinished) finished。(此處采用SDWebImage進行圖片下載展示)

自定義子類實現(xiàn)了,但是Lua環(huán)境注冊的還是父類LVImage,故,lua腳本初始化Image(),還是會初始化父類的實例,故無法調(diào)用到子類的圖片下載賦值方法。父類的注冊,是在LuaView初始化時,那么LuaView初始化后,需要將子類覆蓋父類注冊到lua環(huán)境中,讓 globalName(“Image”)對應(yīng)的是子類:

self.lv[@"Image"] = [XQImage class];

如此后,lua腳本 Image() 創(chuàng)建的就是native的XQImage對象。由此實現(xiàn)網(wǎng)絡(luò)圖片的加載和顯示。

完全自定義類的橋接

上節(jié)通過繼承的方式擴展 LuaView 已封裝的UI控件,并且覆蓋注冊到lua環(huán)境中。那么如何將自定義的一個類,橋接到Lua環(huán)境中使用呢?

正如前面源碼解析,了解了 LuaView封裝的NativeUI 是如何實現(xiàn)的,所以按部就班,照著這個邏輯實現(xiàn)自定義類的橋接。其中最關(guān)鍵的是實現(xiàn) LVClassProtocal 協(xié)議的方法 + (int)lvClassDefine:(lua_State *)L globalName:(NSString *)globalName; 來實現(xiàn)指定類及其初始化方法,全局函數(shù)注冊到 Lua 環(huán)境中,供 Lua 腳本直接使用。

+(int) lvClassDefine:(lua_State *)L globalName:(NSString*) globalName{

[LVUtil reg:L clas:self cfunc:lvNewItem globalName:globalName defaultName:@"XQItemLuaView"];

const struct luaL_Reg memberFunctions [] = {

{"image",? setIconImage},

{"title",? ? title},

{NULL, NULL}

};

lv_createClassMetaTable(L,META_TABLE_CustomView);

luaL_openlib(L, NULL, [LVBaseView baseMemberFunctions], 0);

luaL_openlib(L, NULL, memberFunctions, 0);

const char* keys[] = { "addView", NULL};// 移除多余API

lv_luaTableRemoveKeys(L, keys );

return 1;

}

XQItemLuaView 是我自定義的一個 UIView 的子類,遵循 LVProtocal, LVClassProtocal 協(xié)議。其上有一個 ImageView 和 Label。C函數(shù) lvNewItem 用于初始化一個 XQItemLuaView 的對象,setIconImage 用于根據(jù) Lua 腳本傳入的參數(shù),設(shè)置 iconImageView 的圖片展示。 title 為根據(jù)Lua腳本傳入的參數(shù)字符串,設(shè)置 titleLabel 的text。

LVClassProtocal 是一個靜態(tài)協(xié)議,源碼分析中可以看到,LuaView 加載其擴展類的時候,都是通過初始化 LuaViewCore 時,遍歷所有需要加載的類,調(diào)用其 + (int)lvClassDefine:(lua_State *)L globalName:(NSString *)globalName 方法,實現(xiàn)加載。

而完全自定義的類的加載,最好不要去直接更改其 LuaViewCore 源碼。所以我創(chuàng)建了一個管理自定義類注冊的操作類 XQRegisterManager 。

#import "XQRegisterManager.h"

#import "XQItemLuaView.h"

@implementation XQRegisterManager

/**

自定義類的注冊管理

@param luaState 狀態(tài)機

*/

+(void)registerClassWithLuaState:(lua_State*)luaState{

[XQItemLuaView lvClassDefine:luaState globalName:@"XQItemLuaView"];

}

@end

在 LuaView/LuaViewController 初始化后,去調(diào)用注冊自定義的類。如:

self.lv = [[LView alloc] initWithFrame:lvRect];

[XQRegisterManager registerClassWithLuaState:self.lv.luaviewCore.l];

self.lvCore = [[LuaViewCore alloc]init];

[XQRegisterManager registerClassWithLuaState:self.lvCore.l];

總結(jié)

以上是我一周時間學(xué)習(xí)LuaView的記錄。從簡單運行一個 Lua腳本開始認識這個SDK,到最后分析源碼,來實現(xiàn)自定義類的橋接。下一步的目標是,在此基礎(chǔ)上,研究資源腳本下載實現(xiàn),SDK自帶的debuger工具類的使用,以及當腳本出錯或者下載失敗的降級處理(LuaViewSDK 沒有自帶降級處理,所有運行失敗會有錯誤拋出,需要根據(jù)錯誤,自行處理降級還是顯示失敗頁面)等。

如有紕漏,歡迎指出,謝謝!

Demo地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,622評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,716評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,746評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,991評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,706評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,036評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,029評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,203評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,725評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,451評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,677評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,161評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,857評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,266評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,606評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,407評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,643評論 2 380

推薦閱讀更多精彩內(nèi)容

  • 0. 前言 最近一直在寫Lua腳本,有時候出了問題,不知道是Lua層的問題,還是上游的問題,不知道從何下手。于是我...
    ZSpirytus閱讀 2,420評論 0 3
  • 指令集 lua_capture_error_log lua_use_default_type lua_malloc...
    吃瓜的東閱讀 12,065評論 0 2
  • 第一篇 語言 第0章 序言 Lua僅讓你用少量的代碼解決關(guān)鍵問題。 Lua所提供的機制是C不擅長的:高級語言,動態(tài)...
    testfor閱讀 2,713評論 1 7
  • 今天我很羨慕我們班同學(xué),因為他們讀書筆記寫的好,得了獎狀和不同的小本,他們非常開心。我有點失落,但我想我努...
    碎片幻影閱讀 138評論 0 0