flutter dart 、java/kotlin、V8引擎之间交互
在前一篇 《使用Flutter + V8开发小程序引擎(一)》 介绍了我们是用html+css来做界面描述,交互是用js,那么具体是怎么处理这两者的关系,以达到可动态下发及渲染页面的呢?
java/kotlin 与 Dart交互,这部分应该不用说,做过flutter的同学应该都知道 java/kotlin 与 V8交互,这部分应该很少人接触过,有什么用呢?那么请继续往下看
首先我们先用微信小程序举例,做过微信小程序或者了解过原理的同学应该知道,小程序开发在界面描述上是使用数据绑定,则在需要动态数据的文本或者属性时,通过双大花括号写一个表达式与对应数据进行绑定。请看以下一个简单例子:
<text>{{message1 + message2}}</text> Page({ data: { message1: "hello ", message2: " world" }, onLoad(e) {}, onUnload() {} }) 复制代码
以上例子,text 组件的文本与Page中的data的message1、message2进行绑定,那么如何计算表达式message1 + message2的值呢?则需要用到V8引擎来计算
通过eval将Page加载到V8引擎(ps:这个方法是通过framework.js文件在初始化V8的时候提前注入到V8里面,具体请查看源码实现)
this.__native__evalInPage = function (jsContent) { if (!jsContent) { console.log("js content is empty!"); } eval(jsContent); } 复制代码
我们看下加载后在V8里面的Page对象
还有data里面的数据
同上__native__evalInPage方法,__native__getExpValue也是通过framework.js文件提前注入到V8的一个方法
this.__native__getExpValue = function (script) { const expFunc = exp => { return new Function('', 'with(this){' + exp + '}').bind( this.data )(); }; var value = expFunc(script); if (value instanceof Object) { return JSON.stringify(value); } if (value instanceof Array) { return JSON.stringify(value); } return value; } 复制代码
在java/kotlin调用以下方法,先获取到V8里面对应的页面对象,在通过executeFunction执行V8里面的__native__getExpValue方法
fun handleExpression(pageId: String, expression: String): String? { val page = getV8Page(pageId) return page?.executeFunction("__native__getExpValue", V8Array(V8Manager.v8).push(expression)).toString() } 复制代码
val pageId = "0x0001" val exp = "return message1 + message2" val result = handleExpression(pageId, exp) 复制代码
这里注入一个console的对象给到V8,即在js中执行console.log()的时候会回调到JSConsole的log方法(ps:这里只是列举一种,还有其他方式,后续有机会再介绍)
private fun registerFunc() { val v8Console = V8Object(v8) v8.add("console", v8Console) val jsConsole = JSConsole() v8Console.registerJavaMethod(jsConsole, "log", "log", arrayOf<Class<*>>(java.lang.Object::class.java)) v8Console.release() } 复制代码
class JSConsole { fun log(string: Any) { Log.d("js", string.toString()) } } 复制代码
到这里我们就讲完了java/kotlin与V8交互的部分了
在dart端通过MethodChannel调用
var _methodChannel = MethodChannel("com.cc.hybrid/method"); void _initScript(String script) { _methodChannel.invokeMethod( "attach_page", {"pageId": _pageId, "script": decodeBase64(script)}); } 复制代码
在java/kotlin端实现onMethodCall
override fun onMethodCall(methodCall: MethodCall, result: MethodChannel.Result) { when (methodCall.method) { Methods.ATTACH_PAGE -> { if (methodCall.hasArgument("pageId") && methodCall.hasArgument("script")) { val id = methodCall.argument<String>("pageId") val script = methodCall.argument<String>("script") Logger.d("lry", "attach_page pageId = $id script = $script") JSPageManager.attachPageScriptToJsCore(id!!, script!!) result.success("success") } } ... } } 复制代码
-java/kotlin通知dart
在dart端实现BasicMessageChannel消息监听
var _basicChannel = BasicMessageChannel<String>('com.cc.hybrid/basic', StringCodec()); _initBasicChannel() async { _basicChannel.setMessageHandler((String message) { print('Flutter Received: $message'); var jsonObj = jsonDecode(message); var pageId = jsonObj['pageId']; MessageHandler handler = _handlers[pageId]; if (null != handler) { handler.onMessage(jsonObj); } else { // 实时调试socket过来的数据 var jsonObject = jsonDecode(jsonObj['message']); var pageCode = jsonObject['pageCode']; var content = jsonObject['content']; _pages.putIfAbsent(pageCode, () => content); _handlers.forEach((k, v) { if (k.startsWith(pageCode)) { v.onMessage(jsonObj); } }); } return Future<String>.value("success"); }); } 复制代码
在java/kotlin端发送消息
private fun sendMessage2Flutter(type: Int, pageId: String, content: String) { val jsonObject = JSONObject() jsonObject.put("type", type) jsonObject.put("pageId", pageId) jsonObject.put("message", content) channel.send(jsonObject.toString()) } 复制代码
dart -> java/kotlin 场景(以上面text渲染的流程)
<text>{{message1 + message2}}</text> Page({ data: { message1: "hello ", message2: " world" } }) 复制代码
java/kotlin -> dart 场景(例如:点击打印日志)
<raisedbutton onclick="onclick"> <text>{{message1 + message2}}</text> </raisedbutton> Page({ data: { message1: "hello ", message2: " world" }, onclick(e) { var msg = this.data.message1 + this.data.message2; console.log(msg); } }) 复制代码
从上面的介绍,我们可以看到,要实现动态下发,需要dart->java/kotlin->V8->java/kotlin->dart这样一条长链路,这样子效率相对较低,而且对Android跟iOS来说是需要分别对接与原生交互的差异部分
js引擎方面,Android是用V8,iOS用的是JsCore,目前没有实现,当然,如果实现dart直接调用V8及JsCore的Api,则整个链路可以简化为dart->V8/JsCore->dart,提高效率的同时,两端也没有那么多的差异处理