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
- * APK 中的 DEX 文件获取类的 Smali 代码
2146
+ * 获取或创建 APK DEX 缓存
2123
2147
  */
2124
- public JSObject getClassSmaliFromApk(String apkPath, String dexPath, String className) throws Exception {
2125
- JSObject result = new JSObject();
2126
- StringBuilder smali = new StringBuilder();
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
- Log.d(TAG, "getClassSmaliFromApk: apkPath=" + apkPath + ", dexPath=" + dexPath + ", className=" + className);
2153
+ ApkDexCache cache = apkDexCaches.get(cacheKey);
2129
2154
 
2130
- if (className == null || className.isEmpty()) {
2131
- result.put("smali", "# 未指定类名");
2132
- return result;
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
- String targetType = convertClassNameToType(className);
2136
-
2137
- java.util.zip.ZipFile zipFile = null;
2138
- java.io.InputStream dexInputStream = null;
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
- result.put("smali", "# DEX 文件未找到: " + dexPath);
2158
- return result;
2182
+ throw new Exception("DEX 文件未找到: " + dexPath);
2159
2183
  }
2160
2184
 
2161
- dexInputStream = zipFile.getInputStream(dexEntry);
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[8192];
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
- if (classDef.getType().equals(targetType)) {
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
- } finally {
2201
- if (dexInputStream != null) {
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(30, 100);
2310
+ reportProgress(20, 100);
2254
2311
  Log.d(TAG, "Smali compiled successfully to ClassDef");
2255
2312
 
2256
- // 读取原 APK 中的 DEX 文件
2313
+ // 使用缓存获取 ClassDef(核心优化)
2257
2314
  reportTitle("合并 DEX");
2258
- reportMessage("读取原 DEX...");
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
- java.util.List<ClassDef> mergedClasses = new java.util.ArrayList<>();
2318
+ ApkDexCache cache = getOrCreateDexCache(apkPath, dexPath);
2289
2319
 
2290
- // 添加原 DEX 中除目标类外的所有类
2291
- for (ClassDef classDef : originalDex.getClasses()) {
2292
- if (!classDef.getType().equals(targetType)) {
2293
- mergedClasses.add(classDef);
2294
- }
2295
- }
2320
+ reportProgress(30, 100);
2321
+ reportMessage("更新缓存...");
2296
2322
 
2297
- // 添加新编译的类
2298
- mergedClasses.add(newClassDef);
2323
+ // 更新缓存中的 ClassDef
2324
+ cache.classDefMap.put(targetType, newClassDef);
2299
2325
 
2300
- Log.d(TAG, "Merged " + mergedClasses.size() + " classes");
2326
+ Log.d(TAG, "Using cached " + cache.classDefMap.size() + " classes");
2301
2327
 
2302
- // 创建新的 DEX 文件(使用内存)
2303
- reportMessage("写入 DEX (" + mergedClasses.size() + " 个类)...");
2304
- reportProgress(50, 100);
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 = mergedClasses.size();
2333
+ int totalClasses = cache.classDefMap.size();
2308
2334
  int currentClass = 0;
2309
- for (ClassDef classDef : mergedClasses) {
2335
+ for (ClassDef classDef : cache.classDefMap.values()) {
2310
2336
  dexPool.internClass(classDef);
2311
2337
  currentClass++;
2312
- if (currentClass % 100 == 0 || currentClass == totalClasses) {
2313
- reportProgress(50 + (currentClass * 30 / totalClasses), 100);
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "capacitor-dex-editor",
3
- "version": "0.0.55",
3
+ "version": "0.0.56",
4
4
  "description": "Capacitor-plugin-for-editing-DEX-files-in-APK",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",