如何使用本机函数保留类



如果其中有本机函数,是否可以告诉ProGuard完全跳过该类?

-keepclasseswithmembernames class * { native <methods>; }

上面对我不起作用,因为它保留了类名和本机函数名称,但混淆了其他成员

我想知道是否可以在不明确指定每个类的情况下将所有内容保留在此类类中

谢谢

如果有本机函数,是否可以告诉 ProGuard 完全跳过该类

使用以下规则:

-keepclasseswithmembers class com.your.packages.** { 本地<方法>; } -keepclassmembers class com.your.packages.** { 本地<方法>; }

请注意,使用 Proguard "完全跳过"类总是一个坏主意,因为它也可能间接保留一些类,这些类是从您保留的类的代码中使用的。相反,我推荐以下模式:

-keepclasseswithmembers,allowshrinking,allowoptimizationclass com.your.packages.** { 本地<方法>; } -keepclassmembers class com.your.packages.** { 本地<方法>; }

它将允许收缩和优化非本机方法的代码,存在于同一个类中。

你可以做得更好:如果你的本机方法是按名称解析的(例如,它们被称为类似Java_com_your_packages_methodName(,并且您不使用RegisterNatives来显式注册它们,您可以通过删除第二条规则来缩小未使用的本机方法,这将只留下

-keepclasseswithmembers,allowshrinking,allowoptimization class com.your.packages.** { 本地<方法>; }

如果您希望从 JNI 访问某些类成员(例如,您有一些从本机代码调用的静态回调方法(,您应该显式保留它们:使用专门的注释注释每个这样的成员,并使用基于注释的规则来保留它们:

-keepclassmembers,allowoptimization,includeescriptorclasses class com.your.packages.** { @android.支持.注释.保持 *; }

您可以使用自己的注释来代替 Android 支持库中的注释 — 事实上,最好使用自己的注释,以避免来自 Android Gradle 插件或其他库的现有消费者规则的干扰。


一般来说,我建议你尽可能减少JNI和Java代码之间的摩擦。如果您有多个相关的 Java 方法(从 JNI 调用(,请尝试将它们放在同一个方法中:

@Keep
public static void callback(int action, String arg) {
switch (action) {
...
}
}

从 Java 代码中抛出异常(无论如何,您都将反射性地调用它们的构造函数,因此不妨调用静态帮助程序方法(:

@Keep
public static void throwException(int type, String message) {
switch (type) {
case 0:
throw new BadThingsHappenedException(message);
case 1:
throw new AllHopeIsLostError();
...
}
}

如果你有一个必须传递给 JNI 的类,请尝试传递单个字段而不是该类:

public final class DataClass {
int intField;
String stringField;
public void doSomeNativeOperation() {
JNI.doSomeNativeOperation(this);
}
}
public final class JNI {
public static void doSomeNativeOperation(DataClass arg) {
doSomeNativeOperation0(arg.intField, arg.stringField);
}
private static native void doSomeNativeOperation0(int intField, String stringField);
}

如果您有一个本机对等类(与本机内存中的某个结构紧密相连的类(,则可以在long字段中保留指向该类中本机结构的指针,并将该字段传递给本机方法。然后在本机方法中将该long转换为指针:

public final class Peer {
long pointer;
// constructor is invoked from JNI
@Keep
protected Peer(long pointer) {
this.pointer = pointer;
}
public void doSomeNativeOperation() {
JNI.doSomeNativeOperation(this);
}
}
public final class JNI {
public static void doSomeNativeOperation(Peer peer) {
doSomeNativeOperation0(peer.pointer);
}
private static native void doSomeNativeOperation0(long pointer);
}

在本机代码中:

void JNIEXPORT Java_com_your_packages_methodName(JNIEnv* env, jobject type, jlong ptr) {
struct my_struct peer = (struct my_struc*) (intptr_t) ptr;
...
}

这些简单的规则将允许您使用 JNI 完全混淆任何大型应用程序,除了包含所有native方法的单个小类。

我建议你更进一步,推动重新打包类,由本机回调引用。

而不是

@Keep
public static void callback(YourCustomType arg) {
...
}

您可以通过将其替换为Object来省略参数类型:

@Keep
public static void callback(Object arg) {
// this cast won't make much difference in performance, but it makes huge
// difference for Proguard!
YourCustomType instance = (YourCustomType) arg;
...
}

这将允许您混淆和重新打包甚至类型的回调参数。

使用 -keep 而不是 -keepclasseswithmembernames

-keep class * { native <methods>; }

更多信息: https://jebware.com/blog/?p=418

-保持禁用了ProGuard的所有优点。 不收缩,不混淆;不是为了类,不是为了成员。

最新更新