一、问题背景
在小游戏开发中,常见的一种操作就是设置绘制属性,比如lineWidth、fillStyle这种,这在WebView方案中,因为全跑在JS环境中,没有任何问题。但是在J2V8方案中,因为绘制逻辑在JS层,绘制实现在Java层,这就导致JS层的属性设置,无法传达到Java层。
本文项目地址:v8x
1 | const ctx = canvas.getContext('2d'); // 这里的ctx是Java层返回的V8Object对象 |
上述代码中,ctx.lineWidth = 10;
修改后,在Java层是无法感知的。
1.1 以往方案
以往方案中,是通过JS层,为ctx对象做代理,拦截到属性变化,再通过调用J2V8的绑定方法,告知Java层,某个属性变化的了。
这样做,有以下几个缺点:
有这种需求的类可能很多,每一个都要JS层做相应的适配,这会增加工作量和调试沟通成本;
并不是所有属性的变化都是Java层关心的;
JS层并不知道Java层的属性与类的关系,容易错乱,导致通知了属性变化,却不知道是哪个对象的属性变化了;
后期Java层类做了变动,需要JS层做相应修改,一旦遗漏,就会产生bug;
基于解决以上问题的目的,开发出新的方案——J2V8Binding。
二、J2V8Binding
J2V8Binding的目标是,将JS层属性的变更感知,全部控制在Java层内,无需JS层参与额外代码。
2.1 基本原理
与旧方案类似的,我们仍然需要一个JS层的代理,而与旧方案不同的,新方案的代理是由Java层“生成”。
1 | private const val CREATE_PROXY_JS = """ |
这里我们声明一段JS代码,这里用于创建JS层的代理对象。在初始化V8时,将此段JS代码注入到V8环境中,当我们需要一个JS层代理对象时,就可以执行此JS函数v8CreateProxy
创建一个JS层的代理对象。
1 | private fun createJSProxy(v8obj: V8Object): V8Object { |
2.2 封装
对以上基本逻辑进行封装,有关键类V8Binding、V8Manager、V8Field和V8Method。
2.2.1 V8Binding
一个需要绑定的类,需要实现V8Binding接口。
1 | interface V8Binding { |
在getMyBinding方法中,我们使用V8Manager来创建或者获取一个与当前对象绑定的V8Object对象。
2.2.2 V8Manager
V8Manager是一个针对某个V8环境的管理类,主要是用于维护JS层对象与Java层的绑定关系,执行创建绑定关系、删除绑定关系,分发属性变更事件等作用,J2V8Binding的主要核心逻辑的所在。
由于此处涉及到大量代码细节,故不在此罗列代码具体分析。
2.2.3 V8Field和V8Method
这两个类是注解类,用于标记类内属性和方法。
@V8Field(binding=?): 标记属性,其中有一个binding属性,标记此属性是否需要绑定,如果需要绑定,则会有事件回调,默认值为false。
@V8Method(jsFuncName=?): 标记方法,其中有一个jsFuncName属性,用于标识对应的JS函数的名称。
2.3 基本使用方法
一个简单的示例类如下:
1 | class User : V8Binding { |
当使用这个User类的一个对象user时候,可以按照如下方式使用。
1 | val v8 = V8.createRuntime() |
这里,我们在JS环境中,增加了一个user变量。
然后在JS层,执行以下代码。
1 | user.name = "Smith"; // Java层不会感知到 |
在修改name
属性时,由于此属性是被V8Field标记binding为false,则不会通知Java层此值的修改事件。
在修改age
属性时,由于此值被V8Field标记binding为true,会通知Java层此值的修改事件。
在修改location
属性时,虽然此值为一个对象,但是被V8Field标记binding为true,会通知Java层此值的修改事件。注意:用V8Field标记的对象类型,必须为V8可以接受的数据类型或者V8Binding类型。
在修改introduction
时,虽然此值没有被V8Field标记,但是由于在getCareForFieldKeys返回的数组中,同样会有事件通知。
有了这种机制,小游戏开发中,就可以很方便的感知到绘制属性的变化。
2.4 仍然存在的问题
目前仍然有一种问题是无法解决的,那就是数组中某个数据发生变化时,Java层是无法得知的。
如下代码:
1 | const image = ...; |
目前这种场景较少,只在部分demo中见到直接修改图像像素数据的情况。
三、总结
以上代码片段只为展示逻辑主脉络,省略了大量细节,需要看细节部分,可以从V8Manager这里作为入口。源码链接:v8x
Author: boybeak