
Hybrid混合开发模式越来越趋向主流了,于是就避免不了原生Native和JS的交互,也即是通过JavaScriptCore实现JS和Native的通信,总体来说,苹果的JavaScriptCore的API还是很简单易用的,主要的操作步骤如下:
基本使用
- 获取JSContext
1 | self.jsContext = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; |
- 处理某个js调用Native的方法
1 | _jsContext[@"log"] = ^() { |
- Native调用JS方法
1 | [self.jsContext evaluateScript:@"log('arg1')"]; |
- 重定义某个JS方法
1 | [self.jsContext evaluateScript:@"checkAPI = function(){\ |
上述几个步骤,其实已经能满足我们开发的基本使用了。可是后来发现由于平台差异性问题,在android端JS调用原生Native的时候,需要指定interfaceName
接口名称,所以android和js的通信方式为:
1 | // js调用android方法的方式 |
可是这种方式在iOS平台下,是无法调用iOS原生方法的。
1 | // 这个不能被执行到 |
那么有什么好的解决办法吗?那是肯定的,要不然不白说废话了吗?
高级用法
方案一:
使用JSExport协议,来达到android的处理效果
- 定义一个遵守
协议的协议类LJNWebViewJSHelperProtocol,并在协议中定义JS要调用原生的方法
1 |
|
- 创建一个继承自NSObject的类LJNWebViewJSHelper并遵守LJNWebViewJSHelperProtocol,实现协议方法
1 | #import <Foundation/Foundation.h> |
1 | #import "LJNWebViewJSHelper.h" |
- 注意该类的实例到JSContext中
1 | - (void)webViewDidFinishLoad:(UIWebView *)webView |
至此JS就可以通过xxxname.callNativeMethod的形式调用Native方法了。
注意:
不要编写self.jsContext[@"xxxname"] = self;
这样的代码,因为会导致self实例被JS端持有从而导致内存无法释放。
方案二:
对比方案一,无需实现协议方法
- 创建一个LJNWebViewJSExport类,遵守JSExport协议
1 | #import <Foundation/Foundation.h> |
- 注册给JSContext
1 | self.jsContext[@"xxxname"] = [[LJNWebViewJSExport alloc] init]; |
- 处理JS调用Native的方法
1 | self.jsContext[@"callNativeMethod"] = ^(NSString *arg) { |
至此两种解决方案都已讲述完毕。个人觉得解决方案二有点更多一些,更贴近JS习惯,其次不需要定义那么多的方法,其次对于JS端调用来说,不定参数个数也更容易一些。
总结
- 使用
NSArray *args = [JSContext currentArguments];
得到的args,内部都是JSValue,千万注意这一点,如果想要使用,最好转成相应的类型,比如[obj toString]之类的,具体可以看JSValue的头文件 - 要注意循环引用的问题,不要写出
self.jsContext[@”xxname”] = self;
这样的代码来,其他的就是block中的循环引用问题。 - 注意JS和Native的生命周期,一般情况下不会出现什么问题,但是必要时需要使用JSVirtualMachine包装一下。
- JS的内存管理方式是GC
- iOS的内存管理方式是引用计数