书接上篇:移动端研发技术的进化历程
纯原生开发主要面临动态化和开发成本两个问题,而针对这两个问题,诞生了一些跨平台的动态化框架。
针对原生开发面临的问题,业界一直都在努力寻找好的解决方案,而时至今日,已经有很多跨平台框架(“跨平台”特指 Android 和 iOS 两个平台),根据其原理,主要分为三类:
- H5 + 原生(Cordova、Ionic、微信小程序)
- JavaScript 开发 + 原生渲染 (React Native、Weex)
- 自绘UI + 原生 (Qt for mobile、Flutter)
Hybrid混合开发
将 App 中需要动态变动的内容通过HTML5(简称 H5)来实现,通过原生的网页加载控件WebView 来加载。 H5 + 原生 的开发模式为混合开发 ,App我们称之为混合应用或 Hybrid App ,如果一个应用的大多数功能都是 H5 实现的话,我们称其为 Web App。
优点:
混合开发中,H5 部分是可以随时改变而不用发版,动态化需求能满足;同时,由于 H5 代码只需要一次开发,就能同时在 Android 和 iOS 两个平台运行,这也可以减小开发成本。
缺点:
混合开发中,H5代码是运行在原生WebView 中,其 JavaScript 依然运行在一个权限受限的沙箱中,所以对于大多数系统能力都没有访问权限,如无法访问文件系统、不能使用蓝牙等。且性能体验不佳,对于复杂用户界面或动画,WebView 有时会不堪重任。
在此,我们需要提一下小程序,目前国内各家公司小程序应用层的开发技术栈是 Web 技术栈,而底层渲染方式基本都是 WebView 和原生相结合的方式。
Android 平台使用JSBridge
JSBridge 简单来讲,主要是 给 JavaScript 提供调用 Native 功能的接口,让混合开发中的『前端部分』可以方便地使用平台的地图定位、相册、摄像头等 Native 功能。
实际上,JSBridge 就像其名称中的『Bridge』的意义一样,是 Native 和非 Native 之间的桥梁,它的核心是 构建 Native 和非 Native 间消息通信的通道,而且是“双向通信”的通道。
原生调用H5的方法
WebView调用loadUrl(),会重新刷新页面,无返回结果。
// 调用js中的函数:jsFun(msg)
webView.loadUrl("javascript:jsFun('" + msg + "')");
evaluateJavascript(),不会使页面刷新,有返回结果。
mWebView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
//此处为 js 返回的结果
}
});
}
JS调用原生的方法
WebView通过调用addJavascriptInterface()方法(推荐)
// 参数1:Android的本地对象、 参数2:JS的对象
mWebView.addJavascriptInterface(new MyJSInterface(),"androidJsInterface");
private static class MyJSInterface{
public void showMessage(String msg){
Toast.makeText(WebViewActivity.this, "成功"+ msg, Toast.LENGTH_LONG).show();
}
}
原理: 通过对象映射的方式将Android中的本地对象和JS中的对象进行关联,从而实现JS调用Android的对象和方法。
漏洞:因为addJavascriptInterface绑定了一个Java对象实例,根据Java的反射机制,就可以获得更多方法,甚至间接获得更多的实例对象。Android 4.2版本之后,Google 在Android 4.2 版本中规定对被调用的函数以 @JavascriptInterface进行注解从而避免漏洞攻击。
@JavascriptInterface
public void showMessage(String msg)...
通过WebViewClient.shouldOverrideUrlLoading拦截url,分析意图执行相应操作。
需要与前端人员沟通如何定义Url,此url可以作为内部交互的意图,当网页加载后有一个url,可以进行判断后执行native的逻辑处理。
webView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//a.比如,点击网页按钮关闭Activity。这里url字符串查找是否包含“finish_app”关键字。
if(url.contains("finish_app")) {//退出Activity
finish();
}
//b.比如,这个是从WebView页面跳转另外一个Activity页面。
if (url.contains("MyLuckLog")){//会员页面
startActivity(new Intent(WebViewActivity.this,NewActivity.class));
}
return true;
}
});
例如,网易云音乐的app 里面的积分商城就是使用这种方式实现H5与native交互。微信和网易一样通过拦截url分析意图来执行相应的操作的。
缺陷:因为url是可以伪造的,存在一定的风险。例如,微信自己会在native代码里验证他的appid,所以一定程度上可以避免大部分的攻击。
通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调,拦截JS对话框alert()、confirm()、prompt() 消息。
Android通过 WebChromeClient
的onJsAlert()
、onJsConfirm()
、onJsPrompt()
方法回调分别拦截JS对话框 。得到他们的消息内容,然后分析意图执行相应操作。
同样,也需要和前端开发人员做好约定好消息内容的意图。推荐prompt方法拦截意图。
- 因为onJsPrompt调用较少,而onJsAlert、onJsConfirm可能调用频繁。
- 只有
prompt()
可以返回任意类型的值,而alert()没有返回值、confirm()只能返回两种状态(确定 / 取消)两个值。