小程序基础库与Android之间通信优化的可能

最近在学习graalvm,发现有一个graaljs项目,项目中介绍可以让java与JavaScript做数据转换,比如JavaScript中可以使用java的数据类型与结构。突然想到之前遇到的一个问题,小程序中开发的代码和基础库的部分代码都是j2v8来执行的,其中的数据通信是通过bridge去做的,其实就是把数据结构都转换为字符串,这样就存在问题,比如Android这边的网络请求、音视频帧数据、文件流对外都是通过java封装的对象,无法直接在JavaScript中使用,只能是通过转换为base64来做,而且一个buffer数据基本需要两次转换,sdk转一次,基础库转一次,比较消耗性能。

如果JavaScript和java之间的结构互通,那是不是就解决这个问题了,理论上来说如果js引擎是通过java编写的解释器去执行,那确实是可以这样的。

GraalJs

GraalJs核心代码如下:

BufferedReader br = null;
try {
    br = new BufferedReader(new FileReader("/Users/xxx/tmp/index.md"));
} catch (FileNotFoundException e) {
    throw new RuntimeException(e);
}
try (Context context = Context.newBuilder().allowAllAccess(true).build()) {
    Set<String> languages = context.getEngine().getLanguages().keySet();
    for (String id : languages) {
        context.initialize(id);
        if (id.equals("js")) {
            context.eval("js", "function main(data) { console.log(data.readLine()); }");
            Value funMain = context.getBindings("js").getMember("main");
            funMain.execute(br);
        }
    }
}

这里我们使用GraalVM的jdk是可以运行的,可以看到JavaScript中接收到的data其实是java的BufferedReader,执行之后就发现JavaScript中的data.readLine成功执行并打印了文件内容,WC,如果Android可以这样那就太强了。可惜,GraalJs必须使用GraalVM提供的jdk,也不好直接替换android中使用的jdk。。。
那有没有其他办法?对了,GraalVM是可以将java代码生成分享库的,比如.so,Android也正好可以加载so库。有没有可能这样搞。最终项目如下https://github.com/schizobulia/GraalJs-Android ,可以看到releases中tag为v0.0.8中有arrch64和armv7的so库
小程序基础库与Android之间通信优化的可能_第1张图片
可惜拿到so库之后去Android中使用发现,这个so库不是ABI。麻了…
然后查了相关资料发现GraalVM目前也没有计划支持Android。

不过好在最后发现ChatGPT确实是个好东西,现在查资料基本都是先问问ChatGPT,上面项目中的GitHub Action配置文件也是ChatGPT写的。

参考资料:
Android 关于CPU类型的so文件兼容问题(ABI) - 掘金
一分钟搞明白Android的.so文件、ABI和CPU的关系-CSDN博客

J2V8

那有没有可能从j2v8入手,比如node很多内置函数其实在v8中也是没有的,如果可以在v8中实现这些函数然后提供给JavaScript调用好像有可以,然后开始查node这部分功能的实现原理,最后看到一篇博客:Node.js的底层原理 - 掘金其中说到这个部分的实现是V8的自定义拓展实现的,好像确实可以。不过后来试了试,发现还是不行。

主要原因有:比如文件流、帧数据在Android端对外都是通过java封装的对象,v8的自定义扩展需要c++或者v8提供的语言来编写,依然存在语言之间数据结构转换的问题。

次要原因:实在不想看c++的代码

Rhino

然后通过ChatGPT最终选择试试Rhino,因为他也是java写的,缺点是性能可能没没v8那么强,JavaScript中新的语法糖或者全局属性可能没那么快支持到(不过可以使用一些打包库和第三方静态分析库去优化)。
小程序中存在大量的数据交互,如果使用Rhino说不定会有更好的效果,以下为示例代码,可以看到JavaScript中可以使用java的数据结构,这得益于解释器是使用java写的,节省了不同语言之间数据结构的转换。

var System = java.lang.System;
System.out.println(myClass.add(1, 2));
function test(str) {
    myClass.show(str);
}

通过下面代码的测试,发现读取一个53kb的文件从调用test方法到show方法的触发基本没消耗时间。而且根据这种方式其实也很节省内存,因为数据其实只保存在了jvm中,按j2v8的思路同一份数据需要在v8、jvm中各保存一份。
以下为完整代码:

package com.example.myapplication

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.example.myapplication.ui.theme.MyApplicationTheme
import org.mozilla.javascript.Context
import org.mozilla.javascript.Function
import org.mozilla.javascript.Scriptable
import org.mozilla.javascript.ScriptableObject
import java.nio.ByteBuffer
import java.nio.CharBuffer
import java.nio.charset.Charset


class MainActivity : ComponentActivity() {
    class MyFunctions {
        fun add(a: Int, b: Int): Int {
            return a + b
        }

        fun show(buffer: ByteArray) {
            println("这是js传递过来的:")
            println(System.currentTimeMillis())
            println(buffer)
//            val text = String(buffer)
//            println("File Content: $text")
        }
    }
    class JsEngine {
        public val rhino: Context = Context.enter()
        public val scope: Scriptable

        init {
            rhino.optimizationLevel = -1
            scope = rhino.initStandardObjects()

        }

        fun evaluate(jsCode: String) {
            try {
                ScriptableObject.putProperty(scope,"myClass", Context.javaToJS(MyFunctions(), scope))
                ScriptableObject.putProperty(scope, "javaContext", Context.javaToJS(this, scope))
                ScriptableObject.putProperty(scope, "javaLoader", Context.javaToJS(JsEngine::class.java.classLoader, scope))
                rhino.evaluateString(scope, jsCode, JsEngine::class.java.simpleName, 1, null)
            } finally {
//                Context.exit()
            }
        }

        fun callFunction(functionName: String, vararg args: Any): Any? {
            try {
                val function = scope[functionName, scope] as Function
                return function.call(rhino, scope, scope, args)
            } finally {
//                Context.exit()
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyApplicationTheme {
                // A surface container using the 'background' color from the theme
                Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
                    Greeting("Android")
                }
            }
        }

        val inputStream = resources.assets.open("data.txt");
        val size = inputStream.available()
        val buffer = ByteArray(size)
        inputStream.read(buffer)
        inputStream.close()
        Thread {
            val jsCode = """
            var System = java.lang.System;
            System.out.println(myClass.add(1, 2));
            function test(str) {
                myClass.show(str);
            }
                """.trimIndent()
            val jsEngine = JsEngine()
            jsEngine.evaluate(jsCode)
            println(System.currentTimeMillis())
            jsEngine.callFunction("test", buffer)
        }.start()
    }
}

@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
    Text(
            text = "Hello $name!",
            modifier = modifier
    )
}

@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
    MyApplicationTheme {
        Greeting("Android")
    }
}

最后:ChatGPT真

你可能感兴趣的:(小程序,android)