javascript
js与c语言互相调用,Objc与JS间相互调用
過去3、4年都在進(jìn)行跨平臺的混合應(yīng)用開發(fā),但一直沒有系統(tǒng)梳理跨平臺技術(shù)的底層原理,趁新工作未正式入職,這里整理一下。
跨平臺的一種實(shí)現(xiàn)是基于webview。所謂webview,實(shí)質(zhì)是在原生app中打開一個內(nèi)嵌瀏覽器,具體到iOS平臺就是使用UIWebView這個控件。然后就很容易理解了,我們相當(dāng)于開發(fā)一個webapp(網(wǎng)頁應(yīng)用),然后通過原生應(yīng)用作為用戶入口(而非原生瀏覽器),用戶會訪問到遠(yuǎn)程服務(wù)器的網(wǎng)頁內(nèi)容。從用戶感知上,似乎是在使用一個App,但實(shí)際上是在訪問一個網(wǎng)頁。
以上,只是跨平臺基于webview實(shí)現(xiàn)的工作原理,而更重要的是如何橋接webview的js和app的objc,使得webapp也可用使用原生的功能api,如調(diào)用攝像頭等,而app又可以調(diào)用webview里的js,即向雙通信。
Apple開放了一個叫做JavascriptCore的框架,此框架最早在OSX10.2就存在,但到了2013年在OSX10.9上才發(fā)布其調(diào)用的API,而后又在iOS7上公開,由此我們可用名正言順地使用了。
JavascriptCore提供了以下幾個API,實(shí)現(xiàn)跨平臺通信:
JavascriptCore/API/JSContext.h
JavascriptCore/API/JSExport.h
JavascriptCore/API/JSValue.h
JSContext是JavascriptCore的主入口,它代表了JS的運(yùn)行時環(huán)境,在其中可以定義對象、方法等,這些實(shí)體(對象、方法)的生命周期在JSContext被釋放的時候才結(jié)束。而且可用指定的JSVirtualMachine來創(chuàng)建JSContext,每個JSVM都會獨(dú)立運(yùn)行在一個線程上。
我們可用通過JSContext的evaluateScript方法來定義我們的JS方法,而且是通過字符串定義代碼,當(dāng)然可以通過讀取外邊js文件來實(shí)現(xiàn)。看下面的例子:
// getting a JSContext
JSContext *context = [JSContext new];
// defining a JavaScript function
NSString *jsFunctionText =
@"var isValidNumber = function(phone) {"
" var phonePattern = /^[0-9]{3}[ ][0-9]{3}[-][0-9]{4}$/;"
" return phone.match(phonePattern) ? true : false;"
"}";
[context evaluateScript:jsFunctionText];
這里,相當(dāng)于在objc層,向JSContext注入了一個isValidNumber的js方法。
正如上文所述,JSContext代表了一個JS運(yùn)行環(huán)境,而我們的示例代碼都是單獨(dú)創(chuàng)建這個JSContext運(yùn)行環(huán)境的,實(shí)際上UIWebView實(shí)例也有它自己的JSContext運(yùn)行環(huán)境。為了修改web上的內(nèi)容,我們需要訪問UIWebView的JSContext。
但Apple就是一個悶騷男,雖然已經(jīng)公開了JavascriptCore的API,但又不提供直接訪問UIWebView’s JSContext的方法。
幸好“key-value”把我們救了回來:
// get JSContext from UIWebView instance
JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
然后,我們可以通過JSValue來獲取JSContext中js方法的引用和執(zhí)行的結(jié)果:
// 獲取isValidNumber方法的引用
JSValue *jsFunction = context[@"isValidNumber"];
// 通過callWithArguments方法調(diào)用js方法
JSValue *value = [jsFunction callWithArguments:@[ phone ]];
JavascriptCore會自動轉(zhuǎn)換JSValue的對象類型,比如這里isValidNumber返回的boolean,同時還支持NSString, NSDate, NSDictionary, NSArray等。
另外,我們還可以增加異常捕捉
[context setExceptionHandler:^(JSContext *context, JSValue *value) {
NSLog(@"%@", value);
}];
再有,通過JSExport可以將objc的方法暴露給JS。
@protocol BNRContactAppJS
- (void)addContact:(BNRContact *)contact;
@end
@interface BNRContactApp : NSObject
...
@end
addContact這個方法是在BNRContactAppJS協(xié)議中聲明的,BNRContactAppJS又源自于JSExport,所以addContact方法將會暴露給JS環(huán)境,而其他方法則對JS環(huán)境而言是隱藏的。
最后,我們看一個完整的例子
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
// get JSContext from UIWebView instance
JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
// enable error logging
[context setExceptionHandler:^(JSContext *context, JSValue *value) {
NSLog(@"WEB JS: %@", value);
}];
// give JS a handle to our BNRContactApp instance
context[@"myApp"] = self.app;
// register BNRContact class
context[@"BNRContact"] = [BNRContact class];
// add function for processing form submission
NSString *addContactText =
@"var contactForm = document.forms[0];"
"var addContact = function() {"
" var name = contactForm.name.value;"
" var phone = contactForm.phone.value;"
" var address = contactForm.address.value;"
" var contact = BNRContact.contactWithNamePhoneAddress(name, phone, address);"
" myApp.addContact(contact);"
"};"
"contactForm.addEventListener('submit', addContact);";
[context evaluateScript:addContactText];
}
最終跨平臺調(diào)用就在這一句:
myApp.addContact(contact);
在JS環(huán)境調(diào)用了objc的方法。
總結(jié)一下:
==從objc調(diào)用js:JSContext的evaluateScriptf方法和JSValue的callWithArguments方法;==
==捕捉JS執(zhí)行的異常;==
==從WebView實(shí)例獲取JSContext;==
==通過JSExport將objc方法暴露給js調(diào)用。==
最后啰嗦一下,iOS7以前,并沒有JavascriptCore,所以多使用 stringByEvaluatingJavaScriptFromString。
Titanium 就是使用了JavascriptCore的方式。
總結(jié)
以上是生活随笔為你收集整理的js与c语言互相调用,Objc与JS间相互调用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 哈希表数据结构_Java数据结构哈希表如
- 下一篇: catia怎么进入装配_CATIA教程之