Android NDK - getFieldID导致NoSuchFieldError

时间:2015-07-29 22:03:53

标签: android android-ndk

我已经在这方面挣扎了很长一段时间,而且我已经结束了。基本上我是在我们的Android应用程序中包含一个包含本机组件的库。

在其中一个本机类的init方法中,init包含以下内容:

jclass clazz = env->FindClass(kClassPathName);
if (clazz == NULL) {
    return;
}

fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
if (fields.context == NULL) {
    return;
}

GetFieldID调用始终抛出以下错误:

java.lang.NoSuchFieldError: no "I" field "mNativeContext" in class "Lcom/test/NativeLibrary;" or its superclasses

后面是长堆栈跟踪(省略因为它没用)。 它报告的类是该类的正确完全限定名称。

我已确认该课程中确实存在private int mNativeContext

我尝试的事情:

  • 将该字段公开
  • 禁用proguard混淆
  • 使用javac 1.6而不是1.7编译库(它最初是针对1.6开发的,所以我认为编译器差异可能导致问题)。

非常感谢任何帮助。

2 个答案:

答案 0 :(得分:1)

看起来问题一直是Proguard。我第一次尝试保持符号时,我一定做错了。如果您遇到此问题,请尝试在proguard.cfg中添加类似的内容:

# Don't rename any Java class members that are accessed by name from native code!
-keep class com.test.NativeLibrary {
    *** mNativeContext;
}

答案 1 :(得分:0)

我认为你的kClassPath错了。我没有使用硬编码路径,而且 而是从jCaller参数中拉出类,我将其用作 我所有原生函数的第一个参数。

这是我使用的代码片段,它将整数值写回 Java类。首先是Java类:

import java.nio.ByteBuffer;

public class Mp3 {
  public int mDecodeSampleRate= 8000;
  public int mDecodeChnlCt= 1;
  public int mDecodeByteCt;

  //Interface to Mp3 native library.
  private native int nDecodeBlock(Mp3 mp3, int hDecoder, ByteBuffer src, int srcOffset, int srcCt, ByteBuffer dst);

  public int decodeBuf(ByteBuffer src, int srcOffset, ByteBuffer dst) {
    int consumeCt= 0;
    consumeCt = nDecodeBlock(this, mDecoder, src, srcOffset, src.capacity(), dst);
    //SampleRate, ChnlCt, and ByteCt will have been updated.
    return(consumeCt);
  }

  public int decodeGetSampleRate() { return (mDecodeSampleRate); }
  public int decodeGetChannelCt() { return (mDecodeChnlCt); }
  public int decodeGetAudioByteCt() { return (mDecodeByteCt); }
}

现在是JNI C代码:

static void SetObjInt(JNIEnv *env, jobject obj, char *Name, int Val) {
  jclass objClass;
  jfieldID fieldID;
  if(!(objClass= (*env)->GetObjectClass(env,obj))) {
    LOGE("Unable to obtain objClass(%s)!",Name);
  } else if(!(fieldID= (*env)->GetFieldID(env,objClass,Name,"I"))) {
    LOGE("Unable to obtain field ID(%s).",Name);
  } else {
    //LOGD("Setting %s",Name);
    (*env)->SetIntField(env,obj,fieldID,(jint)Val);
  }
}

JNIEXPORT jint JNICALL Java_com_acme_mp3_nDecodeBlock(
  JNIEnv *env, jobject obj, jobject jCaller,
  jint jDecoder,
  jobject jSrc, jint SrcOffset, jint SrcByteCt,
  jobject jDst
) {
  void *hDecoder= (void*)jDecoder;
  unsigned char *pSrc= (*env)->GetDirectBufferAddress(env,jSrc);
  short *pDst= (*env)->GetDirectBufferAddress(env,jDst);
  int ConsumedCt= 0;
  int DstCt= 0;
  mp3_info_t Info;

  ConsumedCt= mp3_decode(hDecoder,(void*)&pSrc[SrcOffset],SrcByteCt-SrcOffset,(short*)pDst,&Info);
  if(ConsumedCt>0) {
    SetObjInt(env,jCaller,"mDecodeSampleRate",Info.sample_rate);
    SetObjInt(env,jCaller,"mDecodeChnlCt",Info.channels);
    SetObjInt(env,jCaller,"mDecodeByteCt",Info.audio_bytes);
  }
  return(ConsumedCt);
}