返回

混合开发的数据交互问题多多,一次性修复!

前端

Hybrid小程序混合开发之路 - 数据交互

记得xp时代的QQ,有些界面偶尔会弹出熟悉的js错误对话框,还能右键弹出熟悉的IE6的右键菜单,伪装的挺好,差点没认出来,现在的QQ就不知道了。使用了HTML作界面,那么和原生程序之间的数据交互基本上是少不了的~(仅仅当做浏览器性质来用的就算了)~。

如原生程序要控制H5界面:

  1. 通过window.location来进行跳转和控制页面,iOS下webView有个__open可以传入一个JS函数名,Android下用addJavascriptInterface来实现;
  2. 原生程序调用web容器内的JavaScript代码,iOS下webView有个stringByEvaluatingJavaScriptFromString,Android下用evaluateJavascript,代码还是document.title = "abc";
  3. web容器调用原生程序的方法,iOS下可以看看UIWebView的delegate,Android下用addJavascriptInterface。

反之,H5界面要控制原生程序:

  1. 使用location.href来进行页面跳转和控制页面;
  2. H5界面调用web容器的原生方法,iOS下可以看看UIWebView的delegate,Android下用addJavascriptInterface;
  3. web容器调用H5界面的JavaScript代码,iOS下webView有个stringByEvaluatingJavaScriptFromString,Android下用evaluateJavascript。

方法可以千变万化,具体要看实现。

若是web容器内的JavaScript代码控制原生程序,那就需要原生程序提供对应的原生方法供JavaScript调用,iOS下就是UIWebView的delegate,Android下是用addJavascriptInterface。原生程序需要提供给JavaScript一些方法,在Android下叫bridge,React Native也有这个东西,许多第三方库也有类似,比如微信小游戏使用的jsbridge。

原生程序提供的方法在具体实现上可能是原生的,也可能是基于Webkit的,因为实际的Webkit调用比较复杂,实现起来会十分繁琐,容易造成错误,所以许多框架都会进行封装。

比如在iOS下,JS调用OC的桥,本质上是对原生的UIWebView的delegate的封装,然而有些方法比如location.reload的使用是不可能用纯UIWebView的delegate实现的。这时就需要实现基于WebKit的纯C风格的封装(比如WebCore、JavaScriptCore,这些库最终是会调用到原生的API的),以实现location.reload这类方法。

使用原生Webkit的成本实在太高,对于许多原生程序来说,无论是体积、代码量还是阅读难度,都是不可接受的,这时可以选择基于WebKit之上实现的封装库,比如JSCore和JSContext等。

这些封装库在iOS下基本上都绑定的是JavaScriptCore,至少会用到JavaScriptCore里的函数来执行JavaScript代码,方法的参数和返回值类型基本都是Objective-C类型,比如NSString、NSArray等。

Android就不一样了,webview和JavaScriptCore的深度绑定还没有iOS那么强,加上安卓平台的JavaScript引擎是通过AddJavascriptInterface来实现的,引擎也不是JavaScriptCore,所以实现起来和iOS不太一样,更像是对WebView提供的接口进行了一层包装。

现在要开始混编了,我们总不能每次使用都需要原生的开发者介入吧?所以,也需要基于WebKit封装一些接口,让JavaScript调用。

既然要调用,还需要一些协议。协议是怎么定的呢?又需要原生开发者的介入吗?我们可以去寻找原生的封装库里可供JavaScript调用的接口。

比如我们有个button,想通过HTML和CSS来控制它的显示和隐藏,那么我们需要调用原生的接口,想一下,button元素有哪些属性,如果是DOM标准的话,我们看一眼button元素有哪些标准属性,有没有和display相关的,想一下display在DOM里的可能取值,看看能否调用原生的方法来修改button的display值来达到我们的目的。

找到一系列可能可用的接口后,就可以生成我们需要的协议了,把协议写好,就可以编写混编代码了。

这个时候我们会遇到另一个问题,调用原生接口时传入参数会存在类型转换的问题,毕竟JavaScript和C/C++/Objective-C的类型体系是不一样的。怎么办呢?只要你足够细心,那么这根本不是问题,因为在前面我们已经看过原生封装库对JavaScriptCore或者WebKit的绑定代码了,所以只要找到原生的代码和我们编写的协议的对应关系,就可以很容易的确定参数的类型了。

确定好参数类型后,只需要把这些方法暴露出去,就可以使用JavaScript调用原生的方法了。

虽然这不是真正意义上的混合开发(混合开发更多的应该是业务逻辑上的混合),但基本上所有可用的混编框架都是这么做的,现在热门的React Native等基本上也是遵循这个套路,要么是基于Chromium内核,要么是基于WebKit内核。

最后,说说常见的混合开发工具,React Native、Weex、Flutter、uniapp、Taro……本质上大同小异。