Skip to content

Commit 41cec3e

Browse files
committed
feat: Add reflection caching and improved performance
1 parent dc8e99d commit 41cec3e

File tree

7 files changed

+165
-33
lines changed

7 files changed

+165
-33
lines changed

app/src/main/java/com/wmods/wppenhacer/xposed/core/FeatureLoader.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ public static void start(@NonNull ClassLoader loader, @NonNull XSharedPreference
124124
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
125125
mApp = (Application) param.args[0];
126126

127+
ReflectionUtils.initCache(mApp);
128+
127129
// Inject Booloader Spoofer
128130
if (pref.getBoolean("bootloader_spoofer", false)) {
129131
HookBL.hook(loader, pref);

app/src/main/java/com/wmods/wppenhacer/xposed/core/WppCore.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
9595

9696
// ActionUser
9797
var actionUser = Unobfuscator.loadActionUser(loader);
98+
XposedBridge.log("ActionUser: " + actionUser.getName());
9899
XposedBridge.hookAllConstructors(actionUser, new XC_MethodHook() {
99100
@Override
100101
protected void afterHookedMethod(MethodHookParam param) throws Throwable {

app/src/main/java/com/wmods/wppenhacer/xposed/core/devkit/Unobfuscator.java

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -480,38 +480,42 @@ public synchronized static Method loadMediaQualityVideoMethod2(ClassLoader class
480480
}
481481

482482
public synchronized static HashMap<String, Field> loadMediaQualityVideoFields(ClassLoader classLoader) throws Exception {
483-
var method = loadMediaQualityVideoMethod2(classLoader);
484-
var methodString = method.getReturnType().getDeclaredMethod("toString");
485-
var methodData = dexkit.getMethodData(methodString);
486-
var usingFields = Objects.requireNonNull(methodData).getUsingFields();
487-
var usingStrings = Objects.requireNonNull(methodData).getUsingStrings();
488-
var result = new HashMap<String, Field>();
489-
for (int i = 0; i < usingStrings.size(); i++) {
490-
if (i == usingFields.size()) break;
491-
var field = usingFields.get(i).getField().getFieldInstance(classLoader);
492-
result.put(usingStrings.get(i), field);
493-
}
494-
return result;
483+
return UnobfuscatorCache.getInstance().getMapField(classLoader, () -> {
484+
var method = loadMediaQualityVideoMethod2(classLoader);
485+
var methodString = method.getReturnType().getDeclaredMethod("toString");
486+
var methodData = dexkit.getMethodData(methodString);
487+
var usingFields = Objects.requireNonNull(methodData).getUsingFields();
488+
var usingStrings = Objects.requireNonNull(methodData).getUsingStrings();
489+
var result = new HashMap<String, Field>();
490+
for (int i = 0; i < usingStrings.size(); i++) {
491+
if (i == usingFields.size()) break;
492+
var field = usingFields.get(i).getField().getFieldInstance(classLoader);
493+
result.put(usingStrings.get(i), field);
494+
}
495+
return result;
496+
});
495497
}
496498

497499
public synchronized static HashMap<String, Field> loadMediaQualityOriginalVideoFields(ClassLoader classLoader) throws Exception {
498-
var method = loadMediaQualityVideoMethod2(classLoader);
499-
Method methodString;
500-
try {
501-
methodString = method.getParameterTypes()[0].getDeclaredMethod("toString");
502-
} catch (Exception e) {
503-
return new HashMap<>();
504-
}
505-
var methodData = dexkit.getMethodData(methodString);
506-
var usingFields = Objects.requireNonNull(methodData).getUsingFields();
507-
var usingStrings = Objects.requireNonNull(methodData).getUsingStrings();
508-
var result = new HashMap<String, Field>();
509-
for (int i = 0; i < usingStrings.size(); i++) {
510-
if (i == usingFields.size()) break;
511-
var field = usingFields.get(i).getField().getFieldInstance(classLoader);
512-
result.put(usingStrings.get(i), field);
513-
}
514-
return result;
500+
return UnobfuscatorCache.getInstance().getMapField(classLoader, () -> {
501+
var method = loadMediaQualityVideoMethod2(classLoader);
502+
Method methodString;
503+
try {
504+
methodString = method.getParameterTypes()[0].getDeclaredMethod("toString");
505+
} catch (Exception e) {
506+
return new HashMap<>();
507+
}
508+
var methodData = dexkit.getMethodData(methodString);
509+
var usingFields = Objects.requireNonNull(methodData).getUsingFields();
510+
var usingStrings = Objects.requireNonNull(methodData).getUsingStrings();
511+
var result = new HashMap<String, Field>();
512+
for (int i = 0; i < usingStrings.size(); i++) {
513+
if (i == usingFields.size()) break;
514+
var field = usingFields.get(i).getField().getFieldInstance(classLoader);
515+
result.put(usingStrings.get(i), field);
516+
}
517+
return result;
518+
});
515519
}
516520

517521
// TODO: Classes and methods to ShareLimit

app/src/main/java/com/wmods/wppenhacer/xposed/core/devkit/UnobfuscatorCache.java

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,17 @@
1616
import com.wmods.wppenhacer.xposed.utils.ResId;
1717
import com.wmods.wppenhacer.xposed.utils.Utils;
1818

19+
import org.json.JSONException;
20+
import org.json.JSONObject;
21+
1922
import java.io.File;
2023
import java.lang.reflect.Constructor;
2124
import java.lang.reflect.Field;
2225
import java.lang.reflect.Method;
2326
import java.util.ArrayList;
2427
import java.util.Arrays;
2528
import java.util.HashMap;
29+
import java.util.Iterator;
2630
import java.util.Locale;
2731
import java.util.Map;
2832
import java.util.concurrent.CountDownLatch;
@@ -300,6 +304,69 @@ public Class<?>[] getClasses(ClassLoader loader, FunctionCall<Class<?>[]> functi
300304
return classes.toArray(new Class<?>[0]);
301305
}
302306

307+
public HashMap<String, Field> getMapField(ClassLoader loader, FunctionCall<HashMap<String, Field>> functionCall) throws Exception {
308+
var key = getKeyName();
309+
String value = sPrefsCacheHooks.getString(key, null);
310+
if (value == null) {
311+
var result = functionCall.call();
312+
if (result == null) throw new Exception("HashMap is null: " + key);
313+
saveHashMap(key, result);
314+
return result;
315+
}
316+
return loadHashMap(loader, key);
317+
}
318+
319+
private void saveHashMap(String key, HashMap<String, Field> map) {
320+
// Cria um novo JSONObject para armazenar os pares
321+
JSONObject jsonObject = new JSONObject();
322+
for (Map.Entry<String, Field> entry : map.entrySet()) {
323+
Field field = entry.getValue();
324+
String value = field.getDeclaringClass().getName() + ":" + field.getName();
325+
try {
326+
jsonObject.put(entry.getKey(), value);
327+
} catch (JSONException e) {
328+
e.printStackTrace();
329+
}
330+
}
331+
sPrefsCacheHooks.edit().putString(key, jsonObject.toString()).apply();
332+
}
333+
334+
private HashMap<String, Field> loadHashMap(ClassLoader loader, String key) {
335+
HashMap<String, Field> map = new HashMap<>();
336+
String jsonString = sPrefsCacheHooks.getString(key, null);
337+
if (jsonString == null) return map;
338+
339+
try {
340+
JSONObject jsonObject = new JSONObject(jsonString);
341+
Iterator<String> keys = jsonObject.keys();
342+
343+
while (keys.hasNext()) {
344+
String mapKey = keys.next();
345+
String value = jsonObject.getString(mapKey);
346+
347+
// Quebra "com.package.Classe:campo"
348+
String[] parts = value.split(":");
349+
if (parts.length == 2) {
350+
String className = parts[0];
351+
String fieldName = parts[1];
352+
try {
353+
Class<?> clazz = loader.loadClass(className);
354+
Field field = clazz.getDeclaredField(fieldName);
355+
field.setAccessible(true);
356+
map.put(mapKey, field);
357+
} catch (Exception e) {
358+
e.printStackTrace(); // ignora campos inválidos
359+
}
360+
}
361+
}
362+
} catch (JSONException e) {
363+
e.printStackTrace();
364+
}
365+
366+
return map;
367+
}
368+
369+
303370
@SuppressWarnings("ApplySharedPref")
304371
public void saveField(String key, Field field) {
305372
String value = field.getDeclaringClass().getName() + ":" + field.getName();
@@ -385,6 +452,7 @@ private void saveConstructor(String key, Constructor constructor) {
385452
sPrefsCacheHooks.edit().putString(key, value).commit();
386453
}
387454

455+
388456
public interface FunctionCall<T> {
389457
T call() throws Exception;
390458
}

app/src/main/java/com/wmods/wppenhacer/xposed/features/customization/SeparateGroup.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -299,8 +299,6 @@ private void hookTabList(@NonNull Class<?> home) throws Exception {
299299
if (fieldTabsList == null) {
300300
throw new NullPointerException("fieldTabList is NULL!");
301301
}
302-
fieldTabsList.setAccessible(true);
303-
304302
XposedBridge.hookMethod(onCreateTabList, new XC_MethodHook() {
305303
@Override
306304
@SuppressWarnings("unchecked")

app/src/main/java/com/wmods/wppenhacer/xposed/utils/ReflectionUtils.java

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.wmods.wppenhacer.xposed.utils;
22

3+
import android.content.Context;
4+
import android.content.SharedPreferences;
35
import android.util.Pair;
46

57
import androidx.annotation.NonNull;
@@ -19,6 +21,19 @@
1921
@SuppressWarnings("unused")
2022
public class ReflectionUtils {
2123

24+
private static SharedPreferences cachePrefs;
25+
26+
/**
27+
* Initialize the SharedPreferences for caching reflection results
28+
*
29+
* @param context Application context
30+
*/
31+
public static void initCache(Context context) {
32+
if (cachePrefs == null) {
33+
cachePrefs = context.getSharedPreferences("UnobfuscatorCache", Context.MODE_PRIVATE);
34+
}
35+
}
36+
2237
public static Map<String, Class<?>> primitiveClasses = Map.of(
2338
"byte", Byte.TYPE,
2439
"short", Short.TYPE,
@@ -131,11 +146,53 @@ public static List<Field> getFieldsByType(Class<?> cls, Class<?> type) {
131146
}
132147

133148
public static Field getFieldByExtendType(Class<?> cls, Class<?> type) {
134-
return Arrays.stream(cls.getFields()).filter(f -> type.isAssignableFrom(f.getType())).findFirst().orElse(null);
149+
if (cachePrefs == null) {
150+
return Arrays.stream(cls.getFields()).filter(f -> type.isAssignableFrom(f.getType())).findFirst().orElse(null);
151+
}
152+
153+
String cacheKey = "field_cache_" + cls.getName() + "_" + type.getName();
154+
155+
String cachedFieldName = cachePrefs.getString(cacheKey, null);
156+
if (cachedFieldName != null) {
157+
try {
158+
return cls.getField(cachedFieldName);
159+
} catch (NoSuchFieldException e) {
160+
cachePrefs.edit().remove(cacheKey).commit();
161+
}
162+
}
163+
164+
Field field = Arrays.stream(cls.getFields()).filter(f -> type.isAssignableFrom(f.getType())).findFirst().orElse(null);
165+
166+
if (field != null) {
167+
cachePrefs.edit().putString(cacheKey, field.getName()).commit();
168+
}
169+
170+
return field;
135171
}
136172

137173
public static Field getFieldByType(Class<?> cls, Class<?> type) {
138-
return Arrays.stream(cls.getFields()).filter(f -> type == f.getType()).findFirst().orElse(null);
174+
if (cachePrefs == null) {
175+
return Arrays.stream(cls.getFields()).filter(f -> type == f.getType()).findFirst().orElse(null);
176+
}
177+
178+
String cacheKey = "field_cache_direct_" + cls.getName() + "_" + type.getName();
179+
180+
String cachedFieldName = cachePrefs.getString(cacheKey, null);
181+
if (cachedFieldName != null) {
182+
try {
183+
return cls.getField(cachedFieldName);
184+
} catch (NoSuchFieldException e) {
185+
cachePrefs.edit().remove(cacheKey).apply();
186+
}
187+
}
188+
189+
Field field = Arrays.stream(cls.getFields()).filter(f -> type == f.getType()).findFirst().orElse(null);
190+
191+
if (field != null) {
192+
cachePrefs.edit().putString(cacheKey, field.getName()).apply();
193+
}
194+
195+
return field;
139196
}
140197

141198
public static Object callMethod(Method method, Object instance, Object... args) {

app/src/main/res/values/arrays.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,13 +124,15 @@
124124
<item>2.25.27.xx</item>
125125
<item>2.25.28.xx</item>
126126
<item>2.25.29.xx</item>
127+
<item>2.25.30.xx</item>
127128
</string-array>
128129
<string-array name="supported_versions_business">
129130
<item>2.25.25.xx</item>
130131
<item>2.25.26.xx</item>
131132
<item>2.25.27.xx</item>
132133
<item>2.25.28.xx</item>
133134
<item>2.25.29.xx</item>
135+
<item>2.25.30.xx</item>
134136
</string-array>
135137
<string-array name="image_picker">
136138
<item>image/*</item>

0 commit comments

Comments
 (0)