Skip to content

Commit a4e6590

Browse files
committed
Dev: update README.md
1 parent 0926b12 commit a4e6590

4 files changed

Lines changed: 128 additions & 461 deletions

File tree

README.md

Lines changed: 101 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
1-
# JavaCompileEngine
2-
[![](https://jitpack.io/v/xiaoyvyv/JavaCompileEngine.svg)](https://jitpack.io/#xiaoyvyv/JavaCompileEngine)
3-
> 这是一个能够在Android上面编译和运行Java代码的引擎,其原理为将Java代码编译为class文件。
4-
> 再将class文件转换为dex文件(dex是Android平台上(Dalvik虚拟机)的可执行文件)。
5-
> 最后再运行dex文件,并且代理系统的输入和输出。
1+
2+
# JavaCompileEngine (支持 JDK8 语法)
3+
4+
[![Mavnen Central](https://img.shields.io/maven-central/v/io.github.xiaoyvyv/compiler-d8?label=Maven%20Central)](https://search.maven.org/search?q=io.github.xiaoyvyv%20compiler-d8)
5+
6+
这是一个能够在 `Android` 平台上面编译和运行 `Java` 代码的引擎,支持 `JDK8` 语法编译运行。
7+
8+
#### 运行原理:
9+
10+
> 1、将 `*.java` 文件编译为 `*.class` 类文件。
11+
> 2、通过 `Google``R8 | D8` 工具将 `*.class` 文件编译为 `Android` 平台能够运行的 `*.dex` 文件。这一步可以脱糖,可以在安卓平台运行 `JDK` 高版本的语法糖,其内部实现基本上还是语法糖转换成了低版本兼容的代码。
12+
> 3、将 `*.dex` 文件通过 `DexClassLoader` 加载到应用程序中,并寻找 `main` 方法反射调用。
13+
> 4、代理系统的输入`System.in` 和输出 `System.out`
614
715

816
## 示例下载
@@ -13,190 +21,121 @@
1321
|:--|:--|:--|
1422
| ![截图预览](app_image/1.jpg?raw=true) |![截图预览](app_image/2.jpg?raw=true) | ![截图预览](app_image/3.jpg?raw=true) |
1523

16-
## 1.安装
24+
## 1、引入依赖
1725
第一步:在`Project``build.gradle`内添加`jitpack.io`仓库
18-
```kotlin
26+
```groovy
1927
allprojects {
2028
repositories {
21-
...
22-
maven { url 'https://jitpack.io' }
29+
//...
30+
31+
mavenCentral()
2332
}
2433
}
2534
```
2635
第二步:添加依赖
27-
```kotlin
36+
```groovy
2837
dependencies {
29-
...
30-
// 两个都是必须,dx这个引用是转换dex文件相关
31-
implementation 'com.github.xiaoyvyv.JavaCompileEngine:library:1.1'
32-
implementation 'com.github.xiaoyvyv.JavaCompileEngine:dx:1.1'
33-
...
38+
//...
39+
40+
// 这个依赖比较大(9M 左右),因为内部(assets)包含了一个精简的 Jre,你可以自己选择去除或精简。
41+
implementation 'io.github.xiaoyvyv:compiler-d8:<maven-version>'
3442
}
3543
```
3644

37-
## 2.继承 JavaEngineAplication
38-
将你的`Application`修改为继承于`JavaEngineAplication`
39-
```java
40-
public class YourApplication extends JavaEngineApplication {
45+
## 2、初始化
46+
在你的 `Application``onCreate` 中初始化库。
4147

42-
}
43-
```
44-
将其并设置到`manifests`
45-
```xml
46-
<application
47-
android:name=".YourApplication"
48-
android:allowBackup="false"
49-
android:icon="@mipmap/ic_launcher"
50-
android:label="@string/app_name"
51-
android:roundIcon="@mipmap/ic_launcher_round"
52-
android:supportsRtl="true"
53-
android:theme="@style/AppTheme.NoActionBar">
54-
...
55-
</application>
56-
```
57-
## 3.创建JavaConsole
58-
`JavaConsole`用于代理系统输入输出
59-
```java
60-
private TextView printView;
61-
62-
/**
63-
* Java控制台对象
64-
*/
65-
private JavaConsole javaConsole;
66-
67-
@Override
68-
protected void onCreate(Bundle savedInstanceState) {
69-
super.onCreate(savedInstanceState);
70-
71-
...
72-
printView = findViewById(R.id.printView);
73-
...
74-
75-
// 新建一个控制台对象,传入输出监听(回调为主线程)
76-
javaConsole = new JavaConsole(new JavaConsole.AppendStdListener() {
77-
@Override
78-
public void printStderr(CharSequence err) {
79-
printView.append(err);
80-
}
48+
```kotlin
49+
class App : Application() {
8150

82-
@Override
83-
public void printStdout(CharSequence out) {
84-
printView.append(out);
85-
}
86-
});
51+
override fun onCreate() {
52+
super.onCreate()
53+
54+
// init
55+
JavaEngine.init(this)
8756
}
57+
}
8858
```
8959

90-
## 4.编译 Java文件
91-
`ClassCompiler`提供了相关的方法,具体请查阅 [ClassCompiler.java](https://github.com/xiaoyvyv/JavaCompileEngine/blob/master/library/src/main/java/com/xiaoyv/javaengine/compile/ClassCompiler.java)
92-
93-
```java
94-
/**
95-
* 开始编译
96-
*/
97-
private void compileJava() {
98-
printView.setText(null);
99-
100-
// 创建Java文件,写入代码内容
101-
String javaFilePath = PathUtils.getExternalAppFilesPath() + "/SingleExample/Main.java";
102-
FileIOUtils.writeFileFromString(javaFilePath, String.valueOf(codeText.getText()));
103-
104-
// 保存 Class文件 的文件夹
105-
String saveClassFolder = PathUtils.getExternalAppFilesPath() + "/SingleExample/Class";
106-
107-
// 编译 Java 文件
108-
JavaEngine.getClassCompiler().compile(javaFilePath, saveClassFolder, new CompilerListener() {
109-
@Override
110-
public void onSuccess(String path) {
111-
LogUtils.e(path);
112-
// 编译成功 path 为class文件路径
113-
// 接下来将 class文件 转换为 Dex文件
114-
compileDex(path);
115-
}
60+
## 3、编译 Java 文件
61+
`JavaClassCompiler` 提供了相关的方法,具体请查阅 [JavaClassCompiler.kt](https://github.com/xiaoyvyv/JavaCompileEngine/blob/master-d8/compiler-d8/src/main/java/com/xiaoyv/java/compiler/tools/java/JavaClassCompiler.kt)
11662

117-
@Override
118-
public void onError(Throwable error) {
119-
// 编译失败
120-
printView.setText(Html.fromHtml("<font color=\"#F00\">" + error.toString() + "</font>"));
121-
}
122-
});
123-
}
63+
> 注意该方法需要配合协程使用,在协程的作用域内调用
64+
65+
```kotlin
66+
// 编译 class,libFolder 为第三方 jar 存放目录,没有传空即可
67+
// 编译完成返回目标 classes.jar,内部通过协程在 IO 线程处理的
68+
val compileJar: File = JavaEngine.classCompiler.compile(
69+
sourceFileOrDir = javaFilePath,
70+
buildDir = buildDir,
71+
libFolder = null
72+
) { taskName, progress ->
73+
74+
// 这里是进度,回调在主线程...
75+
}
12476
```
125-
## 5.转换 class文件 到 dex文件
126-
`DexCompiler`提供了相关的方法,具体请查阅 [DexCompiler.java](https://github.com/xiaoyvyv/JavaCompileEngine/blob/master/library/src/main/java/com/xiaoyv/javaengine/compile/DexCompiler.java)
127-
128-
```java
129-
/**
130-
* 将 Class文件 转换为 Dex文件
131-
*
132-
* @param classFilePath class文件路径
133-
*/
134-
private void compileDex(String classFilePath) {
135-
// 先创建 Dex空白文件(不能创建在 classFilePath 的同级或子目录)
136-
String dexFilePath = PathUtils.getExternalAppFilesPath() + "/SingleExample/Dex/Main.dex";
137-
138-
JavaEngine.getDexCompiler().compile(classFilePath, dexFilePath, new CompilerListener() {
139-
@Override
140-
public void onSuccess(String dexPath) {
141-
LogUtils.e(dexPath);
142-
// 编译成功 path 为dex文件路径
143-
// 接下运行 dex文件
144-
runDex(dexPath);
145-
}
77+
## 4、转换 class文件 到 dex文件
78+
`DexCompiler` 提供了相关的方法,具体请查阅 [JavaDexCompiler.kt](https://github.com/xiaoyvyv/JavaCompileEngine/blob/master-d8/compiler-d8/src/main/java/com/xiaoyv/java/compiler/tools/dex/JavaDexCompiler.kt)
14679

147-
@Override
148-
public void onError(Throwable error) {
149-
// 转换失败
150-
printView.setText(Html.fromHtml("<font color=\"#F00\">" + error.toString() + "</>"));
151-
}
152-
});
153-
}
80+
> 注意该方法需要配合协程使用,在协程的作用域内调用
81+
82+
```kotlin
83+
// 编译 classes.dex,这一步相关的信息通过 System.xxx.print 输出
84+
val dexFile = JavaEngine.dexCompiler.compile(compileJar.absolutePath, buildDir)
15485
```
155-
## 6.运行 dex文件
156-
运行 Dex文件前,必须开启控制台`javaConsole.start()`,程序执行完成后必须关闭控制台`javaConsole.stop()`。程序运行中时禁止重复运行(如,程序等待用户输入时会挂起,此时严禁再次运行程序,需要完成挂起的程序方可继续运行新程序)
157-
`DexExecutor`提供了相关的方法,具体请查阅 [DexExecutor.java](https://github.com/xiaoyvyv/JavaCompileEngine/blob/master/library/src/main/java/com/xiaoyv/javaengine/executor/DexExecutor.java)
158-
```java
159-
/**
160-
* 运行 Dex文件
161-
*
162-
* @param dexPath Dex文件路径
163-
*/
164-
private void runDex(String dexPath) {
165-
// 运行 Dex文件前,必须开启控制台
166-
// 运行 Dex文件前,必须开启控制台
167-
// 运行 Dex文件前,必须开启控制台
168-
javaConsole.start();
169-
170-
// 第二个参数为运行时,传入 main(String[] args)方法 的参数 args
171-
String[] args = new String[]{};
172-
JavaEngine.getDexExecutor().exec(dexPath, args, new ExecuteListener() {
173-
@Override
174-
public void onExecuteFinish() {
175-
// 运行完成,关闭控制台
176-
javaConsole.stop();
177-
}
86+
## 5、运行 dex文件
87+
`JavaProgram.kt` 提供了相关的方法,具体请查阅 JavaProgram.kt](https://github.com/xiaoyvyv/JavaCompileEngine/blob/master-d8/compiler-d8/src/main/java/com/xiaoyv/java/compiler/tools/exec/JavaProgram.kt)
88+
89+
注意 `chooseMainClassToRun` 默认实现是选中匹配到的第一个 `main` 方法进行运行,你可以自己在该方法回调内去选择需要执行的方法。
90+
- chooseMainClassToRun 第一个回调参数:匹配到的包含 `main` 方法的类
91+
- chooseMainClassToRun 第二个回调参数:协程相关的回调,需要将选择的类回调回去
92+
93+
<font color="#ff0000">注意:chooseMainClassToRun 回调会使内部协程一直挂起,你应该及时的通过 continuation.resume() 或 resume.resumeWithException() 让内部知道处理结果,禁止忽略 continuation 的回调,否则会在后台一直占用资源</font>
94+
95+
`chooseMainClassToRun``printOut``printErr` 回调均在主线程,可以进行 UI 操作。
17896

179-
@Override
180-
public void onExecuteError(Throwable error) {
181-
// 运行完成,关闭控制台
182-
javaConsole.stop();
97+
`run` 方法运行完成(并不代表程序执行完成,例如你的代码启动了其他线程)会返回一个 `programConsole` 句柄,可以用于关闭输入输出流。
18398

184-
// 执行出错
185-
printView.setText(Html.fromHtml("<font color=\"#F00\">" + error.toString() + "</>"));
99+
> 注意该方法需要配合协程使用,在协程的作用域内调用
100+
101+
```kotlin
102+
// JavaEngine.
103+
val programConsole = JavaEngine.javaProgram.run(dexFile, arrayOf("args"),
104+
chooseMainClassToRun = { classes, continuation ->
105+
val dialog = AlertDialog.Builder(this@CompileActivity)
106+
.setTitle("请选择一个主函数运行")
107+
.setItems(classes.toTypedArray()) { p0, p1 ->
108+
p0.dismiss()
109+
continuation.resume(classes[p1])
186110
}
187-
});
188-
}
111+
.setCancelable(false)
112+
.setNegativeButton("取消") { d, v ->
113+
d.dismiss()
114+
continuation.resumeWithException(Exception("取消操作"))
115+
}.create()
116+
117+
dialog.show()
118+
dialog.setCanceledOnTouchOutside(false)
119+
},
120+
printOut = {
121+
binding.printView.append(it)
122+
},
123+
printErr = {
124+
binding.printView.append(it)
125+
})
189126
```
190-
## 7.输入数据 如 Scanner等等的处理
191-
直接调用` JavaConsole.``inputStdin(String stdin)`方法即可输入数据。
192-
```java
193-
javaConsole.inputStdin(str);
127+
## 6、输入数据 如 Scanner等等的处理
128+
直接调用 `programConsole.``inputStdin(String stdin)` 方法即可输入数据。
129+
```kotlin
130+
programConsole.inputStdin(str)
194131
```
132+
## 7、编译相关设置
133+
`JavaEngine.compilerSetting` 提供了相关配置。[JavaEngineSetting.kt](https://github.com/xiaoyvyv/JavaCompileEngine/blob/master-d8/compiler-d8/src/main/java/com/xiaoyv/java/compiler/JavaEngineSetting.kt)
195134

196-
## 8.问题
197-
更多内容请查阅 [Demo](https://github.com/xiaoyvyv/JavaCompileEngine/tree/master/app) 和源码 [library](https://github.com/xiaoyvyv/JavaCompileEngine/tree/master/library/)
135+
## 8问题
136+
更多内容请查阅 [Demo](https://github.com/xiaoyvyv/JavaCompileEngine/tree/master/app) 和源码 [compiler-d8](https://github.com/xiaoyvyv/JavaCompileEngine/tree/master/compiler-d8/)
198137

199-
## 9.反馈
138+
## 9反馈
200139
QQEmail:1223414335@qq.com
201140

202141

app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ dependencies {
4242
implementation 'com.google.android.material:material:1.5.0'
4343
implementation 'com.blankj:utilcodex:1.31.0'
4444

45-
// implementation 'io.github.xiaoyvyv:compiler-d8:1.0.1'
46-
implementation project(':compiler-d8')
45+
implementation 'io.github.xiaoyvyv:compiler-d8:1.0.2'
46+
// implementation project(':compiler-d8')
4747

4848
testImplementation 'junit:junit:4.13.2'
4949
androidTestImplementation 'androidx.test.ext:junit:1.1.3'

app/src/main/java/com/xiaoyv/javaengine/CompileActivity.kt

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class CompileActivity : AppCompatActivity(), CoroutineScope by MainScope() {
5454
true
5555
}.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
5656

57+
5758
}
5859

5960
/**
@@ -103,30 +104,30 @@ class CompileActivity : AppCompatActivity(), CoroutineScope by MainScope() {
103104

104105
binding.printView.append("Run dex start...\n\n")
105106

106-
// JavaEngine.
107-
val programConsole = JavaEngine.javaProgram.run(dexFile, arrayOf("args"),
108-
chooseMainClassToRun = { classes, continuation ->
109-
val dialog = AlertDialog.Builder(this@CompileActivity)
110-
.setTitle("请选择一个主函数运行")
111-
.setItems(classes.toTypedArray()) { p0, p1 ->
112-
p0.dismiss()
113-
continuation.resume(classes[p1])
114-
}
115-
.setCancelable(false)
116-
.setNegativeButton("取消") { d, v ->
117-
d.dismiss()
118-
continuation.resumeWithException(Exception("取消操作"))
119-
}.create()
120-
121-
dialog.show()
122-
dialog.setCanceledOnTouchOutside(false)
123-
},
124-
printOut = {
125-
binding.printView.append(it)
126-
},
127-
printErr = {
128-
binding.printView.append(Html.fromHtml("<font color=\"#FF0000\">$it</font>"))
129-
})
107+
// JavaEngine.
108+
val programConsole = JavaEngine.javaProgram.run(dexFile, arrayOf("args"),
109+
chooseMainClassToRun = { classes, continuation ->
110+
val dialog = AlertDialog.Builder(this@CompileActivity)
111+
.setTitle("请选择一个主函数运行")
112+
.setItems(classes.toTypedArray()) { p0, p1 ->
113+
p0.dismiss()
114+
continuation.resume(classes[p1])
115+
}
116+
.setCancelable(false)
117+
.setNegativeButton("取消") { d, v ->
118+
d.dismiss()
119+
continuation.resumeWithException(Exception("取消操作"))
120+
}.create()
121+
122+
dialog.show()
123+
dialog.setCanceledOnTouchOutside(false)
124+
},
125+
printOut = {
126+
binding.printView.append(it)
127+
},
128+
printErr = {
129+
binding.printView.append(Html.fromHtml("<font color=\"#FF0000\">$it</font>"))
130+
})
130131

131132
binding.btSend.setOnClickListener {
132133
val input = binding.inputEdit.text.toString()

0 commit comments

Comments
 (0)