前世今生
所谓混合开发,其实指代就是他的产物Hybrid App
Hybrid App(混合模式移动应用)是指介于web-app、native-app这两者之间的app,兼具“Native App良好用户交互体验的优势”和“Web App跨平台开发的优势”。
上面的定义是百度百科的官话,用白话文翻译就:所谓hybridApp就是在app中嵌入web页面
兴起原因
之所以Hybrid App会兴起并且红极一时, 其实段移动互联网产业的一种偶然。移动互联网的热潮刮起后,众多公司前赴后继的进入。但是很快发现移动应用的开发人员太少,所以导致疯狂的人才争夺。市场机制下移动应用开发人才的待遇扶摇直上,最终变成众多企业无法负担养一个具备跨平台开发能力的专业移动应用开发团队。而HTML5的出现让Web App露出曙光,HTML5开发移动应用的跨平台和廉价优势让众多想进入移动互联网领域的公司开始心动。可是当下基于HTML5的Web App更是雾里看花,在用户入口习惯、分发渠道和应用体验这三个核心问题没解决之前,Web App也很难得以爆发。正是在这样是机缘巧合下,基于HTML5低成本跨平台开发优势又兼具Native App特质的Hybrid App技术杀入混战,并且很快吸引了众人的目光。大幅的降低了移动应用的开发成本,可以通过现有应用商店模式发行,在用户桌面形成独立入口等等这些,让Hybrid App成为解决移动应用开发困境不错的选择,也成为现阶段Web App的代言人。Hybrid App像刺客一样,在Native App和Web App混战之时,偶然间的在移动应用开发领域占有了一席之地。(引用百度)
优势和劣势
一张图说明问题:
发展历程
混合开发兴起之后,社区便开始不安分了。于是出现了一堆轮子,助力我们快速开发一个Hybrid App
Cordova
这是社区最早出现的轮子,我们统称为 Cordova。Cordova 主要提供三种能力:
- 前端代码与原生代码通信的能力;
- 原生插件机制;
- 跨平台打包能力。
cordova是一个移动应用开发框架,你基于这个东西可以用网页代码作出APP。
Phonegap Build
Phonegap Build是一个在线打包工具,你把使用cordova写好的项目给Phonegap Build,Phonegap Build就会在线打包成App。
Phonegap
电脑软件公司Adobe 2011年10月4日宣布收购了创建了HTML5 移动应用框架PhoneGap 和PhoneGap Build的新创公司Nitobi Software。 全新的Phonegap诞生,他继承了Phonegap Build和cordova ,由此phonegap在混合开发领域变得异常响亮,导致,我们不需要线上打包能力,只使用cordova时,也被叫做使用Phonegap,也就是我们今天的通俗叫法。
该方案只为前端提供了一个 WebView,除此之外完全不干预展现层。当然缺点也非常明显,由于前端还处在发展的初期,很多动画体验和原生相差甚远。并且由于只有一个webview导致根本没有转场动画,app难用程度可想而知。
衍生应用开发平台
针对 Cordova 存在的问题,一些厂商给出了一种优化方案,并且给出友好的文档,但是本质上还是在 Cordova 的基础上做了以下几点改进:
- 以云平台的方式管理项目,整个开发周期除了写代码以外都能在平台上实现;
- 丰富的内置原生能力,做到开箱可用;
- 打造本土化的插件生态;
- 多 WebView 机制,用原生转场解决流畅性问题,这是杀手级特性。
如此一来这个多webview机制,直接解决了专场动画问题,体验可谓上升一个台阶,比较知名的比如: Ionic,不过由于web页面天生的局限,混合应用在 UI 层面很难达到原生界面的细腻程度;界面的载入速度也很容易受到手机运行速度和页面大小的影响。如果前端开发做的不够细致,就很容易给用户带来“网页感”,使 App 的用户体验大打折扣。
JSBridge(webview UI)方案(这期重点学习的)
移动互联网疯狂发展中,it培训机构的崛起,ios,安卓工程师迅速被培养出来,市场一片欣欣向荣,大家可以脑补13年时候的ios安卓有多火,于是,市场上不在缺少原生开发,企业都开始注重app的用户体验,导致之前的混合开发平台的劣势暴露无遗,于是,JSBridge方案被逐渐采用
JSBridge:听其取名就是js和Native之前的桥梁,而实际上JSBridge确实是JS和Native之前的一种通信方式。简单的说,JSBridge就是定义Native和JS的通信,Native只通过一个固定的桥对象调用JS,JS也只通过固定的桥对象调用Native。
其实JSBridge说白了就是去除了各大混合开发平台封装的一条龙的服务,而只保留了web和Native的通信部分,去在app的webview中嵌入web,来解决原生端解决不了的问题,比如:原生无法解析富文本,一个app有个活动,需要上线,如果使用原生去做,还要还需要发版,而恰巧web的热更新能力,恰巧能解决原生app的痛点,从此JSBridge成为了软件开发不可缺少的一部分,虽然体验痛点问题被解决,但是,还是解决不了一个app需要多端协作,多端通信从而导致开发难度增加的问题,于是React Native横空出世
React Native
React Native (简称RN)是Facebook于2015年4月开源的跨平台移动应用开发框架,是Facebook早先开源的JS框架 React 在原生移动应用平台的衍生产物,支持iOS和安卓两大平台。
RN和普通混合开发的区别就是React Native 采用不同的方法进行混合移动应用开发。它不会生成原生 UI 组件,而是基于 React,React Native 是一个用于构建基于 Web 的交互界面的 JavaScript 库,因此会有更丰富的 UI 体验效果,同时也能够很好地调用底层框架的UI使用,达到和原生一样的体验
Weex
Weex 2016年4月21日,阿里巴巴在Qcon大会上宣布没测跨平台移动开发工具Weex,其实他跟RN大同小异,但是对比RN有那么一些优点:
- js 能写业务,跨平台,热更新
- Weex 能用 Vue 的 framework,贴近我们的技术栈
- Weex 比 RN 更轻量,可以分包,每个页面一个实例性能更好
- Weex 解决了 RN 已经存在的一些问题,在 RN 的基础上进行开发
- 有良好的扩展性,比较好扩展新的 组件和 模块
Flutter
RN一经推出异常火爆,于是,同为互联网大佬的谷歌当然也不甘落后,于是他的第一个版本在2018年12月5日发布,一经推出,迅速大火,基本现在风头已经盖过React Native 究其原因就是:
RN不仅桥接系统服务,也将系统UI也桥接到了JaveScript中,这样写出来的UI最终也会渲染成原生的控件。
如上图这样,UI的渲染是很频繁的,要使UI不卡顿,必须达到60Fps。但是桥接会花一定的时间。所以这样的架构有时候会有性能问题。 Flutter使用Dart语言开发,Dart可以被编译(AOT)成不同平台的本地代码,让Flutter可以直接和平台通讯而不需要一个中间的桥接过程,从而提高了性能。
主流混合开发的解决方案
JSBridge(webview UI)方案(本期要剖析,市面app主流的方案)
JSBridge的方案是目前主流的解决方案,比如一些大厂app也都采用的是现在方案,比如:淘宝,头条,微信,饿了么
Native UI方案(React Native,weex,fullter)
这种解决方案,其实就是通过jsbridge把js或者dart解析成原生节点树,使用原生进行渲染的一个解决方案,从而具有了原生的体验
小程序的解决方案
小程序的解决方案是微信首先提出,其他大厂跟风提出的一个解决方案,其实本质上也是利用JSBridge这个桥梁来进行实现
JSBridge深度剖析
我们之前提到Hybrid App的本质就是使用webview作为容器,来承载一个web页面,那么什么是webview呢?
webview
Webview 是一个基于webkit引擎,可以解析DOM 元素,展示html页面的控件,它和浏览器展示页面的原理是相同的,所以可以把它当做浏览器看待。(chrome浏览器,Safari 也是基于webkit引擎开发的)
简而言之,webview就相当于一个浏览器,能解析html css以及js,甚至安卓后期更凶残,直接在4.4版本后直接使用了Chrome
基本原理
讲历史发展的时候已经说过了,再次重复一遍
JSBridge:听其取名就是js和Native之前的桥梁,而实际上JSBridge确实是JS和Native之前的一种通信方式。简单的说,JSBridge就是定义Native和JS的通信,Native只通过一个固定的桥对象调用JS,JS也只通过固定的桥对象调用Native。
用图形象的表示他是这样的:
如何使用?
由于安卓和ios代码不太熟悉,借花献佛,照搬过来了,感谢巨人的肩膀
Android端
Native调JS
代码语言:javascript复制4.4之前的调用方式
// mWebView = new WebView(this);
mWebView.loadUrl("javascript: 方法名('参数,需要转为字符串')");
//ui线程中运行
runOnUiThread(new Runnable() {
@Override
public void run() {
mWebView.loadUrl("javascript: 方法名('参数,需要转为字符串')");
Toast.makeText(Activity名.this, "调用方法...", Toast.LENGTH_SHORT).show();
}
});
4.4之后的调用方式
mWebView.evaluateJavascript("javascript: 方法名('参数,需要转为字符串')", new ValueCallback() {
@Override
public void onReceiveValue(String value) {
//这里的value即为对应JS方法的返回值
}
});
复制代码
JS调Native
代码语言:javascript复制//Js调用Native需要对WebView设置@JavascriptInterface注解,这里有个漏洞,后面会给大家说明。要想js能够Native,需要对WebView设置以下属性。
WebSettings webSettings = mWebView.getSettings();
//Android容器允许JS脚本
webSettings.setJavaScriptEnabled(true);
//Android容器设置侨连对象(我的理解相当于在window下挂个命名空间,名字随便起,不对之处请大佬指出)
mWebView.addJavascriptInterface(getJSBridge(), "JSBridge");
//添加命名空间的方法
private Object getJSBridge(){
Object insertObj = new Object(){
@JavascriptInterface
public String foo(){
return "foo";
}
@JavascriptInterface
public String foo2(final String param){
return "foo2:" param;
}
};
return insertObj;
}
//html中使用
//调用方法一
window.JSBridge.foo(); //返回:'foo'
//调用方法二
window.JSBridge.foo2('test');//返回:'foo2:test'
复制代码
iOS端
Native调JS
代码语言:javascript复制//Swift
webview.stringByEvaluatingJavaScriptFromString("方法名(参数)")
//OC
[webView stringByEvaluatingJavaScriptFromString:@"方法名(参数);"];
JS调Native
代码语言:javascript复制//在ios中引入官方的库文件
#import <JavaScriptCore/JavaScriptCore.h>
//Native注册api函数(OC)
-(void)webViewDidFinishLoad:(UIWebView *)webView{
[self hideProgress];
[self setJSInterface];
}
-(void)setJSInterface{
JSContext *context =[_wv valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
// 注册名为foo的api方法
context[@"foo"] = ^() {
//获取参数
NSArray *args = [JSContext currentArguments];
NSString *title = [NSString stringWithFormat:@"%@",[args objectAtIndex:0]];
//做一些自己的逻辑
//返回一个值 'foo:' title
return [NSString stringWithFormat:@"foo:%@", title];
};
}
//在js中直接调用
window.top.foo('test');
当然如果你单纯的js调用Native还有一种拦截URL SCHEME方案
代码语言:javascript复制url scheme是一种类似于url的链接,是为了方便app直接互相调用设计的。具体来讲如果是系统的url scheme,则打开系统应用,否则找看是否有app注册这种scheme,打开对应app,主要区别是 protocol 和 host 一般是自定义的。
例如:
tiantianxiangshang://hoahaoxuexi/url?url=abcdefg.qwer,
protocol 是 tiantianxiangshang,host 则是 hoahaoxuexi。
Web 端通过某种方式发送scheme请求,Native用某种方法捕获对应的url触发事件,然后拿到当前的触发url,根据定义好的协议,分析当前触发了那种方法。
社区轮子
上面的通信过程,复杂而又繁琐,并且两端还不统一,于是,我们的社区轮子层出不穷,比如
解决ios的通信轮子WebViewJavascriptBridge 解决安卓的通信轮子JsBridge 还有一个三端易用的轮子DSBridge其实就是集成了前两个的优点,写成一套了
研究了一下其代码发现,他们其实是在基础的两端通信的基础上加入了一些封装思想,比如:加入了回调啊,支持异步啊,等等的思想,是的在原生的基础上变得更灵活和好用了!
附上流程图结束这些轮子的思想:
总结
在混合开发的方案中,目前为止还没有发现一个完美的解决方案(当然也不能完美,不然原生工程师就得下岗了),每种方案都有着自己的缺点和弊端,而在单位的技术选型中我一般遵循以下几点仅供大家参考:
- 1、如果是已有原生项目,并且项目流量巨大,需要小幅重构,采用JsBridge比较问题,毕竟成本低,方案成熟,毕竟小心使得万年船
- 2、如果是新项目并且是小型项目,且公司有成本支持的情况下(一般得是大公司),可以采用激进方案Flutter或者rn进行尝试
- 3、如果新项目但是一开始就是冲着宏大的构想去的,我一般也时采用稳妥的方式,原生加上JsBridge
基本混合开发的知识点我觉得需要学习的也就这么多了,在需要深入研究涉及到源码层面的东西,还请移步github和各家官方文档,最后提醒,此文属于个人学习笔记共享,如有错误之处,请大佬指出!
最后
始终不能忘了巨人的肩膀: Hybrid App 开发快速指南 Flutter是什么? JSBridge深度剖析