摘要:在中,我們可以引入框架,這樣,我們可以層來(lái)操作層代碼的執(zhí)行。都會(huì)發(fā)送相應(yīng)的消息給。在端,由于只有暴露在全局的函數(shù)聲明才能夠讓端訪問(wèn),這就限制了端的靈活性。我們有理由憧憬未來(lái)在和下更方便的集成引擎來(lái)完成建議的雙向通信。
JavaScriptCore引擎
????我們都知道WebKit是個(gè)渲染引擎,簡(jiǎn)單來(lái)說(shuō)負(fù)責(zé)頁(yè)面的布局,繪制以及層的合成,但是WebKit工程中不僅僅有關(guān)于渲染相關(guān)的邏輯,也集成了默認(rèn)的javascript引擎--JavaScriptCore,目前Safari的js引擎也基于JSC構(gòu)建,不過(guò)有一些私有的優(yōu)化,總體性能相差不大。JSC的執(zhí)行理念比較符合傳統(tǒng)的引擎邏輯,它包括了2部分:解釋器和簡(jiǎn)單方法JIT。解釋器比較容易理解,針對(duì)某種類型的文件解釋執(zhí)行,在JSC中,它的目標(biāo)文件是由代碼構(gòu)建的語(yǔ)法樹(shù)生成的字節(jié)碼文件,類似于java中的字節(jié)碼,不過(guò)在JSC中字節(jié)碼的執(zhí)行是在基于寄存器的虛擬機(jī)中而不是基于棧,好處在于可以方便的在ARM架構(gòu)處理器中使用三地址指令,減少了次數(shù)較多的出棧和入棧等指令分派以及耗時(shí)的內(nèi)存IO;JIT在java虛擬機(jī)中應(yīng)用比較多,針對(duì)執(zhí)行較多次的熱點(diǎn)方法進(jìn)行編譯為本地方法,執(zhí)行效率更高,JSC中的JIT同理。
????在iOS7中,我們可以引入JSC框架,這樣,我們可以oc層來(lái)操作js層代碼的執(zhí)行。另外JSC暴露了許多C層面的接口,我們也可以在底層來(lái)構(gòu)建自定義的js執(zhí)行環(huán)境,操作執(zhí)行js代碼,可控執(zhí)行可擴(kuò)展性更強(qiáng)。
????既然有了這么給力的引擎,我們?cè)跇?gòu)建hybrid app時(shí)可以使用JSC來(lái)代替cordova的webViewJavascriptBridge框架完成簡(jiǎn)易的接口暴露,未來(lái)在oc層逐漸可以將UI組件模塊化,并通過(guò)JSExport暴露接口,由js層負(fù)責(zé)調(diào)用相應(yīng)模塊的初始化方法完成界面的hybrid化。
??oc端初始化一個(gè)js執(zhí)行上下文JSContext對(duì)象很容易, [[JSContext alloc] init]即可,但是在hybrid app中,通過(guò)這種方式初始化JSContext與承載頁(yè)面的UIWebVIew并不是同一個(gè)js環(huán)境,因此我們需要獲取UIWebView對(duì)應(yīng)的JSContext。但是apple官方并未提供相關(guān)的方法,不過(guò)這邊難不倒某些人,有些人發(fā)現(xiàn),通過(guò)KVC的方式可獲取UIWebView對(duì)應(yīng)的JSContext,方式如下[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]。一旦獲取到對(duì)應(yīng)的JSContext,我們可以做的就有很多了。
// 獲取對(duì)應(yīng)的JSContext JSContext *context=[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; // 設(shè)置JSContext的錯(cuò)誤處理函數(shù) [context setExceptionHandler:^(JSContext *context, JSValue *value) { NSLog(@"oc catches the exception: %@", value); }]; // 組件化某個(gè)功能類或UIController ShowjoyFad *sf=[ShowjoyFad new]; // 暴露改類至JSContext中,在js層的全局屬性中我們可以訪問(wèn)該類,如window.showjoyFad context[@"showjoyFad"]=sf; context[@"ViewController"] = self; // 引用js層定義的函數(shù) JSValue * abc = context[@"abc"]; // 執(zhí)行 JSValue * ret = [abc callWithArguments:@[@"helloworld"]]; NSLog(@"ret: %@",[ret toString]);
????通過(guò)簡(jiǎn)單的例子可以很明顯的看出JSC通信的簡(jiǎn)潔性,與android的WebView通信類似,native端可以直接講接口注入到j(luò)s上下文中,js在何時(shí)的時(shí)機(jī)調(diào)用函數(shù)。但是這里涉及到一個(gè)比較棘手的問(wèn)題,JSContext的獲取實(shí)在UIWebView的那個(gè)階段呢?
????我做過(guò)一個(gè)測(cè)試:首先在UIWebView的webViewDidStartLoad階段創(chuàng)建JSContext并暴露oc端的方法,在加載一級(jí)頁(yè)面時(shí)js正常調(diào)用oc的方法,而跳轉(zhuǎn)到二級(jí)頁(yè)面中卻無(wú)法執(zhí)行oc的方法;而在webViewDidStartLoad階段由于并未加載完js文件, 因此js層定義的函數(shù)在oc端無(wú)法執(zhí)行。
????其次,在webVIewDidFinishLoad階段創(chuàng)建JSContext并透出oc方法,由于加載js階段在webVIewDidFinishLoad階段之前,因此一級(jí)頁(yè)面js無(wú)法調(diào)用oc方法,但是二級(jí)頁(yè)面同理也是如此,但是由于js代碼是在iOS的UI線程執(zhí)行,因此為了讓js可以調(diào)用oc方法,可以通過(guò)在js設(shè)置setTimeout來(lái)讓任務(wù)放到執(zhí)行隊(duì)列的末端,先執(zhí)行oc層的webVIewDidFinishLoad方法,待任務(wù)完成后再執(zhí)行js中的異步代碼,通過(guò)這種方式可以完成js調(diào)用oc方法;反過(guò)來(lái),oc層調(diào)用js函數(shù)沒(méi)有任何問(wèn)題,因?yàn)樵趙ebVIewDidFinishLoad階段js代碼已執(zhí)行完畢(除了異步代碼)。
????為此,可以通過(guò)實(shí)現(xiàn)一個(gè)簡(jiǎn)易的框架來(lái)完成js層和oc層的交互,為了更好的兼容性,只有在webVIewDidFinishLoad階段創(chuàng)建JSContext。而在js層則有兩種方式來(lái)監(jiān)測(cè)并執(zhí)行oc的方法:
1,在oc層的webVIewDidFinishLoad階段,暴露oc接口之后,通過(guò)JSContext或者UIWebView的stringByEvluateJavascriptString方法構(gòu)建一個(gè)```webViewDidFinishLoad```事件,js端進(jìn)行偵聽(tīng)并調(diào)用 2,簡(jiǎn)單的通過(guò)setTimeout將js的執(zhí)行順序排至隊(duì)列末端
????通過(guò)上述方法,構(gòu)建了一個(gè)簡(jiǎn)單的JSCBridge,但是缺點(diǎn)也很明顯,對(duì)oc端接口暴露時(shí)機(jī)有硬性要求,并且js執(zhí)行oc端的代碼始終是異步,有違我們的初衷。
為何放棄第一種方案 UIWebView的JSContext獲取????上篇中,我們通過(guò)簡(jiǎn)單的kvc獲取UIWebVIew的JSContext,但是實(shí)際上,apple并未給開(kāi)發(fā)者提供訪問(wèn)UIWebView的方法,雖然通過(guò)KVC可達(dá)到目標(biāo),但是當(dāng)APP采用該種hack方法時(shí),有很大幾率不能通過(guò)APP Store的審核,這對(duì)于一個(gè)基于上線的商業(yè)APP而言是難以忍受的,所以我們必須尋找另一種方法來(lái)獲取UIWebView的JSContext而且足夠安全易用,因此我們需轉(zhuǎn)移目光。
解決 WebFrameLoadDelegate????在OS X中,WebFrameLoadDelegate負(fù)責(zé)WebKit與NSWebView的通信,由于NSWebView內(nèi)部仍然使用WebKit渲染引擎,若要偵聽(tīng)渲染過(guò)程中的一系列事件,則必須使用WebFrameLoadDelegate對(duì)象:
????????1、加載過(guò)程:
在一個(gè)訪問(wèn)一個(gè)網(wǎng)頁(yè)的的整個(gè)過(guò)程,包括開(kāi)始加載,加載標(biāo)題,加載結(jié)束等。webkit都會(huì)發(fā)送相應(yīng)的消息給WebFrameLoadDelegate 。 webView:didStartProvisionalLoadForFrame:開(kāi)始加載,在這里獲取加載的url webView:didReceiveTitle:forFrame:獲取到網(wǎng)頁(yè)標(biāo)題 webView:didFinishLoadForFrame:頁(yè)面加載完成
????????2、錯(cuò)誤的處理:
加載的過(guò)程當(dāng)中,有可能會(huì)發(fā)生錯(cuò)誤。錯(cuò)誤的消息也會(huì)發(fā)送給WebFrameLoadDelegate 。我們可以在這兩個(gè)函數(shù)里面對(duì)錯(cuò)誤信息進(jìn)行處理 webView:didFailProvisionalLoadWithError:forFrame: 這個(gè)錯(cuò)誤發(fā)生在請(qǐng)求數(shù)據(jù)之前,最常見(jiàn)是發(fā)生在無(wú)效的URL或者網(wǎng)絡(luò)斷開(kāi)無(wú)法發(fā)送請(qǐng)求 webView:didFailLoadWithError:forFrame: 這個(gè)錯(cuò)誤發(fā)生在請(qǐng)求數(shù)據(jù)之后
????可是在iOS中呢?我嘗試過(guò),并沒(méi)有WebFrameLoadDelegate這個(gè)對(duì)象,看來(lái)iOS中的WebKit框架并未提供UIWebView這么多的接口,但是有些人通過(guò)WebKit的源碼還是發(fā)現(xiàn)了一二,他就是Nick Hodapp。
Nick的發(fā)現(xiàn)????在iOS中,盡管沒(méi)有暴露WebFrameLoadDelegate,但是在具體實(shí)現(xiàn)上仍會(huì)判斷WebKit的implement有沒(méi)有實(shí)現(xiàn)這個(gè)協(xié)議的某些方法,如果實(shí)現(xiàn)則仍會(huì)執(zhí)行,而且在webit的WebFrameLoaderClient.mm文件中,
if (implementations->didCreateJavaScriptContextForFrameFunc) { CallFrameLoadDelegate(implementations->didCreateJavaScriptContextForFrameFunc, webView, @selector(webView:didCreateJavaScriptContext:forFrame:), script.javaScriptContext(), m_webFrame.get()); }
會(huì)判斷當(dāng)前的對(duì)象有沒(méi)有實(shí)現(xiàn)webView:didCreateJavaScriptContext:forFrame:方法,有則執(zhí)行。該方法會(huì)傳遞三個(gè)參數(shù),第一個(gè)是與webkit通信的WebView(此WebView并不是UIWebVIew,Nick層做過(guò)測(cè)試通過(guò)獲取的WebView并不能遍歷到我們需要的UIWebVIew,因此推測(cè),這個(gè)WebView是一個(gè)UIView的proxy對(duì)象,不是UIView類);第二個(gè)則是我們想要獲取的JSContext;第三個(gè)參數(shù)是webkit框架中的WebFrame對(duì)象,與我們的期望無(wú)關(guān)。
????為了讓webkit執(zhí)行這個(gè)函數(shù),我們必須讓對(duì)象實(shí)現(xiàn)這個(gè)方法。由于所有的OC對(duì)象都繼承自NSObject對(duì)象,因此我們可以在NSObject對(duì)象上實(shí)現(xiàn)該方法,這樣可以保證該段代碼可以在webkit框架中執(zhí)行。
????其次,我們既然獲取到了JSContext,但是并不知道JSContext與UIWebVIew的對(duì)應(yīng)關(guān)系,我們的ViewController中可能會(huì)有多個(gè)UIWebView,如何將獲取的JSContext與UIWebview對(duì)應(yīng)起來(lái)也是一個(gè)難題。在此處有一個(gè)簡(jiǎn)單的方法,就是獲取所有的UIWebView對(duì)象,在每個(gè)對(duì)象中執(zhí)行一段js代碼,在js上下文設(shè)置一個(gè)變量做為標(biāo)記,然后在我們獲取的JSContext中判斷該變量是否與遍歷的UIWebVIew對(duì)象中的對(duì)象是否相等來(lái)獲取。這樣,我們可以在UIWebView的webViewDidStartLoad和webViewDidFinishLoad之間獲取到JSContext,進(jìn)行oc和js的雙向通信。
完善????我們通過(guò)上節(jié)的闡述,大致明白了Nick的思路,因此可以通過(guò)協(xié)議和類別來(lái)完成這種通信機(jī)制,當(dāng)然采用oc運(yùn)行時(shí)也是可以的。最終oc端接口的代碼放在webView:didCreateJavaScriptContext:forFrame:中,這樣js文件只需加載完畢就可執(zhí)行oc的接口方法;而oc端要訪問(wèn)js的接口則可在webVIewDidFinishLoad中執(zhí)行,完美解決接口訪問(wèn)時(shí)機(jī)的問(wèn)題。
????在js端,由于只有暴露在全局的函數(shù)聲明才能夠讓oc端訪問(wèn),這就限制了js端的靈活性。我嘗試過(guò)在js端通過(guò)“賦值”完成接口的暴露(window.say = function(){alert("hello world!")};),在oc端無(wú)法訪問(wèn),只有通過(guò)普通的函數(shù)聲明才能解決問(wèn)題,這可能與JSContext的內(nèi)存指針引用相關(guān),為了解決此問(wèn)題,我通過(guò)創(chuàng)建一個(gè)全局函數(shù)來(lái)暴露js端的接口對(duì)象,通過(guò)獲取的對(duì)象來(lái)訪問(wèn)具體的接口方法。
if(isiOS4JSC){ // 將注冊(cè)的方法透出到window.jscObj的屬性上 var ev = eval; $.JSBridge._JSMethod = method; // 暴露函數(shù)至全局 // jsc只能執(zhí)行全局函數(shù)聲明方式定義的函數(shù),不可以將函數(shù)指針復(fù)制給其他變量執(zhí)行 ev("function toObjectCExec() {" + "window.jscObj = window.jscObj ? window.jscObj : {};"+ "window.jscObj["" + methodName + ""] = function (message) {" + " var ret = $.JSBridge._JSMethod(message);" + " return JSON.stringify(ret);" + "};" + "return jscObj;" + "}"); }
如此,js端的接口暴露就很容易了。
尾聲????我現(xiàn)在仍然相信,目前的iOS hybridAPP的主流通信方式仍然適corava的javascriptWebViewBridge,但是隨著jsc引入到iOS7中,本文介紹的使用jsc(嵌入js引擎的方式)來(lái)完成oc和js的通信將更為流行,盡管目前apple提供的針對(duì)jsc的開(kāi)發(fā)接口文檔幾乎沒(méi)有,但是我們通過(guò)webkit的源碼做一些hack的方式也不是不可以,畢竟只要UIWebView仍然使用webkit進(jìn)行渲染,這種方式會(huì)一直有效,除非apple在代碼層面針對(duì)hack做過(guò)濾,不過(guò)這種可能性真的很小。我們有理由憧憬未來(lái)在iOS和android下更方便的集成js引擎來(lái)完成建議的雙向通信。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/78454.html
摘要:在上有這樣一個(gè)項(xiàng)目可以拿到了上下文創(chuàng)建的事件,只不過(guò)也是改獲取方法也是蘋果的私有,原來(lái)項(xiàng)目中使用了這個(gè)庫(kù)上架蘋果應(yīng)用商店沒(méi)有問(wèn)題,現(xiàn)在審核情況不太了解。 前言 動(dòng)態(tài)化是移動(dòng)開(kāi)發(fā)技術(shù)中的重要的一部分 ,當(dāng)前普遍的動(dòng)態(tài)化方案 , 如 React Native 、Weex 、Hybrid部分解決方案及之前流行的熱修復(fù)框架 JSPatch ,背后都用到了 JavaScriptCore 框架 ,...
摘要:接下來(lái),我將從原理優(yōu)缺點(diǎn)等方面為大家分享跨平臺(tái)技術(shù)演進(jìn)。小程序年是微信小程序飛速發(fā)展的一年,年,各大廠商快速跟進(jìn),已經(jīng)有了很大的影響力。下面,我們以微信小程序?yàn)槔?,分析小程序的技術(shù)架構(gòu)。 前言 大家好,我是simbawu ,@BooheeFE Team Leader,關(guān)于這篇文章,有問(wèn)題歡迎來(lái)這里討論。 隨著移動(dòng)互聯(lián)網(wǎng)的普及和快速發(fā)展,手機(jī)成了互聯(lián)網(wǎng)行業(yè)最大的流量分發(fā)入口。以及隨著5G...
摘要:接下來(lái),我將從原理優(yōu)缺點(diǎn)等方面為大家分享跨平臺(tái)技術(shù)演進(jìn)。小程序年是微信小程序飛速發(fā)展的一年,年,各大廠商快速跟進(jìn),已經(jīng)有了很大的影響力。下面,我們以微信小程序?yàn)槔?,分析小程序的技術(shù)架構(gòu)。 前言 大家好,我是simbawu ,@BooheeFE Team Leader,關(guān)于這篇文章,有問(wèn)題歡迎來(lái)這里討論。 隨著移動(dòng)互聯(lián)網(wǎng)的普及和快速發(fā)展,手機(jī)成了互聯(lián)網(wǎng)行業(yè)最大的流量分發(fā)入口。以及隨著5G...
閱讀 2151·2021-11-11 16:54
閱讀 1117·2021-10-12 10:12
閱讀 444·2019-08-30 15:43
閱讀 721·2019-08-29 13:15
閱讀 1146·2019-08-29 13:12
閱讀 1597·2019-08-26 12:09
閱讀 1726·2019-08-26 10:24
閱讀 2347·2019-08-26 10:15