开发移动App主要有三种模式:Native、 Hybrid和Web App。各种开发模式的优缺点这里就不讨论了,有很多科技博客都做过专题报道。需要注意的一点是在选择开发模式的时候,要根据你的项目类型(图片类?视频类?新闻类?等),产品业务和人员技术储备等做权衡。
本文主要介绍Hybrid App的开发模式,Hybrid开发模式就是既有Native开发也有Web app的开发。那我们怎么去确定App中某个功能模块使用Native还是Web开发?它们之间如果需要接口通信又该如何去实现呢?又该如何更好的去维护Hybrid App产品呢?
当我们选择用Hybrid模式开发App时,应先熟悉项目整个框架和App各个模块。然后将通用的,对性能要求不是那么高的App界面可抽出来作为web界面开发,Native直接调用。一般Hybrid App开发,App界面之间的跳转关系由Native实现并完成,web界面主要作为内容填充到Android和iOS的浏览器控件中。拉手网之前Android客户端就是采用这种模式,Native搭一个App框架壳,里面展示内容和网络请求全部由web实现。由于客户端浏览器控件版本不同,web界面加载,渲染和缓存机制等原因,这种模式开发的App整体在图文比较多的情况下体验就不太好,甚至可能会出现意想不到的奇葩问题。 建议图文列表,涉及到视频等多并发和界面元素比较负责或具有很强的动画特效的情况优先考虑Native开发。 这方面在Android尤其有必要,Android 4.4之前的浏览器控件WebView基于缺省的WebKit内核,它不同于Chromium所使用的Webkit内核;而在4.4之后(包括4.4)的WebView的实现被替换成Chromium WebKit内核。当我们想让App尽可能的在低版本也能运行良好时,就需要不断做兼容性开发和测试了,除非自己在App里面打包编译最新WebView源码,类似开发一个浏览器。
Android和iOS都提供了API直接调用:
webview.loadUrl("javascript:functionName(/"" + argument + "/")");
别忘了设置 webview.getSettings().setJavaScriptEnabled(true);
[webView stringByEvaluatingJavaScriptFromString:@"alert('hello world!')"];
Android实现js调用native方法一般是先编写供js调用的类,然后通过添加javascripteInterface的方法,如
webView.addJavascriptInterface(pandoInterface, "pando");
将java对象pandoInterface生成js对象pando,然后直接window.pando就可以调用pandoInterface对象里面的方法。需要注意的是供js调用的pandoInterface对象里面的java方法需手动加上@JavascriptInterface注解,这个是Google为解决安全问题引入的。
iOS实现js调用Native方法相对繁琐一点。主要是iOS原生并没有提供js直接调用native的方式,只能通过UIWebView相关的UIWebViewDelegate协议的方法来做拦截,并在这个方法中,根据url的协议或特征字符串来做调用方法或触发事件等工作。当js需要调用Native方法时,js创建一个隐藏的iframe设置或改变其src就会触发Native拦截url事件。
如
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { NSURL *url = [request URL]; if ([[url scheme] isEqualToString:@"someFunction") { //调用原生方法 return NO; } else { return YES; } }
不知道大家发现没有,以上两种方法都只是实现了js调用Native方法,但是都没有实现js函数回调,将Native方法返回结果传递给js。
微信开放了很多jsApi接口,供大家直接调用微信Native的功能。通过jsApi接口和文档我们会发现里面实现了Native方法执行结果回调给js。这种Hybrid App开发模式很好的利用了Native高性能,多并发的优势,将网络请求,扫码等复杂的逻辑或者web不可能实现的功能由Native完成,而web只是做了界面显示效果。将微信Android安装包解压之后,在assets/jsapi目录下有个80多kb的wxjs.js文件,里面实现了微信jsApi逻辑jsbridge。下面主要简单介绍一下这种jsbridge实现原理。
jsbridge核心难点在于如何在Native方法执行完之后将返回接口给js,并且让js能理解传过来的参数所表达的意思。针对这,我们可以在js调用Native方法之间,将js一次调用Native的唯一标示符identifier和期望Native调用完后执行的回调函数存储在Map里面,将identifier和其他参数传递给Native,native执行完成后,将identifier和执行结果作为参数调用js函数,在js函数里面解析参数,得到identifier,然后在Map里查询对应的回调函数,将native执行结果作为参数传递进去调用。有点绕,但是巧妙的利用消息传递机制,实现js调用Native进行回调函数调用,同理,Native调用js函数将返回结果给Native调用也是可以的。
Android在实现jsbridge时,早期方法也是需要iOS那样,先给web界面创建iframe,然后通过js改变iframe src,Native方法通过shouldOverrideUrlLoading(WebView view, String url)拦截url处理。但是在Android 4.2.2系统上,这个方法在iframe src改变是,是不会触发的。目前建议大家通过拦截js alert弹框做处理。
@Override public boolean onJsAlert(WebView view, String url, String message, JsResult result) { LogUtils.i("url:" + url+"/n message:" + message); try { String msg = URLDecoder.decode(message, "utf-8"); if (msg.startsWith(BridgeUtil.PANDO_RETURN_DATA)) { // 如果是返回数据 handlerReturnData(message); result.cancel(); return true; } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return super.onJsAlert(view, url, message, result); }
注意,onJsAlert方法拦截做处理后一定要result.cancel();不然web界面就会出现点击没反应的情况。
Hybrid App涉及到html、css和js等web资源文件,当web和Native之间有相互调用时,两者之间任何一方接口变动都会导致App出现bug。
Hybrid App开发一路走来踩过不少坑,欢迎大家多批评指点,多交流。@~~@