【问题标题】:Keeping java methods called from Android JNI保留从 Android JNI 调用的 java 方法
【发布时间】:2014-07-23 12:10:50
【问题描述】:

我正在尝试通过 Proguard 混淆 Android 应用代码。使用 proguard 处理后,应用程序可以自行运行,但是从 c 到 java 的本机调用失败并显示 java.lang.NoSuchMethodError

此代码来自本机部分,其中调用了名为 EngineStarted 的 java 类实例:

void callEngineStarted( JNIEnv* env, bool isStreamOne )
{
    jclass cls;
    if(isStreamOne == true) {
        cls = ( *env )->GetObjectClass( env, currentObjectOne );
    } else {
        cls = ( *env )->GetObjectClass( env, currentObjectTwo );
    }

    jmethodID midCallBack = ( *env )->GetMethodID( env, cls, "EngineStarted", "(I)V" );
    if (0 == midCallBack) {
        LOGW("Could not find EngineStarted method in class");
        return;
    }

    if(isStreamOne == true) {
        ( *env )->CallVoidMethod( env, currentObjectOne, midCallBack, 1 );
    } else {
        ( *env )->CallVoidMethod( env, currentObjectTwo, midCallBack, 0 );
    }
}

java有这个方法。它仅从本机部分调用,而不是从其他任何地方调用。因此,proguard 正在删除该方法。

  public void EngineStarted ( int isStreamOne )
  {
    Log.v( "decoderService", "PDecoder - Engine started, using stream " + ( isStreamOne == 1 ? "one" : "two" ) );
    this.isStreamOne = isStreamOne == 1;

    // Initialize the player
    InitializePlayer( isStreamOne );
  }

我尝试将其添加到 proguard-project.txt,但没有解决问题。

-keep class com.emrahgunduz.AppBase.Services.PlayService.players.pDecoders.PDecoderNative {
    void EngineStarted( int );
    void PositionChanged( int );
    void SetDuration( int );
    void Completed();
    void CompletedWithFade();
    void Spectrum ( *** );
}

编译后,mapping.txt 不包含方法,我怀疑 proguard 删除了它们。 如何保持这些方法被删除和/或重命名?

编辑/解决方案:

我能够通过使用通配符更改完整位置来解决问题。这节省了一些方法,但还不够。不知道为什么,但是被倾倒的方法调用的另一种方法(void InitializePlayer(int))也被倾倒,这以某种方式产生了连锁反应。添加此方法解决了剩余的缺失方法。最终的解决方案变成了

-keepclassmembers class **.PDecoderNative {
    native <methods>;
    void InitializePlayer(int);
    void EngineStarted(int);
    void PositionChanged(int);
    void SetDuration(int);
    void Completed();
    void CompletedWithFade();
    void Spectrum(float[]);
}

编辑:问题不在于 proguard,而是 proguard 不时无法读取 project.txt 文件。将整个项目移动到磁盘上的新位置并重新创建文件。它运行良好。

【问题讨论】:

  • 这可能是由于proguard更改了方法名称。有一个选项可以省略对 1 个或多个方法的优化,请参阅手册中的 Keep 选项在使用 > 保留下。
  • 您可以将更改的名称记录到 mapping.txt,这是默认行为。有类似PDecoderNative getPrevious() -&gt; e的项目,但我提到的方法并没有出现在日志中。

标签: java android android-ndk java-native-interface proguard


【解决方案1】:

您的分析是正确的,您的配置看起来也正确。您应该仔细检查您的类的完全限定名称 (com.emrahgunduz.AppBase.Services.PlayService.players.pDecoders.PDecoderNative)。请注意,您必须使用 '$' 而不是 '.'分离内部类(如果适用)。

如果您指定了正确的名称,您将在 ProGuard 在 Android 构建过程中写出的文件 proguard/seeds.txt 中看到它们。

一旦成功,您可以将-keep 替换为-keepclassmembers。 ProGuard 仍然会保留方法名,但会混淆类名,这在这种情况下很好。

【讨论】:

  • 感谢您的回复。我检查了班级的名字,没问题。相同的名称也出现在使用转储回调方法的 usage.txt 中。我不知道为什么,但是类名的完全使用会导致 keep 方法的转储。但是将名称更改为 **.PDecoderNative 解决了这个问题。我会接受你的答案作为正确的答案,因为我设法通过你的名字/包裹建议解决了这个问题。
  • 我无法想象 ProGuard 会错误地解析没有通配符的类名的原因。要仔细检查,您可以尝试将其从 proguard/seeds.txt 复制/粘贴到 dexguard-project.txt 中。您还应该在 proguard/seeds.txt 中看到保留的方法名称。
  • 猜猜看,我在使用通配符进行更改后开始遇到同样的问题。移动项目文件夹并从零重新创建 project.txt 文件。现在它可以在没有通配符的情况下工作。可能是文件被锁定或损坏,或者某些安全问题不让 proguard 不时读取文件。我认为 sdk 或 macosx 的最新更新让我感到困惑。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-09
  • 2013-08-22
  • 1970-01-01
  • 2011-09-28
相关资源
最近更新 更多