-
Notifications
You must be signed in to change notification settings - Fork 21
/
JavaProgram.kt
117 lines (104 loc) · 4.22 KB
/
JavaProgram.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package com.xiaoyv.java.compiler.tools.exec
import android.text.SpannableStringBuilder
import android.text.style.ForegroundColorSpan
import com.xiaoyv.java.compiler.JavaEngine
import com.xiaoyv.java.compiler.JavaEngineSetting
import com.xiaoyv.java.compiler.exception.CompileException
import dalvik.system.DexClassLoader
import kotlinx.coroutines.*
import java.io.File
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
/**
* JavaProgram
*
* @author why
* @since 2022/3/8
*/
class JavaProgram {
private val defaultChooseMainClassToRun: (List<String>, CancellableContinuation<String>) -> Unit =
{ mainFunClasses, continuation ->
if (mainFunClasses.isEmpty()) {
continuation.resumeWithException(CompileException("未找到包含 main(String[] args) 方法的可执行类"))
} else {
continuation.resume(mainFunClasses.first())
}
}
/**
* 运行 Dex 文件
*
* ### [chooseMainClassToRun]
* - 第一个回调参数为查询到的包含 main(String[] args) 方法的所有类全路径
* - 第二个回调参数为 [CancellableContinuation] ,将选取的类名通过 `continuation.resume()` 方法回调。
*
* > 若长时间未回调选择结果,协程则会一直挂起,占用资源。请及时回调或者取消 `continuation.cancel()`
*
* @param dexFile 文件路径
* @param args 文件参数
* @param chooseMainClassToRun 选取一个主类进行运行
* @return [JavaProgramConsole] 可以关闭该程序相关句柄
*/
suspend fun run(
dexFile: String,
args: Array<String> = emptyArray(),
chooseMainClassToRun: (List<String>, CancellableContinuation<String>) -> Unit = defaultChooseMainClassToRun,
printOut: (CharSequence) -> Unit = { },
printErr: (CharSequence) -> Unit = { },
) = run(File(dexFile), args, chooseMainClassToRun, printOut, printErr)
suspend fun run(
dexFile: File,
args: Array<String> = emptyArray(),
chooseMainClassToRun: (List<String>, CancellableContinuation<String>) -> Unit = defaultChooseMainClassToRun,
printOut: (CharSequence) -> Unit = { },
printErr: (CharSequence) -> Unit = { },
) = withContext(Dispatchers.IO) {
JavaEngine.resetProgram()
// 包含的全部 Main 方法
val mainFunctionList = JavaProgramHelper.queryMainFunctionList(dexFile)
// 选者的 Fun
val mainClass = suspendCancellableCoroutine<String> {
launch(Dispatchers.Main) {
chooseMainClassToRun.invoke(mainFunctionList, it)
}
}
val optimizedDirectory = JavaEngineSetting.defaultCacheDir
val dexClassLoader = DexClassLoader(
dexFile.absolutePath, optimizedDirectory, null, ClassLoader.getSystemClassLoader()
)
// 加载 Class
val clazz = dexClassLoader.loadClass(mainClass)
// 获取 main 方法
val method = clazz.getDeclaredMethod("main", Array<String>::class.java)
JavaProgramConsole().apply {
logNormalListener = {
runCatching {
// 输出样式
val colorSpan = ForegroundColorSpan(JavaEngine.compilerSetting.normalLogColor)
printOut.invoke(SpannableStringBuilder().apply {
append(it)
setSpan(colorSpan, 0, it.length, 0)
})
}
}
logErrorListener = {
runCatching {
// 错误样式
val colorSpan = ForegroundColorSpan(JavaEngine.compilerSetting.errorLogColor)
printErr.invoke(SpannableStringBuilder().apply {
append(it)
setSpan(colorSpan, 0, it.length, 0)
})
}
}
// 开启日志代理
interceptSystemPrint()
JavaEngine.lastProgram = this
launch(Dispatchers.IO) {
// 调用静态方法可以直接传 null
delay(100)
method.invoke(null, args)
delay(100)
}
}
}
}