flutter dart 、java/kotlin、V8引擎之間交互css
在前一篇《使用Flutter + V8開發小程序引擎(一)》介紹了咱們是用html+css來作界面描述,交互是用js,那麼具體是怎麼處理這二者的關係,以達到可動態下發及渲染頁面的呢?html
java/kotlin 與 Dart交互,這部分應該不用說,作過flutter的同窗應該都知道java
java/kotlin 與 V8交互,這部分應該不多人接觸過,有什麼用呢?那麼請繼續往下看git
首先咱們先用微信小程序舉例,作過微信小程序或者瞭解過原理的同窗應該知道,小程序開發在界面描述上是使用數據綁定,則在須要動態數據的文本或者屬性時,經過雙大花括號寫一個表達式與對應數據進行綁定。請看如下一個簡單例子:github
<text>{{message1 + message2}}</text>
Page({
data: {
message1: "hello ",
message2: " world"
},
onLoad(e) {},
onUnload() {}
})
複製代碼
以上例子,text 組件的文本與Page中的data的message一、message2進行綁定,那麼如何計算表達式message1 + message2的值呢?則須要用到V8引擎來計算express
經過eval將Page加載到V8引擎(ps:這個方法是經過framework.js文件在初始化V8的時候提早注入到V8裏面,具體請查看源碼實現)json
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的一個方法bash
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,提升效率的同時,兩端也沒有那麼多的差別處理
源碼地址:傳送門
系列文章: