狭义的跨平台开发指的是跨Android 和 iOS,开发一套代码,可以同时在这两个系统上运行。
跨平台开发模式,随着技术的沉淀和新业务场景的出现,随着时间发展而不断演变,一般业界通用的说法是有三个时代的变迁过程。
第一个时代: Web容器时代
通过原生应用内嵌浏览器控件WebView的方式进行HTML5页面渲染,并定义HTML5和原生代码交互协议。
这个时代的典型的框架包括 Cordova(PhoneGap)、Ionic 和微信小程序。
在Android端,浏览器控件为WebView,在IOS端,浏览器控件为UIWebView或WKWebView。
原生与HTML5交互的协议叫作JSBridge。这种既有原生应用代码,又有Web应用代码的开发模式就称为Hybrid模式。
第二个时代:泛Web容器时代
web容器方案有生态繁荣、开发体验友好、生产效率高、扩平台的优势,但是它承载着大量web标准的web容器是比较笨重的,所以性能会比原生差。
泛容器时代的解决方案优化了web容器时代加载、解析、渲染流程,只保留了构建移动页面必要的web标准(如flexbox), 同时用原生自带的UI组件替代了浏览器控件,仅保持必要的空间渲染能力,从而使得渲染过程更加简化,也保证了良好的渲染性能。
这个时代的典型框架如React Native、Weex 和快应用,广义的还包括天猫的 Virtual View 等。
第三个时代:自绘引擎时代
泛web容器时代使用原生的UI控件进行界面渲染,解决了不少性能问题,但是也带来新的问题。框架本身要处理大量的平台相关的逻辑,随着系统版本变化和API的变化,还需要处理不同平台的原生控件渲染能力差异,修复各类奇奇怪怪的bug。
Flutter就开辟了一种全新的思路,即从头到尾重写一套跨平台的UI框架,包括渲染逻辑,甚至是开发语言。
渲染引擎依靠跨平台的Skia图形库来实现,Skia引擎会将使用Dart构建的抽象视图结构设计加工成GPU数据,交由OpenGL最终提供给GPU渲染。
开发语言选用的是同时支持JIT(Just-in-Time)和AOT(Ahead-of-Time)的Dart,不仅保证了开发效率,更提升了执行效率。
以上内容参考自极客时间课程Flutter核心技术与实战,作者:陈雨航
核心JSbridge的设计(API注入方式)
在前两个时代中,JSBridge都占据重要的位置,下面以容器时代为例,了解JSbridge的设计。
JSBridge是承载原生应用(Native)与Web应用(JS)之间的桥梁。
JSBridge技术的实现大体可以分为六个步骤。
1.定义全局桥对象
var Android = window.Android || (window.Android = {})
Android官方提供了接口,让JS可以调用Native
在web容器控件初始化的时候注入webView
// 原生安卓代码
// 添加 JavaScript 接口
webView.addJavascriptInterface(new WebAppInterface(this), "Android");
2.JS调用原生
业务场景如app初始化时,会调用原生的接口拿系统信息。
Android这边定义好命名空间后,JS就可以通过该命名空间将数据传递给原生。
<!-- assets/index.html -->
<!DOCTYPE html>
<html>
<body>
<h1>WebView JavaScript Demo</h1>
<script type="text/javascript">
function asyncNativeFunction() {
return new Promise((resolve, reject) => {
// 调用 Android 对象的 nativeFunction 方法并传递参数
Android.nativeFunction('Hello from JavaScript!', (response) => {
if (response.success) {
resolve(response.data);
} else {
reject(new Error(response.error));
}
});
});
}
async function callAsyncNativeFunction() {
try {
const response = await asyncNativeFunction();
console.log("Received response from Native: " + response);
} catch (error) {
console.error("Error:", error);
}
}
</script>
<button onclick="callAsyncNativeFunction()">Send Message to Android</button>
</body>
</html>
3.Native接收JS传递过来的参数
关键的代码如下
// 供javascript调用的接口
@JavascriptInterface
public void nativeFunction(String message, final ValueCallback<String> callback) {
// 处理从 JavaScript 传递过来的参数
System.out.println("Received message from JavaScript: " + message);
// 调用 JavaScript 的回调函数并传递参数
webView.post(() -> {
// 在webView容器里执行JS代码,执行相应的回调函数
webView.evaluateJavascript("javascript:callback('" + response + "')", null);
});
}
更完整代码如:
package com.example.webviewdemo;
import android.os.Bundle;
import android.webkit.JavascriptInterface;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private WebView webView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
webView = findViewById(R.id.webview);
webView.setWebViewClient(new WebViewClient());
webView.getSettings().setJavaScriptEnabled(true);
// 添加 JavaScript 接口
webView.addJavascriptInterface(new WebAppInterface(this), "Android");
// 加载本地 HTML 文件或网络页面
webView.loadUrl("file:///android_asset/index.html");
}
public class WebAppInterface {
Context mContext;
// Instantiate the interface and set the context
WebAppInterface(Context c) {
mContext = c;
}
@JavascriptInterface
public void nativeFunction(String message, final ValueCallback<String> callback) {
// 处理从 JavaScript 传递过来的参数
System.out.println("Received message from JavaScript: " + message);
// 调用 JavaScript 的回调函数并传递参数
webView.post(() -> {
// 在webView容器里执行JS代码,执行相应的回调函数
webView.evaluateJavascript("javascript:callback('" + response + "')", null);
});
}
}
}
4.Native 返回数据给JS
// 调用 JavaScript 的回调函数并传递参数
webView.post(() -> {
// 在webView容器里执行JS代码,执行相应的回调函数
webView.evaluateJavascript("javascript:callback('" + response + "')", null);
});
5.通过回调函数,JS接收Natvie返回数据
function asyncNativeFunction() {
return new Promise((resolve, reject) => {
Android.nativeFunction('Hello from JavaScript!', (response) => {
// 这里接收回调传过来的数据
if (response.success) {
resolve(response.data);
} else {
reject(new Error(response.error));
}
});
});
}
总结
总结这整个流程,大致有以下步骤
- app初始化的时候,android通过
webView.addJavascriptInterface(new WebAppInterface(this), "Android")将Android相关的功能函数注入到window全局对象上。 - JS通过
Android.nativeFunction('Hello from JavaScript!',callbackFn)调用安卓的nativeFunction方法,并传入数据和回调函数 - 安卓的
nativeFunction处理好数据后,通过在webview里面执行JS代码,触发传入的回调函数的执行 - JS通过回调函数的执行,接收到从native回传的数据,可以依据数据做相应的处理。