Lmsgsendnilself

Uninhibited Soul, Free Craziness

UIWebview与JS混编优化

| Comments

  在我们开发过程中,UIWebview与JS交互是经常要遇到的事情。这种方式可以达到代码在Android和iOS两端复用,降低开发成本,其劣势当然是以牺牲一定的原生展示效果为代价。例如对于之前开发的编辑器,其富文本定制就是通过对UIWebview注入大量的JS个性化定制实现的。网上关于Webview和JS交互的基本普及贴详细的很多,在此也不做赘述,简单描述下。   实现原理:
iOS调用JS端代码:通过UIWebView的实例方法:

1
-(nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;

  该方法是线程同步的。script 的样式可以是:
window.__Editor.methodName(@"'arg'")

  需要注意的是arg如果是字典或者数组等容器,需要用base64处理一下转成字符串。

  JS调用iOS端代码:基于移动端和JS约定好的通信协议,JS端通过隐藏的iframe发送url,而iOS端则通过UIWebview的代理方法,

1
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;

  对JS发出的url进行拦截解析,url的格式可以是
customProtocol://methodName+arg1...

  其中customProtocol://为URL的scheme,也即事先约定好的协议。通过这个我们能够判断出是否请求来自我们自己的JS定制。后面紧跟着方法和参数,我是用字典来作为参数的容器的,这样参数的数量用起来比较灵活,当然字典用base64编码一下。

  这个就是UIWebview和JS交互的基本流程,虽然这对于简单的交互已经足够,但是对于需要较高时,会出现一些问题,需要在此流程上面进行一下优化。

1. 当初始化加载JS代码时,这需要一些时间,而在此加载过程中,很有可能iframe已经发送了一些URL请求,那么,如果不做任何处理,将会使得所有url请求处理涉及到的数据为nil。 为了应对此问题,首先在js端的代码文件加载完成时触发一个url请求,在OC端则利用一个标志位isReady,根据url的内容使其标志是否加载完毕。在OC端利用一个可变缓存数组cacheJavaScriptCommands,将isReady为NO时所有的指令依次添加其中,等UIWebview初始化JS代码加载完成准备好后,再按照顺序依次执行缓存中的JS指令。

2. 有这么一种场景:如果JS发送的URL很长时,也就是后面的参数很长,长到一定程度,而多个iframe在一个非常短的时间内发送多个url,这样,前一个url未处理完时,有可能被后面的url冲掉,就会出现问题。 为了处理此问题,将JS对OC的通信分成了两步,第一步发送的url中并不直接将参数包含其中,而是为参数设置一个ID,将ID号替代参数作为url中的部分。第二步则是,通过拦截url并解析获得ID号,再通过UIWebview的实例方法

1
-(nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;

根据ID号获得真正的参数。因为此方法是线程同步的,所以解决了描述的问题。当然,为了实现这种方式,需要在JS代码里面维护一个ID号哈希表,并且添加一个JS方法,以便OC端根据ID获取详细参数

Comments