这是一个能够在 Android
平台上面编译和运行 Java
代码的引擎,支持 JDK8
语法编译运行。
1、将
*.java
文件编译为*.class
类文件。 2、通过R8 | D8
工具将*.class
文件编译为Android
平台能够运行的*.dex
文件。这一步可以脱糖,可以在安卓平台运行JDK
高版本的语法糖,其内部实现基本上还是语法糖转换成了低版本兼容的代码。 3、将*.dex
文件通过DexClassLoader
加载到应用程序中,并寻找main
方法反射调用。 4、代理系统的输入System.in
和输出System.out
。
第一步:在Project
的build.gradle
内添加jitpack.io
仓库
allprojects {
repositories {
//...
mavenCentral()
}
}
第二步:添加依赖
dependencies {
//...
// 这个依赖比较大(9M 左右),因为内部(assets)包含了一个精简的 Jre,你可以自己选择去除或精简。
implementation 'io.github.xiaoyvyv:compiler-d8:<maven-version>'
}
在你的 Application
的 onCreate
中初始化库。
class App : Application() {
override fun onCreate() {
super.onCreate()
// init
JavaEngine.init(this)
}
}
JavaClassCompiler
提供了相关的方法,具体请查阅 JavaClassCompiler.kt。
注意该方法需要配合协程使用,在协程的作用域内调用
// 编译 class,libFolder 为第三方 jar 存放目录,没有传空即可
// 编译完成返回目标 classes.jar,内部通过协程在 IO 线程处理的
val compileJar: File = JavaEngine.classCompiler.compile(
sourceFileOrDir = javaFilePath,
buildDir = buildDir,
libFolder = null
) { taskName, progress ->
// 这里是进度,回调在主线程...
}
DexCompiler
提供了相关的方法,具体请查阅 JavaDexCompiler.kt
注意该方法需要配合协程使用,在协程的作用域内调用
// 编译 classes.dex,这一步相关的信息通过 System.xxx.print 输出
val dexFile = JavaEngine.dexCompiler.compile(compileJar.absolutePath, buildDir)
JavaProgram.kt
提供了相关的方法,具体请查阅 JavaProgram.kt
注意 chooseMainClassToRun
默认实现是选中匹配到的第一个 main
方法进行运行,你可以自己在该方法回调内去选择需要执行的的指定某个类。
- chooseMainClassToRun 第一个回调参数:匹配到的包含
main
方法的类 - chooseMainClassToRun 第二个回调参数:协程相关的回调,需要将选择的类回调回去
注意:chooseMainClassToRun 回调会使内部协程一直挂起,你应该及时的通过 continuation.resume() 或 resume.resumeWithException() 让内部知道处理结果,禁止忽略 continuation 的回调,否则会在后台一直占用资源
chooseMainClassToRun
、printOut
、printErr
回调均在主线程,可以进行 UI 操作。
run
方法运行完成(并不代表程序执行完成,例如你的代码启动了其他线程)会返回一个 programConsole
句柄,可以用于关闭输入输出流。
注意该方法需要配合协程使用,在协程的作用域内调用
// JavaEngine.
val programConsole = JavaEngine.javaProgram.run(dexFile, arrayOf("args"),
chooseMainClassToRun = { classes, continuation ->
val dialog = AlertDialog.Builder(this@CompileActivity)
.setTitle("请选择一个主函数运行")
.setItems(classes.toTypedArray()) { p0, p1 ->
p0.dismiss()
continuation.resume(classes[p1])
}
.setCancelable(false)
.setNegativeButton("取消") { d, v ->
d.dismiss()
continuation.resumeWithException(Exception("取消操作"))
}.create()
dialog.show()
dialog.setCanceledOnTouchOutside(false)
},
printOut = {
binding.printView.append(it)
},
printErr = {
binding.printView.append(it)
})
直接调用 programConsole.
的 inputStdin(String stdin)
方法即可输入数据。
programConsole.inputStdin(str)
JavaEngine.compilerSetting
提供了相关配置。JavaEngineSetting.kt
更多内容请查阅 Demo 和源码 compiler-d8
QQEmail:[email protected]