Android NDK - getFieldID 导致 NoSuchFieldError

Android NDK - getFieldID results in NoSuchFieldError

我已经为此苦苦挣扎了很长一段时间,现在已经束手无策了。基本上,我在我们的 Android 应用程序中包含了一个具有本机组件的库。

在原生 classes 之一的 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

后面是一段很长的堆栈跟踪(因为没用所以省略)。 它报告的 class 是 class.

的正确完全限定名称

我已经确认 class 中确实有一个 private int mNativeContext

我尝试过的事情:

如有任何帮助,我们将不胜感激。

我认为你的 kClassPath 是错误的。我不使用硬编码路径,并且 而是从我用作 我所有本机函数的第一个参数。

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

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

看起来问题一直出在 Proguard 上。我第一次尝试保留符号时一定做错了什么。如果您 运行 遇到这个问题,请尝试将类似的内容添加到您的 proguard.cfg:

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