capacitor-dex-editor 0.0.55 → 0.0.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -98,6 +98,30 @@ public class DexManager {
|
|
|
98
98
|
|
|
99
99
|
// 存储多 DEX 会话(MCP 工作流)
|
|
100
100
|
private final Map<String, MultiDexSession> multiDexSessions = new HashMap<>();
|
|
101
|
+
|
|
102
|
+
// APK DEX 缓存 - 用于加速编译(key: apkPath + ":" + dexPath)
|
|
103
|
+
private final Map<String, ApkDexCache> apkDexCaches = new HashMap<>();
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* APK DEX 缓存 - 缓存从 APK 读取的 DEX 数据和 ClassDef
|
|
107
|
+
*/
|
|
108
|
+
private static class ApkDexCache {
|
|
109
|
+
String apkPath;
|
|
110
|
+
String dexPath;
|
|
111
|
+
long lastModified;
|
|
112
|
+
Map<String, ClassDef> classDefMap; // type -> ClassDef
|
|
113
|
+
int dexVersion;
|
|
114
|
+
|
|
115
|
+
ApkDexCache(String apkPath, String dexPath) {
|
|
116
|
+
this.apkPath = apkPath;
|
|
117
|
+
this.dexPath = dexPath;
|
|
118
|
+
this.classDefMap = new HashMap<>();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
String getCacheKey() {
|
|
122
|
+
return apkPath + ":" + dexPath;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
101
125
|
|
|
102
126
|
/**
|
|
103
127
|
* DEX 会话 - 存储加载的 DEX 文件及其修改状态
|
|
@@ -2119,27 +2143,28 @@ public class DexManager {
|
|
|
2119
2143
|
}
|
|
2120
2144
|
|
|
2121
2145
|
/**
|
|
2122
|
-
*
|
|
2146
|
+
* 获取或创建 APK DEX 缓存
|
|
2123
2147
|
*/
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2148
|
+
private ApkDexCache getOrCreateDexCache(String apkPath, String dexPath) throws Exception {
|
|
2149
|
+
String cacheKey = apkPath + ":" + dexPath;
|
|
2150
|
+
java.io.File apkFile = new java.io.File(apkPath);
|
|
2151
|
+
long currentModified = apkFile.lastModified();
|
|
2127
2152
|
|
|
2128
|
-
|
|
2153
|
+
ApkDexCache cache = apkDexCaches.get(cacheKey);
|
|
2129
2154
|
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2155
|
+
// 检查缓存是否有效
|
|
2156
|
+
if (cache != null && cache.lastModified == currentModified && !cache.classDefMap.isEmpty()) {
|
|
2157
|
+
Log.d(TAG, "Using DEX cache for: " + cacheKey + " (" + cache.classDefMap.size() + " classes)");
|
|
2158
|
+
return cache;
|
|
2133
2159
|
}
|
|
2134
2160
|
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2161
|
+
// 需要重新加载
|
|
2162
|
+
Log.d(TAG, "Loading DEX into cache: " + cacheKey);
|
|
2163
|
+
cache = new ApkDexCache(apkPath, dexPath);
|
|
2164
|
+
cache.lastModified = currentModified;
|
|
2139
2165
|
|
|
2166
|
+
java.util.zip.ZipFile zipFile = new java.util.zip.ZipFile(apkPath);
|
|
2140
2167
|
try {
|
|
2141
|
-
zipFile = new java.util.zip.ZipFile(apkPath);
|
|
2142
|
-
|
|
2143
2168
|
// 尝试多种可能的 dexPath 格式
|
|
2144
2169
|
java.util.zip.ZipEntry dexEntry = zipFile.getEntry(dexPath);
|
|
2145
2170
|
if (dexEntry == null) {
|
|
@@ -2154,33 +2179,70 @@ public class DexManager {
|
|
|
2154
2179
|
}
|
|
2155
2180
|
|
|
2156
2181
|
if (dexEntry == null) {
|
|
2157
|
-
|
|
2158
|
-
return result;
|
|
2182
|
+
throw new Exception("DEX 文件未找到: " + dexPath);
|
|
2159
2183
|
}
|
|
2160
2184
|
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
// 读取 DEX 文件到内存
|
|
2185
|
+
// 读取 DEX 文件
|
|
2186
|
+
java.io.InputStream dexInputStream = zipFile.getInputStream(dexEntry);
|
|
2164
2187
|
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
|
|
2165
|
-
byte[] buffer = new byte[
|
|
2188
|
+
byte[] buffer = new byte[16384];
|
|
2166
2189
|
int len;
|
|
2167
2190
|
while ((len = dexInputStream.read(buffer)) != -1) {
|
|
2168
2191
|
baos.write(buffer, 0, len);
|
|
2169
2192
|
}
|
|
2193
|
+
dexInputStream.close();
|
|
2170
2194
|
byte[] dexBytes = baos.toByteArray();
|
|
2171
2195
|
|
|
2172
|
-
// 解析 DEX
|
|
2196
|
+
// 解析 DEX 文件并缓存所有 ClassDef
|
|
2173
2197
|
DexBackedDexFile dexFile = new DexBackedDexFile(Opcodes.getDefault(), dexBytes);
|
|
2198
|
+
cache.dexVersion = 35; // 默认 DEX 版本
|
|
2174
2199
|
|
|
2175
|
-
// 查找目标类
|
|
2176
|
-
ClassDef targetClass = null;
|
|
2177
2200
|
for (ClassDef classDef : dexFile.getClasses()) {
|
|
2178
|
-
|
|
2179
|
-
targetClass = classDef;
|
|
2180
|
-
break;
|
|
2181
|
-
}
|
|
2201
|
+
cache.classDefMap.put(classDef.getType(), classDef);
|
|
2182
2202
|
}
|
|
2183
2203
|
|
|
2204
|
+
Log.d(TAG, "Cached " + cache.classDefMap.size() + " classes from " + dexPath);
|
|
2205
|
+
} finally {
|
|
2206
|
+
zipFile.close();
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2209
|
+
apkDexCaches.put(cacheKey, cache);
|
|
2210
|
+
return cache;
|
|
2211
|
+
}
|
|
2212
|
+
|
|
2213
|
+
/**
|
|
2214
|
+
* 清除指定 APK 的 DEX 缓存
|
|
2215
|
+
*/
|
|
2216
|
+
public void clearDexCache(String apkPath) {
|
|
2217
|
+
java.util.Iterator<String> it = apkDexCaches.keySet().iterator();
|
|
2218
|
+
while (it.hasNext()) {
|
|
2219
|
+
if (it.next().startsWith(apkPath + ":")) {
|
|
2220
|
+
it.remove();
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
Log.d(TAG, "Cleared DEX cache for: " + apkPath);
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2226
|
+
/**
|
|
2227
|
+
* 从 APK 中的 DEX 文件获取类的 Smali 代码(使用缓存)
|
|
2228
|
+
*/
|
|
2229
|
+
public JSObject getClassSmaliFromApk(String apkPath, String dexPath, String className) throws Exception {
|
|
2230
|
+
JSObject result = new JSObject();
|
|
2231
|
+
|
|
2232
|
+
Log.d(TAG, "getClassSmaliFromApk: apkPath=" + apkPath + ", dexPath=" + dexPath + ", className=" + className);
|
|
2233
|
+
|
|
2234
|
+
if (className == null || className.isEmpty()) {
|
|
2235
|
+
result.put("smali", "# 未指定类名");
|
|
2236
|
+
return result;
|
|
2237
|
+
}
|
|
2238
|
+
|
|
2239
|
+
String targetType = convertClassNameToType(className);
|
|
2240
|
+
|
|
2241
|
+
try {
|
|
2242
|
+
// 使用缓存获取 ClassDef
|
|
2243
|
+
ApkDexCache cache = getOrCreateDexCache(apkPath, dexPath);
|
|
2244
|
+
ClassDef targetClass = cache.classDefMap.get(targetType);
|
|
2245
|
+
|
|
2184
2246
|
if (targetClass == null) {
|
|
2185
2247
|
result.put("smali", "# 类未找到: " + className + "\n# 目标类型: " + targetType);
|
|
2186
2248
|
return result;
|
|
@@ -2197,13 +2259,8 @@ public class DexManager {
|
|
|
2197
2259
|
|
|
2198
2260
|
result.put("smali", stringWriter.toString());
|
|
2199
2261
|
|
|
2200
|
-
}
|
|
2201
|
-
|
|
2202
|
-
try { dexInputStream.close(); } catch (Exception ignored) {}
|
|
2203
|
-
}
|
|
2204
|
-
if (zipFile != null) {
|
|
2205
|
-
try { zipFile.close(); } catch (Exception ignored) {}
|
|
2206
|
-
}
|
|
2262
|
+
} catch (Exception e) {
|
|
2263
|
+
result.put("smali", "# 加载失败: " + e.getMessage());
|
|
2207
2264
|
}
|
|
2208
2265
|
|
|
2209
2266
|
return result;
|
|
@@ -2250,72 +2307,42 @@ public class DexManager {
|
|
|
2250
2307
|
return result;
|
|
2251
2308
|
}
|
|
2252
2309
|
|
|
2253
|
-
reportProgress(
|
|
2310
|
+
reportProgress(20, 100);
|
|
2254
2311
|
Log.d(TAG, "Smali compiled successfully to ClassDef");
|
|
2255
2312
|
|
|
2256
|
-
//
|
|
2313
|
+
// 使用缓存获取 ClassDef(核心优化)
|
|
2257
2314
|
reportTitle("合并 DEX");
|
|
2258
|
-
reportMessage("
|
|
2259
|
-
|
|
2260
|
-
java.util.zip.ZipFile zipFile = new java.util.zip.ZipFile(apkPath);
|
|
2261
|
-
java.util.zip.ZipEntry dexEntry = zipFile.getEntry(dexPath);
|
|
2262
|
-
|
|
2263
|
-
if (dexEntry == null) {
|
|
2264
|
-
result.put("success", false);
|
|
2265
|
-
result.put("error", "DEX 文件未找到: " + dexPath);
|
|
2266
|
-
zipFile.close();
|
|
2267
|
-
return result;
|
|
2268
|
-
}
|
|
2269
|
-
|
|
2270
|
-
// 读取原 DEX
|
|
2271
|
-
java.io.InputStream dexInputStream = zipFile.getInputStream(dexEntry);
|
|
2272
|
-
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
|
|
2273
|
-
byte[] buffer = new byte[16384];
|
|
2274
|
-
int len;
|
|
2275
|
-
while ((len = dexInputStream.read(buffer)) != -1) {
|
|
2276
|
-
baos.write(buffer, 0, len);
|
|
2277
|
-
}
|
|
2278
|
-
byte[] originalDexBytes = baos.toByteArray();
|
|
2279
|
-
dexInputStream.close();
|
|
2280
|
-
|
|
2281
|
-
// 解析原 DEX
|
|
2282
|
-
DexBackedDexFile originalDex = new DexBackedDexFile(Opcodes.getDefault(), originalDexBytes);
|
|
2283
|
-
|
|
2284
|
-
reportMessage("收集类定义...");
|
|
2285
|
-
reportProgress(40, 100);
|
|
2315
|
+
reportMessage("获取缓存...");
|
|
2286
2316
|
|
|
2287
2317
|
String targetType = "L" + className.replace(".", "/") + ";";
|
|
2288
|
-
|
|
2318
|
+
ApkDexCache cache = getOrCreateDexCache(apkPath, dexPath);
|
|
2289
2319
|
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
if (!classDef.getType().equals(targetType)) {
|
|
2293
|
-
mergedClasses.add(classDef);
|
|
2294
|
-
}
|
|
2295
|
-
}
|
|
2320
|
+
reportProgress(30, 100);
|
|
2321
|
+
reportMessage("更新缓存...");
|
|
2296
2322
|
|
|
2297
|
-
//
|
|
2298
|
-
|
|
2323
|
+
// 更新缓存中的 ClassDef
|
|
2324
|
+
cache.classDefMap.put(targetType, newClassDef);
|
|
2299
2325
|
|
|
2300
|
-
Log.d(TAG, "
|
|
2326
|
+
Log.d(TAG, "Using cached " + cache.classDefMap.size() + " classes");
|
|
2301
2327
|
|
|
2302
|
-
// 创建新的 DEX
|
|
2303
|
-
reportMessage("写入 DEX (" +
|
|
2304
|
-
reportProgress(
|
|
2328
|
+
// 创建新的 DEX 文件(使用缓存的 ClassDef)
|
|
2329
|
+
reportMessage("写入 DEX (" + cache.classDefMap.size() + " 个类)...");
|
|
2330
|
+
reportProgress(40, 100);
|
|
2305
2331
|
|
|
2306
2332
|
DexPool dexPool = new DexPool(Opcodes.getDefault());
|
|
2307
|
-
int totalClasses =
|
|
2333
|
+
int totalClasses = cache.classDefMap.size();
|
|
2308
2334
|
int currentClass = 0;
|
|
2309
|
-
for (ClassDef classDef :
|
|
2335
|
+
for (ClassDef classDef : cache.classDefMap.values()) {
|
|
2310
2336
|
dexPool.internClass(classDef);
|
|
2311
2337
|
currentClass++;
|
|
2312
|
-
if (currentClass %
|
|
2313
|
-
reportProgress(
|
|
2338
|
+
if (currentClass % 200 == 0 || currentClass == totalClasses) {
|
|
2339
|
+
reportProgress(40 + (currentClass * 40 / totalClasses), 100);
|
|
2314
2340
|
}
|
|
2315
2341
|
}
|
|
2316
2342
|
|
|
2317
2343
|
// 使用内存写入 DEX(避免临时文件)
|
|
2318
2344
|
reportProgress(80, 100);
|
|
2345
|
+
reportMessage("生成 DEX...");
|
|
2319
2346
|
com.android.tools.smali.dexlib2.writer.io.MemoryDataStore memoryStore =
|
|
2320
2347
|
new com.android.tools.smali.dexlib2.writer.io.MemoryDataStore();
|
|
2321
2348
|
dexPool.writeTo(memoryStore);
|
|
@@ -2323,8 +2350,6 @@ public class DexManager {
|
|
|
2323
2350
|
|
|
2324
2351
|
Log.d(TAG, "Merged DEX size: " + newDexBytes.length + " bytes");
|
|
2325
2352
|
|
|
2326
|
-
zipFile.close();
|
|
2327
|
-
|
|
2328
2353
|
// MT 风格:直接替换 APK 内的 DEX
|
|
2329
2354
|
reportTitle("更新 APK");
|
|
2330
2355
|
reportMessage("正在替换 DEX...");
|
|
@@ -2389,6 +2414,9 @@ public class DexManager {
|
|
|
2389
2414
|
Log.d(TAG, "APK updated successfully: " + apkPath);
|
|
2390
2415
|
reportProgress(100, 100);
|
|
2391
2416
|
|
|
2417
|
+
// 更新缓存的 lastModified 以匹配新的 APK
|
|
2418
|
+
cache.lastModified = new java.io.File(apkPath).lastModified();
|
|
2419
|
+
|
|
2392
2420
|
result.put("success", true);
|
|
2393
2421
|
result.put("message", "Smali 编译成功!APK 已更新");
|
|
2394
2422
|
result.put("apkPath", apkPath);
|