js与c语言互相调用,Objc与JS间相互调用

过去3、4年都在进行跨平台的混合应用开发,但一直没有系统梳理跨平台技术的底层原理,趁新工作未正式入职,这里整理一下。

跨平台的一种实现是基于webview。所谓webview,实质是在原生app中打开一个内嵌浏览器,具体到iOS平台就是使用UIWebView这个控件。然后就很容易理解了,我们相当于开发一个webapp(网页应用),然后通过原生应用作为用户入口(而非原生浏览器),用户会访问到远程服务器的网页内容。从用户感知上,似乎是在使用一个App,但实际上是在访问一个网页。

以上,只是跨平台基于webview实现的工作原理,而更重要的是如何桥接webview的js和app的objc,使得webapp也可用使用原生的功能api,如调用摄像头等,而app又可以调用webview里的js,即向双通信。

Apple开放了一个叫做JavascriptCore的框架,此框架最早在OSX10.2就存在,但到了2013年在OSX10.9上才发布其调用的API,而后又在iOS7上公开,由此我们可用名正言顺地使用了。

JavascriptCore提供了以下几个API,实现跨平台通信:

JavascriptCore/API/JSContext.h

JavascriptCore/API/JSExport.h

JavascriptCore/API/JSValue.h

JSContext是JavascriptCore的主入口,它代表了JS的运行时环境,在其中可以定义对象、方法等,这些实体(对象、方法)的生命周期在JSContext被释放的时候才结束。而且可用指定的JSVirtualMachine来创建JSContext,每个JSVM都会独立运行在一个线程上。

我们可用通过JSContext的evaluateScript方法来定义我们的JS方法,而且是通过字符串定义代码,当然可以通过读取外边js文件来实现。看下面的例子:

// 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];

这里,相当于在objc层,向JSContext注入了一个isValidNumber的js方法。

正如上文所述,JSContext代表了一个JS运行环境,而我们的示例代码都是单独创建这个JSContext运行环境的,实际上UIWebView实例也有它自己的JSContext运行环境。为了修改web上的内容,我们需要访问UIWebView的JSContext。

但Apple就是一个闷骚男,虽然已经公开了JavascriptCore的API,但又不提供直接访问UIWebView’s JSContext的方法。

幸好“key-value”把我们救了回来:

// get JSContext from UIWebView instance

JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

然后,我们可以通过JSValue来获取JSContext中js方法的引用和执行的结果:

// 获取isValidNumber方法的引用

JSValue *jsFunction = context[@"isValidNumber"];

// 通过callWithArguments方法调用js方法

JSValue *value = [jsFunction callWithArguments:@[ phone ]];

JavascriptCore会自动转换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协议中声明的,BNRContactAppJS又源自于JSExport,所以addContact方法将会暴露给JS环境,而其他方法则对JS环境而言是隐藏的。

最后,我们看一个完整的例子

- (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];

}

最终跨平台调用就在这一句:

myApp.addContact(contact);

在JS环境调用了objc的方法。

总结一下:

==从objc调用js:JSContext的evaluateScriptf方法和JSValue的callWithArguments方法;==

==捕捉JS执行的异常;==

==从WebView实例获取JSContext;==

==通过JSExport将objc方法暴露给js调用。==

最后啰嗦一下,iOS7以前,并没有JavascriptCore,所以多使用 stringByEvaluatingJavaScriptFromString。

Titanium 就是使用了JavascriptCore的方式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
是的,为了实现在Web页面中与iOS和Android原生应用程序之的交互,可以使用JSBridge。JSBridge是一个桥接工具,可以将JavaScript与原生应用程序之的通信连接起来。 在iOS中,可以使用JavaScriptCore框架和WKWebView来实现JSBridge。在Android中,可以使用WebView和JavaScriptInterface来实现JSBridge。 具体来说,需要在原生应用程序中创建一个JavaScriptInterface类,该类可以实现JavaScript与原生代码之的通信。然后在Web页面中,可以通过JavaScript调用原生应用程序中的方法。 例如,在iOS中,可以使用以下代码创建一个JavaScriptInterface类: ``` @objc class JSInterface: NSObject { weak var webView: WKWebView? init(webView: WKWebView) { self.webView = webView super.init() } @objc func showToast(_ message: String) { let alertController = UIAlertController(title: "Toast", message: message, preferredStyle: .alert) let okAction = UIAlertAction(title: "OK", style: .default, handler: nil) alertController.addAction(okAction) self.webView?.viewController?.present(alertController, animated: true, completion: nil) } } ``` 然后在Web页面中,可以通过以下代码调用原生应用程序中的showToast方法: ``` window.webkit.messageHandlers.jsInterface.postMessage({action: 'showToast', message: 'Hello world!'}); ``` 在Android中,可以使用以下代码创建一个JavaScriptInterface类: ``` public class JSInterface { private Context context; public JSInterface(Context context) { this.context = context; } @JavascriptInterface public void showToast(String message) { Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); } } ``` 然后在Web页面中,可以通过以下代码调用原生应用程序中的showToast方法: ``` window.jsInterface.showToast('Hello world!'); ``` 通过这种方式,可以实现JavaScript与iOS和Android原生应用程序之的交互。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值