如何用Rust開(kāi)發(fā)一個(gè)FinClip小程序沙箱SDK原生擴(kuò)展
不懂移動(dòng)端開(kāi)發(fā),也可以貢獻(xiàn)移動(dòng)端代碼。
無(wú)限增強(qiáng)FinClip小程序安全運(yùn)行沙箱FinClip小程序安全運(yùn)行沙箱,以SDK的方式供App開(kāi)發(fā)者嵌入,讓自己的App秒變能運(yùn)行小程序的超級(jí)App。在這里,“App”還不僅僅是指iOS或者Android的應(yīng)用,宿主可以強(qiáng)大至配備多核CPU和較多內(nèi)存的PC,也可以是運(yùn)算能力比較有限的嵌入式設(shè)備(embeddeddevices),例如一個(gè)帶觸摸屏的RaspberryPI。
宿主硬件環(huán)境和軟件環(huán)境都各有不同,需要暴露給小程序利用的功能不一樣;此外,不同的商業(yè)環(huán)境下,應(yīng)用所集成的原生能力(例如地圖、支付、加密、音視頻、甚至AR/XR等)也不同。FinClip支持開(kāi)發(fā)者自定義各種API接口,注入至FinClipSDK中,從而以JavaScript的方式供小程序開(kāi)發(fā)者調(diào)用。
通過(guò)這種方式,任何小程序理論上可利用所在宿主環(huán)境的任何技術(shù)能力,而不僅受限于FinClipSDK所提供的標(biāo)準(zhǔn)接口。
官方支持的自定義接口擴(kuò)展方式當(dāng)前FinClip官方支持的自定義接口,需要用所在宿主環(huán)境的原生技術(shù)實(shí)現(xiàn),例如在iOS上需要用Objective-C或Swift開(kāi)發(fā),在Android上則是采用Java/Kotlin等。這個(gè)做法就需要起碼兩個(gè)平臺(tái)的工程師分別開(kāi)發(fā)-當(dāng)然過(guò)去以來(lái)這也不是問(wèn)題,任何手機(jī)端App都不得不維持兩隊(duì)人馬搞兩個(gè)版本。但是當(dāng)你有更多類(lèi)型的終端要支持的時(shí)候,就很麻煩了。
正如在《FinClip小程序+Rust》這個(gè)系列所提到,對(duì)于純邏輯類(lèi)、算法類(lèi)的功能例如音視頻編碼的處理、加解密等等,完全沒(méi)有人機(jī)交互的部分,采用一個(gè)跨平臺(tái)的通用語(yǔ)言來(lái)實(shí)現(xiàn),更加便利。但是我們又都不想去折騰C的代碼,Rust是一個(gè)很好的選擇。作為一種新興的、內(nèi)存安全、線程安全的語(yǔ)言,Rust可跨平臺(tái)編譯,高性能、體積小,尤其適合于設(shè)備端的編程,包括在低算力、低功耗、低內(nèi)存的IoT設(shè)備上開(kāi)發(fā)Heapless代碼;并且,Rust已經(jīng)是Android官方支持的系統(tǒng)語(yǔ)言。
那么,F(xiàn)inClip能否支持開(kāi)發(fā)者用Rust提供對(duì)其安全運(yùn)行沙箱的自定義擴(kuò)展呢?
先看一下FinClip環(huán)境下的技能分工與協(xié)同回答上述問(wèn)題前,在這里我們先想象一下,假如在一個(gè)端到端的應(yīng)用軟件生產(chǎn)鏈路上,以FinClip技術(shù)為各環(huán)節(jié)的“粘合劑”,一個(gè)新型的技術(shù)小組的角色(技能)組成是怎樣的:
iOS/Android/其他終端的“宿主”開(kāi)發(fā)工程師:負(fù)責(zé)宿主“殼”應(yīng)用的開(kāi)發(fā),以及FinClipSDK集成。需要懂ObjC/Swift、Java/Kotlin、乃至Electron/Qt/C++,視乎所要支持的目標(biāo)操作系統(tǒng)
FinClip小程序開(kāi)發(fā)工程師:負(fù)責(zé)各種業(yè)務(wù)功能的前端開(kāi)發(fā),懂HTML/JavaScript以及一些前端框架即可
FinClipSDKExtension開(kāi)發(fā)工程師:負(fù)責(zé)實(shí)現(xiàn)一些通用的邏輯算法、底層的基礎(chǔ)設(shè)施,供各目標(biāo)終端的“宿主”應(yīng)用集成。需要掌握Rust編程
DevOps工程師:如果打算自行運(yùn)行維護(hù)自己的FinClip小程序中心、管理自己的小程序開(kāi)發(fā)者和開(kāi)發(fā)生態(tài),那么就需要工程師去部署運(yùn)行FinClip服務(wù)器端
這是一條起碼的“流水線”,各個(gè)崗位用不同的語(yǔ)言技能,分工越清晰越好,哪怕這些事情都是同一個(gè)人“包打天下”了,也需要自己明確在不同角色下做不同的事,有助于梳理出合理的架構(gòu),在一個(gè)端到端的技術(shù)鏈路上,界定好每一個(gè)環(huán)節(jié)的功能范疇。
Gluecode和生態(tài)FinClip這門(mén)技術(shù),單純從軟件工程角度看,它扮演的是gluecode(粘合劑)的角色,把一個(gè)涉及多種語(yǔ)言、多類(lèi)技術(shù)的軟件系統(tǒng)粘合起來(lái)。Gluecode往往是最繁瑣、也最容易出錯(cuò)的地方,把這個(gè)層面的技術(shù)解決好了,能大大釋放開(kāi)發(fā)者的生產(chǎn)力和創(chuàng)造力。相當(dāng)于流水線搭建好了,每個(gè)環(huán)節(jié)都可以獨(dú)立潤(rùn)滑、豐富、加強(qiáng)。而且每個(gè)環(huán)節(jié)都變得更加專業(yè),甚至形成自己的零部件“供應(yīng)鏈”。
例如,把HTML/JavaScript部分的技術(shù),和設(shè)備端原生技術(shù)對(duì)接好,就引入了大量的可以專注于小程序開(kāi)發(fā)的工程師提供豐富的應(yīng)用場(chǎng)景;把設(shè)備端原生技術(shù)中跨設(shè)備通用的邏輯解耦出來(lái)成為可插拔的“插件”,又可以進(jìn)一步促生僅聚焦這一部分工作的人,產(chǎn)出豐富、高品質(zhì)的插件。只要標(biāo)準(zhǔn)化,就有機(jī)會(huì)形成生態(tài)。
提供端到端的應(yīng)用解決方案,變成是“集大成”,在技術(shù)鏈路各環(huán)節(jié)的“供應(yīng)鏈”中,選取自己需要的零部件,去組裝自己的應(yīng)用。
插件開(kāi)發(fā)者:用Rust實(shí)現(xiàn)FinClipSDK的“插件”正如FinClip小程序的開(kāi)發(fā)者無(wú)需懂得任何iOS/ObjC/Swift、Android/Java/Kotlin的技能知識(shí),僅憑對(duì)HTML/JavaScript的掌握即可開(kāi)發(fā)出有用的應(yīng)用一樣,F(xiàn)inClipSDKExtension的開(kāi)發(fā)者,最好也無(wú)需了解太多操作系統(tǒng)平臺(tái)的編程知識(shí),甚至無(wú)需跟ObjC、Java打交道,即可開(kāi)發(fā)出自己的擴(kuò)展。
我們?cè)诩夹g(shù)是可以做到的。按以下的步驟-注意下述內(nèi)容都發(fā)生在Rust這側(cè),對(duì)ObjC/Java空間的代碼開(kāi)發(fā)要求為零。
準(zhǔn)備構(gòu)建一個(gè)靜態(tài)庫(kù)所需的環(huán)境用cargo創(chuàng)建一個(gè)lib類(lèi)型的項(xiàng)目,例如
cargo?new?--lib?myplugin然后修訂一下所生成的Cargo.toml:
[package]name?=?"myplugin"version?=?"0.1.0"edition?=?"2021"[lib]name?=?"myplugin"#?this?is?needed?to?build?for?iOS?and?Android.crate-type?=?["staticlib",?"lib"][dependencies]serde_json?=?"1.0.81"??#?建議使用這個(gè)crate實(shí)現(xiàn)json對(duì)象序列化#?其他你準(zhǔn)備封裝或者依賴的crate定義準(zhǔn)備注入至FinClip的API在src/lib.rs,開(kāi)始定義和封裝你計(jì)劃提供給原生宿主應(yīng)用開(kāi)發(fā)者注入至其FinClipSDK的函數(shù)。
首先,定義一個(gè)新的類(lèi)型:
type?FinClipCall?=?fn(&String)?->?String;這個(gè)類(lèi)型名字請(qǐng)命名為'FinClipCall',且這個(gè)類(lèi)型所表示的函數(shù)簽名,必須是:
fn(&String)?->?String它實(shí)際上是一個(gè)函數(shù)指針,它能夠指向這樣的函數(shù),例如:
fn?invoke(param:?&String)?->?String?{...}注意這個(gè)類(lèi)型的函數(shù),期望的輸入?yún)?shù)是一個(gè)合法的JSON字符串,返回的也必須是一個(gè)合法的JSON字符串。因?yàn)镕inClip的自定義API,統(tǒng)一用JSON作為入?yún)⒑统鰠ⅲ阌谛〕绦騻?cè)JavaScript代碼的處理。所以在上文推薦引入serde_json這個(gè)crate,幫助做一些JSON相關(guān)的數(shù)據(jù)轉(zhuǎn)換。
其次,開(kāi)始實(shí)現(xiàn)你的函數(shù)實(shí)現(xiàn),例如:
fn?api_drinker(input:?&String)?->?String?{????//?先處理一下入?yún)ⅲ堰M(jìn)來(lái)的字符串檢測(cè)為合法JSON對(duì)象,????//?再用serde_json把它轉(zhuǎn)化為某個(gè)類(lèi)型的參數(shù)對(duì)象,供后續(xù)使用????println!("invoked?with?parameter?{}",?input);????//中間的邏輯算法從略,這里應(yīng)該是你自己的算法,產(chǎn)生的結(jié)果對(duì)象,可以????//用serde_json進(jìn)行Json?serialization????let?john?=?json!({????????"name":?"john?doe",????????"phones":?"1234567"????});????john.to_string()}fn?api_whisky(input:?&String)?->?String?{????//?先處理一下入?yún)ⅲ堰M(jìn)來(lái)的字符串檢測(cè)為合法JSON對(duì)象,????//?再用serde_json把它轉(zhuǎn)化為某個(gè)類(lèi)型的參數(shù)對(duì)象,供后續(xù)使用????println!("invoked?with?parameter?{}",?input);????//中間的邏輯算法從略,這里應(yīng)該是你自己的算法,產(chǎn)生的結(jié)果對(duì)象,可以????//用serde_json進(jìn)行Json?serialization????let?brands?=?json!({????????"whisky":?{????????????"jack":?"daniel",????????????"johny":?"walker",????????????"henry":?"Mckenna",????????????"suntory":?"toki"????????}????});????brands.to_string()}以上以此類(lèi)推,按類(lèi)似的簽名來(lái)實(shí)現(xiàn)你的API。
文字命名你的API名稱并造冊(cè)登記“花名”FinClip小程序側(cè),調(diào)用自定義API的辦法,是通過(guò)API的名字。例如你把一個(gè)API接口命名為'abc',那么這個(gè)接口被注入到FinClipSDK后,它在JavaScript側(cè)的調(diào)用,就是'ft.abc(...)'。
在此,你需要給每一個(gè)自定義函數(shù)一個(gè)文本命名,并映射它們的關(guān)系,這確實(shí)有點(diǎn)像代碼編譯器里面的virtualtable。這里,是我們開(kāi)發(fā)的這個(gè)myplugin項(xiàng)目中另一個(gè)需要注意一下的地方:
pub?unsafe?extern?"C"?fn?myplugin_register_apis()?->?*mut?HashMap<String,?FinClipCall>?{????let?mut?map:?HashMap<String,?FinClipCall>?=?HashMap::new();????map.insert("get_drinker".to_string(),?api_drinker);????map.insert("get_whisky".to_string(),?api_whisky);????Box::into_raw(Box::new(map))}這個(gè)函數(shù)做了什么事情呢?雖然只有幾行代碼,有必要解釋一下:
首先,我們初始化了一個(gè)HashMap,這個(gè)HashMap的Key和Value的類(lèi)型,分別是String和之前我們自定義的函數(shù)指針類(lèi)型FinClipCall
其次,開(kāi)始造“花名冊(cè)”,也就是直接粗暴的窮舉所有要輸出的函數(shù),把它們?nèi)紿ashMap,完成造冊(cè)
最后,是比較“技巧性”的地方,就是如何把這個(gè)HashMap對(duì)象返回出去。記得我們的這些函數(shù),最終必須暴露給iOS、Android等平臺(tái)上的宿主應(yīng)用,以便于這些應(yīng)用的開(kāi)發(fā)者,把這些函數(shù)注入到FinClipSDK,所以這里是你的Rust代碼和你的合作伙伴的ObjC或者Java/JNI代碼的臨界點(diǎn)。此處我們用了一個(gè)辦法,就是把HashMap這個(gè)只存在于Rust側(cè)的collections類(lèi)型(就像Java里的collectionclasses只存在于Java一樣),包裝在一個(gè)類(lèi)似C++的smartpointer這樣的Box里,返回這一整個(gè)數(shù)據(jù)結(jié)構(gòu)在內(nèi)存里的地址
至此,我們的工作基本上完成90%,是不是很簡(jiǎn)單?
把提供“花名冊(cè)”的函數(shù)暴露給其他語(yǔ)言使用到這一步為止,我們都是在Rust世界中折騰。但是最終這些成果必須被外界發(fā)現(xiàn)和使用。最后異步,就是把“花名冊(cè)”送到異構(gòu)語(yǔ)言的世界中,我們需要利用RustFFI(ForeignFunctionInterface)去讓Rust編譯器編譯上述代碼時(shí),生成C風(fēng)格的代碼庫(kù),所以對(duì)上述函數(shù)還要做一點(diǎn)補(bǔ)充:
#[no_mangle]pub?unsafe?extern?"C"?fn?myext_register_apis()?->?*mut?HashMap<String,?FinClipCall>?{????let?mut?map:?HashMap<String,?FinClipCall>?=?HashMap::new();????map.insert("api_drinker".to_string(),?api_drinker);????map.insert("api_whisky".to_string(),?api_whisky);????Box::into_raw(Box::new(map))}'no_mangle'告訴Rust編譯器,編譯時(shí)不要混淆或改變'myplugin_register_api()'這個(gè)函數(shù)名字,否則ObjC或者Java/JNI側(cè)就無(wú)法知道用什么名字調(diào)用了。
注意上述函數(shù)的聲明里,有'unsafe'和'extern"C"'的標(biāo)識(shí),extern好理解,就是標(biāo)識(shí)這個(gè)函數(shù)是供異構(gòu)語(yǔ)言以C函數(shù)調(diào)用的方式使用,'unsafe'涉及Rust關(guān)于什么才是內(nèi)存安全、線程安全的規(guī)則或者說(shuō)思想,詳情讀者可自行了解。在此,主要是我們用到了'Box::into_raw'這個(gè)函數(shù),即我們把一個(gè)只在Rust里面才能解析的數(shù)據(jù)結(jié)構(gòu),通過(guò)一個(gè)原始指針把它丟到C側(cè)了,相當(dāng)于這一片內(nèi)存被異構(gòu)語(yǔ)言下的代碼“持有”,其內(nèi)存安全不再受Rust的監(jiān)控和保障,所以是不安全的。
這里有一個(gè)問(wèn)題,就是:既然Rust側(cè)的HashMap無(wú)法被C側(cè)解析,把這玩意兒的原始指針丟過(guò)去有什么用呢?有用,因?yàn)樗鼘?shí)際上相當(dāng)于一個(gè)不透明指針,是一個(gè)由宿主應(yīng)用側(cè)“持有”的handle,當(dāng)宿主需要調(diào)用Rust的函數(shù)時(shí),把這個(gè)handle傳回來(lái)就是了。
有去有回,記得防止內(nèi)存泄漏在RustFFI,每一次產(chǎn)生的'into_raw'操作,最終都必須有一次對(duì)應(yīng)的'from_raw'操作。前者把一片Rust管理的內(nèi)存的控制權(quán)轉(zhuǎn)移出去了,后者是外部的異構(gòu)語(yǔ)言下的代碼必須把該內(nèi)存的控制權(quán)還給Rust,否則內(nèi)存泄漏就發(fā)生了。所以,最后我們還需要增加一個(gè)函數(shù),供異構(gòu)語(yǔ)言在使用完上面的東西后,記得通知Rust回收:
#[no_mangle]pub?unsafe?extern?"C"?fn?myext_release(ptr:?*mut?HashMap<String,?FinClipCall>)?{????if?!ptr.is_null()?{???????drop(Box::from_raw(ptr));????}}注意,調(diào)用這個(gè)函數(shù)是宿主應(yīng)用開(kāi)發(fā)者的責(zé)任,所以必須在你的plugin的使用說(shuō)明文檔中向他們強(qiáng)調(diào)。這個(gè)比較丑陋但似乎沒(méi)有什么好辦法,跨語(yǔ)言的調(diào)用總是有一些小不便。
Re-cap:用Rust開(kāi)發(fā)一個(gè)FinClipSDK擴(kuò)展的步驟實(shí)際上是非常簡(jiǎn)單和自由的,沒(méi)有什么特殊庫(kù)或者協(xié)議需要去繼承實(shí)現(xiàn),就是“徒手”寫(xiě)一個(gè)Rustlib,只要它包含以下要素:
準(zhǔn)備提供給FinClip小程序調(diào)用的函數(shù),以JSON字符串為入?yún)⒑统鰠ⅰ:瘮?shù)遵循'fn(&String)->String'的簽名。這些函數(shù)多少個(gè)都行,叫什么名字也無(wú)妨,自由選擇
給上面這些函數(shù)造一個(gè)“花名冊(cè)”,花名冊(cè)的數(shù)據(jù)結(jié)構(gòu)必須是以HashMap去存儲(chǔ)“名字”->“函數(shù)指針”的映射關(guān)系,其中“名字”是你打算讓外面的世界知道和使用的函數(shù)名(字符串),函數(shù)指針則是指向上述函數(shù)簽名的類(lèi)型
產(chǎn)生“花名冊(cè)”的函數(shù),需要使用RustFFI,也就是標(biāo)記'no_mangle',以及聲明為unsafe。“花名冊(cè)”的數(shù)據(jù)結(jié)構(gòu)(HashMap),包在一個(gè)不透明指針(opaquepointer)里,丟出去給異構(gòu)語(yǔ)言代碼(也就是準(zhǔn)備使用這個(gè)plugin的宿主)持有備用。這個(gè)返回“花名冊(cè)”的函數(shù),名字叫什么也無(wú)法,你只需要在自己的說(shuō)明文檔里注明(說(shuō)明文檔你總得有吧?)
提供一個(gè)釋放“花名冊(cè)”數(shù)據(jù)結(jié)構(gòu)內(nèi)存的函數(shù),同樣的,函數(shù)名字隨意,告訴宿主開(kāi)發(fā)者是什么即可
交付物是一個(gè)靜態(tài)庫(kù)正如《FinClip小程序+Rust》這個(gè)系列里所介紹過(guò),Rust代碼需要進(jìn)行跨平臺(tái)編譯,構(gòu)建出aarch64-apple-ios、x86_64-apple-ios以及Android相關(guān)的目標(biāo)架構(gòu)下的二進(jìn)制庫(kù)。例如生成適合在iossimulator和ios設(shè)備上運(yùn)行的universallibrary:
cbindgen?src/lib.rs?-l?c?>?myplugin.hcargo?lipo?--release最終你交付給宿主應(yīng)用開(kāi)發(fā)者的內(nèi)容應(yīng)該包括:
一個(gè)'libmyplugin.a'文件、一個(gè)'myplugin.h'的頭文件
一個(gè)使用說(shuō)明,包括:
如何獲得你所提供的API的花名錄,例如你提供了一個(gè)函數(shù)"myplugin_register_apis"
如何通知你釋放花名錄內(nèi)存,例如你提供了一個(gè)函數(shù)"myplugin_release"
你的API花名錄中,每一個(gè)API的“花名”,以及該API期望的JSON入?yún)ⅲ祷氐腏SON出參
至此,作為一個(gè)Rust開(kāi)發(fā)者提供FinClipSDKExtension的使命完成。再次明確,'myplugin'的頭文件名字、創(chuàng)建花名錄數(shù)據(jù)結(jié)構(gòu)和釋放花名錄內(nèi)存的函數(shù)名字,都是開(kāi)發(fā)者自由決定。
App開(kāi)發(fā)者:如何使用Rust的插件接下來(lái),就輪到宿主應(yīng)用的開(kāi)發(fā)者怎么使用了。宿主應(yīng)用,就是運(yùn)行在iOS、Android或者其他設(shè)備端的應(yīng)用軟件,它嵌入了FinClipSDK從而獲得運(yùn)行小程序能力。
編寫(xiě)代碼僅需加三行集成FinClipSDK詳見(jiàn)官網(wǎng),《FinClip小程序+Rust》也有介紹,不在此贅述。以下以iOSApp的集成上述myplugin為例,在AppDelegate.m增加三行代碼(下面有注解的三行):
[package]name?=?"myplugin"version?=?"0.1.0"edition?=?"2021"[lib]name?=?"myplugin"#?this?is?needed?to?build?for?iOS?and?Android.crate-type?=?["staticlib",?"lib"][dependencies]serde_json?=?"1.0.81"??#?建議使用這個(gè)crate實(shí)現(xiàn)json對(duì)象序列化#?其他你準(zhǔn)備封裝或者依賴的crate0上面的代碼,AppDelegate.m,在用xcode創(chuàng)建ObjC項(xiàng)目時(shí)自動(dòng)生成,我們?cè)谶@里初始化了FinClipSDK(詳情見(jiàn)官網(wǎng),或《FinClip小程序+Rust》系列。在此基礎(chǔ)上,再安裝myplugin,代碼非常簡(jiǎn)單:
引用myplugin.h
調(diào)用FinClipExt的installFor方法,入?yún)槌跏蓟腇inClipSDKhandle,以及myplugin的API“花名錄”對(duì)象(由myplugin_register_apis產(chǎn)生)
在宿主應(yīng)用的生命周期中,找合適的階段(例如應(yīng)用退出)釋放“花名錄”內(nèi)存(由myplugin_release提供)
這里的'FinClipExt',負(fù)責(zé)了把RustAPI轉(zhuǎn)換成ObjC方法再注入到FinClipSDK中。
編譯構(gòu)建需要鏈接靜態(tài)庫(kù)myplugin這個(gè)庫(kù),從上面我們已經(jīng)可以看到,沒(méi)有使用任何與FinClip直接相關(guān)的特殊的庫(kù),唯一的約束,就是兩個(gè)規(guī)范:
自定義一個(gè)叫'FinClipCall'的函數(shù)指針類(lèi)型,函數(shù)簽名必須是'fn(&String)->String'
提供一個(gè)函數(shù),能生成你計(jì)劃提供給FinClip小程序開(kāi)發(fā)者使用的自定義API的“花名錄”,它的數(shù)據(jù)結(jié)構(gòu),必須是'HashMap<String,FinClipCall>'
僅此而已。那么這個(gè)庫(kù)是怎么被注入到FinClipSDK并能被小程序調(diào)用的呢?魔術(shù)在于,宿主App的開(kāi)發(fā)者,在其項(xiàng)目中引入一個(gè)叫l(wèi)ibfincliprust.a的靜態(tài)庫(kù)(這個(gè)庫(kù)目前尚不是FinClip官方支持的標(biāo)準(zhǔn)工具,只是我個(gè)人的項(xiàng)目,且目前僅提供iOS版本。有興趣的朋友可以去優(yōu)化,歡迎提供Android的版本,源代碼在GitHub上,由ObjC和Rust代碼組成)。作為使用者,無(wú)需關(guān)注其中的實(shí)現(xiàn),只要下載這個(gè)靜態(tài)庫(kù),編譯構(gòu)建App的時(shí)候指定依賴與鏈接它即可。
最后,當(dāng)然也必須把所要安裝的myplugin的靜態(tài)庫(kù),libmyplugin.a,引入項(xiàng)目,一同構(gòu)建。
作為宿主應(yīng)用開(kāi)發(fā)者,引入一個(gè)叫myplugin的FinClipSDKExtension供FinClip小程序開(kāi)發(fā)者調(diào)用的使命,也完成了。
小程序開(kāi)發(fā)者:如何調(diào)用Rust接口上述myplugin的“花名錄”里,有兩個(gè)暴露給小程序的API,分別是'api_drinker'和'api_whisky'。這兩個(gè)用Rust寫(xiě)出來(lái)的、以JSON字符串為入?yún)⒑统鰠⒌暮瘮?shù),經(jīng)過(guò)libfincliprust.a的一些“魔術(shù)”操作,被轉(zhuǎn)換成ObjC的method,并被動(dòng)態(tài)注入到FinClipSDK中。要使用這些API,F(xiàn)inClip小程序開(kāi)發(fā)者需要在自己的小程序項(xiàng)目根目錄下編寫(xiě)一個(gè)FinClipConf.js:
[package]name?=?"myplugin"version?=?"0.1.0"edition?=?"2021"[lib]name?=?"myplugin"#?this?is?needed?to?build?for?iOS?and?Android.crate-type?=?["staticlib",?"lib"][dependencies]serde_json?=?"1.0.81"??#?建議使用這個(gè)crate實(shí)現(xiàn)json對(duì)象序列化#?其他你準(zhǔn)備封裝或者依賴的crate1此后,在JavaScript中對(duì)這些API的調(diào)用,只需要通過(guò)'ft'對(duì)象即可進(jìn)行,例如'ft.get_drinker'。
總結(jié)在一個(gè)現(xiàn)代的軟件項(xiàng)目中,多語(yǔ)言混合編程是難以避免的-不同的語(yǔ)言在端到端技術(shù)鏈路上適合于解決不同環(huán)節(jié)的問(wèn)題,但是也難免導(dǎo)致集成、融合的麻煩,往往是影響開(kāi)發(fā)效率、引起諸多麻煩的。例如跨語(yǔ)言的轉(zhuǎn)接,涉及API接口的產(chǎn)生和數(shù)據(jù)結(jié)構(gòu)在異構(gòu)語(yǔ)言中反反復(fù)復(fù)的“翻譯”,寫(xiě)gluecode非常繁瑣。FinClip更平滑的解決了前端的異構(gòu)技術(shù)對(duì)接問(wèn)題,在本文中,進(jìn)一步介紹了一個(gè)較為“透明”的方法,讓完全不熟悉JavaScript、不懂ObjC、不了解終端開(kāi)發(fā)的工程師,能通過(guò)Rust這種強(qiáng)大的語(yǔ)言開(kāi)發(fā)出邏輯通用的FinClipSDK擴(kuò)展,最終供小程序開(kāi)發(fā)者使用。
本文的示范代碼在?https://github.com/kornhill/finclip-rust-ext-demo?。
原文:https://juejin.cn/post/7096359509529985055
如何用Rust開(kāi)發(fā)一個(gè)FinClip小程序沙箱SDK原生擴(kuò)展
正如FinClip小程序的開(kāi)發(fā)者無(wú)需懂得任何iOS\/ObjC\/Swift、Android\/Java\/Kotlin的技能知識(shí),僅憑對(duì)HTML\/JavaScript的掌握即可開(kāi)發(fā)出有用的應(yīng)用一樣,FinClipSDKExtension的開(kāi)發(fā)者,最好也無(wú)需了解太多操作系統(tǒng)平臺(tái)的編程知識(shí),甚至無(wú)需跟ObjC、Java打交道,即可開(kāi)發(fā)出自己的擴(kuò)展。 我們?cè)诩夹g(shù)是可以做到的。按以下的步驟-注意...
FinClip小程序+Rust(五):用內(nèi)聯(lián)SVG實(shí)現(xiàn)二維碼
在宿主App部分,我們將新功能集成到FinClipExt中,修改了`generate_wallet`函數(shù),使其在生成錢(qián)包地址后,利用Rust代碼生成對(duì)應(yīng)的SVG數(shù)據(jù),并轉(zhuǎn)換為Base64編碼,最終將結(jié)果放入`public_address_qrcode`字段中返回。接下來(lái),我們來(lái)到了小程序展示部分,以簡(jiǎn)潔的方式在界面上添加了展示二維碼的區(qū)域。通過(guò)CSS,我...
前端移動(dòng)端開(kāi)發(fā)app需要那些技術(shù)? - 知乎
Tauri框架由Rust語(yǔ)言實(shí)現(xiàn),專注于多平臺(tái)部署,主打優(yōu)化、安全和前端獨(dú)立性,借助Rust的特性顯著提升框架價(jià)值。打包體積小、開(kāi)發(fā)效率高,是Tauri的顯著優(yōu)勢(shì)。FinClip作為一款小程序容器技術(shù),提供基于瀏覽器內(nèi)核的動(dòng)態(tài)語(yǔ)言(JS)和聲明式View構(gòu)建,兼容主流小程序技術(shù),支持跨端運(yùn)行,為開(kāi)發(fā)者帶來(lái)高效的小程序開(kāi)...
盤(pán)點(diǎn)| 跨平臺(tái)桌面應(yīng)用開(kāi)發(fā)的5大主流框架
其次,wxPython和pyqt利用Python語(yǔ)言,分別封裝wxWidgets和Qt,提供不同操作系統(tǒng)風(fēng)格的桌面應(yīng)用開(kāi)發(fā)。wxWidgets基于操作系統(tǒng)API,適合定制化開(kāi)發(fā),但可能有一些小問(wèn)題。相比之下,Tauri由Rust語(yǔ)言驅(qū)動(dòng),注重優(yōu)化、安全和前端獨(dú)立性,其輕量級(jí)特性使得開(kāi)發(fā)高效,且支持HTML+CSS。FinClip則以凡泰極客的小程序容器技術(shù)...
盤(pán)點(diǎn)| 跨平臺(tái)桌面應(yīng)用開(kāi)發(fā)的5大主流框架
3. Tauri 以Rust語(yǔ)言打造的Tauri,以其高性能、安全性和前端的獨(dú)立性脫穎而出。Rust的特性確保了其在可靠性上的卓越表現(xiàn),尤其適合對(duì)穩(wěn)定性和性能有高要求的項(xiàng)目。4. FinClip 由凡泰極客研發(fā)的FinClip,是基于小程序容器技術(shù)的創(chuàng)新。它兼容了互聯(lián)網(wǎng)主流小程序技術(shù),采用HTML+CSS的簡(jiǎn)潔語(yǔ)法,使得數(shù)據(jù)共享...
相關(guān)評(píng)說(shuō):
天全縣雙轉(zhuǎn): ______ 不必. STATIC只是表明該變量只與類(lèi)相關(guān),既該變量在該類(lèi)所創(chuàng)建的對(duì)象中都共用同一個(gè)內(nèi)存空間.
天全縣雙轉(zhuǎn): ______ final定義的變量可以看做一個(gè)常量,不能被改變; final定義的方法不能被覆蓋; final定義的類(lèi)不能被繼承. final static 就是再加上static的特性就可以了 static 和final是沒(méi)有直接關(guān)系的 static 是在內(nèi)存中分配一塊區(qū)域,供整個(gè)類(lèi)通用,所有的類(lèi)的...
天全縣雙轉(zhuǎn): ______ -謝謝-var Finalgrade =MES*0.35 + FES*0.45 + A1*1/30 + A2*1/30 + A3*1/30 + DB1*1/30 + DB2*1...
天全縣雙轉(zhuǎn): ______ 1.根據(jù)Rust的特點(diǎn)和定位適用于對(duì)控制性比較強(qiáng),對(duì)性能很敏感的領(lǐng)域,比如底層軟件(OS/driver),基礎(chǔ)系統(tǒng)軟件(Compiler/VM/DB等),性能要求比較高的應(yīng)用軟件(瀏覽器/圖形圖像處理/游戲/高性能服務(wù)器軟件等,或者可能包括近來(lái)很火...
天全縣雙轉(zhuǎn): ______ 如果你之前使用了一個(gè)有Windows下GUI安裝向?qū)У?msi文件安裝了Rust,請(qǐng)到控制面板->程序與功能(添加/刪除程序)里卸載.設(shè)置安裝路徑.默認(rèn)它會(huì)把Rust裝到你的~/.rustup 里,依賴庫(kù)下載到~/.cargo 里.需要修改的話,設(shè)置環(huán)境變量RUSTUP_HOME 和 CARGO_HOME 分別修改這兩個(gè)值(例如D:\rustup和D:\cargo).配置代理#1:國(guó)內(nèi)有些地區(qū)訪問(wèn)Rustup的服務(wù)器不太順暢,可以配置中科大的Rustup鏡像:設(shè)置環(huán)境變量.
天全縣雙轉(zhuǎn): ______ import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; public class Btn extends JFrame { private static final long se...
天全縣雙轉(zhuǎn): ______ 字符串效率很低用數(shù)組來(lái)做效率高很多下面是求1000項(xiàng)的Fibonaci數(shù)列,你參考下改成求階乘也一樣public class Fibonacci{ private static final int MAX_LENGTH = 1000000; privat...
天全縣雙轉(zhuǎn): ______ typedef struct _Data{ double *FINAL; strcut _Data *next;}Data;//表示行數(shù)據(jù),假設(shè)有m行,n列,則分配m個(gè)該節(jié)點(diǎn)的內(nèi)存,而且每行中的FINAL的個(gè)數(shù)也可以動(dòng)態(tài)分...
天全縣雙轉(zhuǎn): ______ 創(chuàng)建這個(gè)新語(yǔ)言的目的是為了解決一個(gè)頑疾:軟件的演進(jìn)速度大大低于硬件的演進(jìn),軟件在語(yǔ)言級(jí)別上無(wú)法真正利用多核計(jì)算帶來(lái)的性能提升.Rust是針對(duì)多核體系提出的語(yǔ)言,并且吸收一些其他動(dòng)態(tài)語(yǔ)言的重要特性,比如不需要管理內(nèi)存,比如不會(huì)出現(xiàn)Null指針等等.
天全縣雙轉(zhuǎn): ______ static是靜態(tài)修飾關(guān)鍵字,可以修飾變量和程序塊以及類(lèi)方法:當(dāng)你定義一個(gè)static的變量的時(shí)候jvm會(huì)將將其分配在內(nèi)存堆上,所有程序?qū)λ囊枚紩?huì)指向這一個(gè)地址而不會(huì)重新分配內(nèi)存;修飾一個(gè)程序塊的時(shí)候(也就是直接將代碼寫(xiě)在static{...}中)時(shí)候,虛擬機(jī)就會(huì)優(yōu)先加載靜態(tài)塊中代碼,這主要用于系統(tǒng)初始化;當(dāng)修飾一個(gè)類(lèi)方法時(shí)候你就可以直接通過(guò)類(lèi)來(lái)調(diào)用而不需要新建對(duì)象.final可以修飾變量、方法及類(lèi),當(dāng)你定義一個(gè)final變量時(shí),jvm會(huì)將其分配到常量池中,程序不可改變其值;當(dāng)你定義一個(gè)方法時(shí),改方法在子類(lèi)中將不能被重寫(xiě);當(dāng)你修飾一個(gè)類(lèi)時(shí),該類(lèi)不能被繼承.