代码之家  ›  专栏  ›  技术社区  ›  Mickey Tin

如何使用本机函数保存类

  •  0
  • Mickey Tin  · 技术社区  · 6 年前

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

    -keepclasseswithmembernames class * { native <methods>; }
    

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

    我想知道是否可以将所有内容都保存在这样的类中,而不必显式地指定每个类

    谢谢你

    2 回复  |  直到 6 年前
        1
  •  2
  •   user1643723    6 年前

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

    使用以下规则:

    -keepclasseswithmembers class com.your.packages.** {
        native <methods>;
    }
    -keepclassmembers class com.your.packages.** {
        native <methods>;
    }
    

    注意,使用Proguard“完全跳过”类总是一个坏主意,因为它还可能间接保留一些类,这些类是从保存类的代码中使用的。相反,我建议使用以下模式:

    -keepclasseswithmembers,allowshrinking,allowoptimization class com.your.packages.** {
        native <methods>;
    }
    -keepclassmembers class com.your.packages.** {
        native <methods>;
    }
    

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

    您可以做得更好:如果您的本地方法是通过名称来解析的(例如,它们被称为 Java_com_your_packages_methodName ),而你不使用 RegisterNatives 要显式地注册它们,可以通过删除第二条规则(只留下

    -keepclasseswithmembers,allowshrinking,allowoptimization class com.your.packages.** {
        native <methods>;
    }
    

    如果您想从JNI访问一些类成员(例如,您有一些静态回调方法要从本机代码调用),那么应该保留它们 明确地 :使用专用注释为每个此类成员添加注释,并使用基于注释的规则保留它们:

    -keepclassmembers,allowoptimization,includedescriptorclasses class com.your.packages.** {
        @android.support.annotation.Keep *;
    }
    

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


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

    @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 字段,并将该字段传递给本机方法。然后在本机方法中 长的 指向指针:

    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;
    
        ...
    }
    

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

        2
  •  0
  •   Shmuel Developer_vaibhav    6 年前

    使用-keep而不是-keepclasseswithmembernames

    -keep class * { native <methods>; }
    

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

    -keep禁用所有ProGuard的优点。不收缩,不混淆;不适用于类,不适用于成员。