0%

Rhino与Android的互操作

Rhino作为基于Java的JavaScript实现,可以方便地在Android中运行JavaScript脚本。这里记录一下相互调用的一些问题。

JSON序列化

为了便于互操作,JavaScript和Java的数据均以JSON字符串进行交换。而Rhino的JSON.stringify和Java的数据类型不兼容,对Java类型的变量操作会出现循环,同样的,用于序列化Java数据类型的Gson库也不支持Rhino的JavaScript数据类型。

仿照解决JSON.stringify的方法,判断变量是否为Java对象,是则调用Gson进行转换:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function handleCircular() {
var cache = []
var keyCache = []
return (key, value) => {
if(value instanceof Packages.java.lang.Object) {
return JSON.parse(Packages.${JsonUtil.javaClass.name}.INSTANCE.toJson(value));
}
if (typeof value === 'object' && value !== null) {
var index = cache.indexOf(value);
if (index !== -1) return '[Circular ' + keyCache[index] + ']'
cache.push(value)
keyCache.push(key || 'root')
}
return value
}
}

var tmp = JSON.stringify;
JSON.stringify = function(value, replacer, space) {
replacer = replacer || handleCircular();
return tmp(value, replacer, space);
}

异步线程

Rhino没有实现asyncawait。借助Java线程池实现异步,先写个带参数的Callable

1
2
3
4
5
class JsAsyncTask(val js: (Any?) -> Any?, private val params: Any?) : Callable<Any?> {
override fun call(): Any? {
return js(params)
}
}

async返回闭包函数,创建callable,加入线程池并返回Future

1
2
3
4
5
6
7
8
9
var AsyncTask = Packages.${JsAsyncTask::class.java.name};
var _cachedThreadPool = Packages.${App::class.java.name}.Companion.getCachedThreadPool();

function async(fun){
return (param)=>{
var task = new AsyncTask(function(params){ try { return JSON.stringify(fun(params)) } catch(e){ return new Error(e) } }, param)
return _cachedThreadPool.submit(task)
}
}

为了看起来像,再包个await,把asyncstringify的转回来:

1
2
3
4
5
function await(task){
var data = task.get()
if(data instanceof Error) throw data.message
return JSON.parse(data)
}