Android C++系列:JNI常见问题

1,215 阅读17分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情

1. 背景

本文整理了JNI开发中常见的问题和解决方案。

2. 编译时指定SDK版本问题

智能语音交互SDK工程模块编译时指定的ANDROID_PLATFORM统一是23:-DANDROID_PLATFORM=23,ndk使用的是版本是17,在手上现有设备跑的都没问题,但是在一个新采购的temi移动机器人上跑不起来,定位到问题是信号处理库报了下面问题:

java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol "__aeabi_memclr4" referenced by "/data/app/com.xxx.xxxx.robot-2/lib/arm/libkeos_signal_processing.so"...

最开始以为是信号处理库中用到了什么不兼容方法,把库的实现都改为空实现后仍报该错误,网上查询到是target version和目标设备不对应会报该错,机器人的系统版本是6.0,信号处理库编译时Application.mk中设置的APP_PLATFORM := android-26,修改完后就果然解决。

github.com/android/ndk…

github.com/android/ndk…

官网中对该问题有介绍:

当尝试加载原生库时,这些错误会显示在日志中。此符号可以是 __aeabi_* 中的任意一个;其中 __aeabi_memcpy__aeabi_memclr 可能是最常见的。此问题已记录在问题 126

2. Using _FILE_OFFSET_BITS=64 with older API levels

与上一个类似,也是API版本问题。

统一头文件之前,NDK 并不支持 _FILE_OFFSET_BITS=64。如果在构建应用时定义了该选项,系统会静默地忽略它。现在,_FILE_OFFSET_BITS=64 选项受统一头文件的支持,但在旧版本的 Android 中,很少有 off_t API 可用作 off64_t 变体。因此,如果将该功能与旧版 API 级别搭配使用,会导致可用函数减少。

r16 博文bionic 文档对该问题作了详细解释。

问题: build 请求获得的 minSdkVersion 中不存在的 API。

解决方案:停用 _FILE_OFFSET_BITS=64 或提高 minSdkVersion

3. jni local reference table overflow (max=512)

正常情况我们在方法里面申请的local reference会在方法执行完后自动释放,所以一直没有太在意local reference的释放,结果在一个线程的looper方法中有两个jstring使用完一直没释放,导致交互多轮后泄露崩溃。

4. FindClass 找不到类

常见问题类型:

  1. 确保类名称字符串的格式正确无误,JNI 类名称以软件包名称开头,并用斜线分隔,例如 java/lang/String。如果要查找某个数组类,则需要以适当数量的英文方括号开头,并且还必须用“L”和“;”将该类包裹起来,因此 String 的一维数组将是 [Ljava/lang/String;。如果要查找内部类,使用“$”而不是“.”,可以在 .class 文件上使用 javap 是查找类的内部名称。
  2. 如果启用代码混淆,请确保查找的类名未被混淆。
  3. 如果类名称形式正确,可能是遇到了类加载器问题。FindClass 需要在与代码关联的类加载器的启动类搜索。它会检查调用堆栈,如下所示:
    Foo.myfunc(Native Method)
        Foo.main(Foo.java:10)

最顶层的方法是 Foo.myfuncFindClass 会查找与 Foo 类关联的 ClassLoader 对象并使用它。

采用这种方法一般情况可以满足我们的需求。但是如果在native线程(比如通过调用 pthread_create,然后使用 AttachCurrentThread 进行附加),可能会有问题。因为在这个线程中没有堆栈帧,如果从此线程调用 FindClass,JavaVM 会在“系统”类加载器(而不是与应用关联的类加载器)中启动,因此尝试查找特定于应用的类将失败。

可以通过以下几种方法来解决此问题:

  • JNI_OnLoad 中执行一次 FindClass 查找,然后缓存类引用以供日后使用。在执行 JNI_OnLoad 过程中发出的任何 FindClass 调用都会使用与调用 System.loadLibrary 的函数关联的类加载器(这是一条特殊规则,用于更方便地进行库初始化)。如果我们的应用代码要加载库,FindClass 会使用正确的类加载器。
  • 通过声明原生方法来获取 Class 参数,然后传入 Foo.class,从而将类的实例传递给需要它的函数。
  • 在某个便捷位置缓存 ClassLoader 对象的引用,然后直接发出 loadClass 调用,这个比较麻烦一些,而且缓存classloader可能也会存在一些问题。
  • 使用java/lang/ClassLoadergetClassLoader,然后调用findClass:
static jobject gClassLoader;
static jmethodID gFindClassMethod;

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *pjvm, void *reserved) {
    gJvm = pjvm;  // cache the JavaVM pointer
    auto env = getEnv();
    //replace with one of your classes in the line below
    auto randomClass = env->FindClass("com/example/RandomClass");
    jclass classClass = env->GetObjectClass(randomClass);
    auto classLoaderClass = env->FindClass("java/lang/ClassLoader");
    auto getClassLoaderMethod = env->GetMethodID(classClass, "getClassLoader",
                                             "()Ljava/lang/ClassLoader;");
    gClassLoader = env->CallObjectMethod(randomClass, getClassLoaderMethod);
    gFindClassMethod = env->GetMethodID(classLoaderClass, "findClass",
                                    "(Ljava/lang/String;)Ljava/lang/Class;");

    return JNI_VERSION_1_6;
}

jclass findClass(const char* name) {
    return static_cast<jclass>(getEnv()->CallObjectMethod(gClassLoader, gFindClassMethod, getEnv()->NewStringUTF(name)));
}

JNIEnv* getEnv() {
    JNIEnv *env;
    int status = gJvm->GetEnv((void**)&env, JNI_VERSION_1_6);
    if(status < 0) {    
        status = gJvm->AttachCurrentThread(&env, NULL);
        if(status < 0) {        
            return nullptr;
        }
    }
    return env;
}
No pending exception expected: java.lang.ClassNotFoundException: Didn't find class "com.qingkouwei.foundclassdemo.TestClass" on path: DexPathList[[directory "."],nativeLibraryDirectories=[/system/lib64, 

stackoverflow.com/questions/1…

5. 返回值错误问题

我们在其他平台,比如linux中声明了返回值类型为int,实际函数体没有return可能没有问题,但是Android平台会直接崩溃。

2022-03-07 15:09:01.152 16695-16695/com.qingkouwei.foundclassdemo A/libc: Fatal signal 5 (SIGTRAP), code 1 (TRAP_BRKPT), fault addr 0x7656c5c5e4 in tid 16695 (.foundclassdemo), pid 16695 (.foundclassdemo)
2022-03-07 15:09:01.157 16639-16881/? E/SecurityComp10105306EncryptUtil: exception : Failed resolution of: Lorg/bouncycastle/crypto/engines/AESEngine; , you should implementation bcprov-jdk15on library
2022-03-07 15:09:01.222 16908-16908/? A/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
2022-03-07 15:09:01.222 16908-16908/? A/DEBUG: Build fingerprint: 'HONOR/YAL-AL00/HWYAL:10/HUAWEIYAL-AL00/10.1.0.162C00:user/release-keys'
2022-03-07 15:09:01.222 16908-16908/? A/DEBUG: Revision: '0'
2022-03-07 15:09:01.222 16908-16908/? A/DEBUG: ABI: 'arm64'
2022-03-07 15:09:01.226 16908-16908/? A/DEBUG: SYSVMTYPE: Maple
    APPVMTYPE: Art
2022-03-07 15:09:01.226 16908-16908/? A/DEBUG: Timestamp: 2022-03-07 15:09:01+0800
2022-03-07 15:09:01.226 16908-16908/? A/DEBUG: pid: 16695, tid: 16695, name: .foundclassdemo  >>> com.qingkouwei.foundclassdemo <<<
2022-03-07 15:09:01.226 16908-16908/? A/DEBUG: uid: 10616
2022-03-07 15:09:01.226 16908-16908/? A/DEBUG: signal 5 (SIGTRAP), code 1 (TRAP_BRKPT), fault addr 0x7656c5c5e4
2022-03-07 15:09:01.226 16908-16908/? A/DEBUG:     x0  000000000000006a  x1  0000007fd6d0b8e0  x2  0000000000000004  x3  0000000000000005
2022-03-07 15:09:01.226 16908-16908/? A/DEBUG:     x4  0000000000000000  x5  4008000000000000  x6  0000000000000000  x7  7f7f7f7f7f7f7f7f
2022-03-07 15:09:01.226 16908-16908/? A/DEBUG:     x8  cefdb6c250ecaf4a  x9  cefdb6c250ecaf4a  x10 00000076f406305c  x11 000000000000003b
2022-03-07 15:09:01.226 16908-16908/? A/DEBUG:     x12 0000000000000018  x13 ffffffffffffffff  x14 ffffffffff000000  x15 ffffffffffffffff
2022-03-07 15:09:01.226 16908-16908/? A/DEBUG:     x16 00000076f406a8f8  x17 00000076f5a4d560  x18 00000076f8cbe000  x19 0000007672210800
2022-03-07 15:09:01.226 16908-16908/? A/DEBUG:     x20 0000000000000000  x21 0000007672210800  x22 0000007fd6d0c450  x23 000000766176c9cb
2022-03-07 15:09:01.226 16908-16908/? A/DEBUG:     x24 0000000000000008  x25 00000076f7e51020  x26 00000076722108b0  x27 0000000000000002
2022-03-07 15:09:01.226 16908-16908/? A/DEBUG:     x28 0000007fd6d0c1e0  x29 0000007fd6d0c160
2022-03-07 15:09:01.226 16908-16908/? A/DEBUG:     sp  0000007fd6d0c0b0  lr  0000007656c5c5e4  pc  0000007656c5c5e4
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG: backtrace:
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #00 pc 00000000000025e4  /data/app/com.qingkouwei.foundclassdemo-9j6a3XxFGTn9Cw9oVWDDpg==/lib/arm64/libnative-lib.so (myFindClass+344) (BuildId: c9adfab317deb3b439ef1646bc9b03951ef68837)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #01 pc 0000000000002c30  /data/app/com.qingkouwei.foundclassdemo-9j6a3XxFGTn9Cw9oVWDDpg==/lib/arm64/libnative-lib.so (BuildId: c9adfab317deb3b439ef1646bc9b03951ef68837)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #02 pc 0000000000150350  /apex/com.android.runtime/lib64/libart.so (art_quick_generic_jni_trampoline+144) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #03 pc 0000000000147334  /apex/com.android.runtime/lib64/libart.so (art_quick_invoke_stub+548) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #04 pc 00000000001561b4  /apex/com.android.runtime/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+252) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #05 pc 00000000002fd900  /apex/com.android.runtime/lib64/libart.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+384) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #06 pc 00000000002f8bd0  /apex/com.android.runtime/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+912) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #07 pc 00000000005cb550  /apex/com.android.runtime/lib64/libart.so (MterpInvokeVirtual+648) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #08 pc 0000000000141814  /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_virtual+20) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #09 pc 0000000000123f60  [anon:dalvik-classes.dex extracted in memory from /data/app/com.qingkouwei.foundclassdemo-9j6a3XxFGTn9Cw9oVWDDpg==/base.apk] (com.qingkouwei.foundclassdemo.MainActivity$1.onClick+12)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #10 pc 00000000005cd060  /apex/com.android.runtime/lib64/libart.so (MterpInvokeInterface+1752) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #11 pc 0000000000141a14  /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_interface+20) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #12 pc 000000000023af0c  /system/framework/framework.jar (android.view.View.performClick+40)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #13 pc 00000000005cb860  /apex/com.android.runtime/lib64/libart.so (MterpInvokeVirtual+1432) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #14 pc 0000000000141814  /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_virtual+20) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #15 pc 000000000023af56  /system/framework/framework.jar (android.view.View.performClickInternal+6)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #16 pc 00000000005cdbfc  /apex/com.android.runtime/lib64/libart.so (MterpInvokeDirect+1168) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #17 pc 0000000000141914  /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_direct+20) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #18 pc 00000000002363fc  /system/framework/framework.jar (android.view.View.access$3600)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #19 pc 00000000005ce408  /apex/com.android.runtime/lib64/libart.so (MterpInvokeStatic+1136) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #20 pc 0000000000141994  /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_static+20) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #21 pc 000000000021320c  /system/framework/framework.jar (android.view.View$PerformClick.run+56)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #22 pc 00000000005cd060  /apex/com.android.runtime/lib64/libart.so (MterpInvokeInterface+1752) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #23 pc 0000000000141a14  /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_interface+20) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #24 pc 000000000030cc4c  /system/framework/framework.jar (android.os.Handler.handleCallback+4)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #25 pc 00000000005ce408  /apex/com.android.runtime/lib64/libart.so (MterpInvokeStatic+1136) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #26 pc 0000000000141994  /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_static+20) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #27 pc 000000000030cab8  /system/framework/framework.jar (android.os.Handler.dispatchMessage+8)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #28 pc 00000000005cb860  /apex/com.android.runtime/lib64/libart.so (MterpInvokeVirtual+1432) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #29 pc 0000000000141814  /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_virtual+20) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #30 pc 0000000000337c30  /system/framework/framework.jar (android.os.Looper.loop+480)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #31 pc 00000000005ce408  /apex/com.android.runtime/lib64/libart.so (MterpInvokeStatic+1136) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #32 pc 0000000000141994  /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_static+20) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #33 pc 00000000001abfe0  /system/framework/framework.jar (android.app.ActivityThread.main+1372)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #34 pc 00000000002ce22c  /apex/com.android.runtime/lib64/libart.so (_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_6JValueEbb.llvm.10887373532384510885+320) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #35 pc 00000000005bc090  /apex/com.android.runtime/lib64/libart.so (artQuickToInterpreterBridge+1012) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #36 pc 0000000000150468  /apex/com.android.runtime/lib64/libart.so (art_quick_to_interpreter_bridge+88) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #37 pc 00000000001475b8  /apex/com.android.runtime/lib64/libart.so (art_quick_invoke_static_stub+568) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #38 pc 00000000001561d4  /apex/com.android.runtime/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+284) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #39 pc 00000000004d8820  /apex/com.android.runtime/lib64/libart.so (art::(anonymous namespace)::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::(anonymous namespace)::ArgArray*, art::JValue*, char const*)+104) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #40 pc 00000000004da248  /apex/com.android.runtime/lib64/libart.so (art::InvokeMethod(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jobject*, _jobject*, unsigned long)+1476) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #41 pc 000000000046412c  /apex/com.android.runtime/lib64/libart.so (art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobjectArray*)+52) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #42 pc 00000000000f8c34  /system/framework/arm64/boot.oat (art_jni_trampoline+180) (BuildId: 8fb9eb57b3ea725573d56f165d17f78dd54965d7)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #43 pc 0000000000147334  /apex/com.android.runtime/lib64/libart.so (art_quick_invoke_stub+548) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #44 pc 00000000001561b4  /apex/com.android.runtime/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+252) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #45 pc 00000000002fd900  /apex/com.android.runtime/lib64/libart.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+384) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #46 pc 00000000002f8bd0  /apex/com.android.runtime/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+912) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #47 pc 00000000005cb550  /apex/com.android.runtime/lib64/libart.so (MterpInvokeVirtual+648) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #48 pc 0000000000141814  /apex/com.android.runtime/lib64/libart.so (mterp_op_invoke_virtual+20) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #49 pc 000000000040010e  /system/framework/framework.jar (com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run+22)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #50 pc 00000000002ce22c  /apex/com.android.runtime/lib64/libart.so (_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_6JValueEbb.llvm.10887373532384510885+320) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #51 pc 00000000005bc090  /apex/com.android.runtime/lib64/libart.so (artQuickToInterpreterBridge+1012) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #52 pc 0000000000150468  /apex/com.android.runtime/lib64/libart.so (art_quick_to_interpreter_bridge+88) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #53 pc 0000000000b253bc  /system/framework/arm64/boot-framework.oat (com.android.internal.os.ZygoteInit.main+3660) (BuildId: 839677a7635b67b1b883c48b6ed1ea5c4a581790)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #54 pc 00000000001475b8  /apex/com.android.runtime/lib64/libart.so (art_quick_invoke_static_stub+568) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #55 pc 00000000001561d4  /apex/com.android.runtime/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+284) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #56 pc 00000000004d8820  /apex/com.android.runtime/lib64/libart.so (art::(anonymous namespace)::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::(anonymous namespace)::ArgArray*, art::JValue*, char const*)+104) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #57 pc 00000000004d848c  /apex/com.android.runtime/lib64/libart.so (art::InvokeWithVarArgs(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, std::__va_list)+408) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #58 pc 00000000003d7cf8  /apex/com.android.runtime/lib64/libart.so (art::JNI::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)+660) (BuildId: 08543716770b195bd10fafbe11bb5052)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #59 pc 00000000001015c4  /system/lib64/libandroid_runtime.so (_JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...)+116) (BuildId: 034c759a360c29cae9a101b25604f692)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #60 pc 0000000000104c48  /system/lib64/libandroid_runtime.so (android::AndroidRuntime::start(char const*, android::Vector<android::String8> const&, bool)+1248) (BuildId: 034c759a360c29cae9a101b25604f692)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #61 pc 00000000000034e0  /system/bin/app_process64 (main+1168) (BuildId: e844be217eb39b34490d3798328d1f12)
2022-03-07 15:09:01.375 16908-16908/? A/DEBUG:       #62 pc 000000000006b108  /apex/com.android.runtime/lib64/bionic/libc.so (__libc_init+108) (BuildId: b91c775ccc9b0556e91bc575a2511cd0)
2022-03-07 15:09:01.405 17145-17756/? E/X509CertUtil: can not open cbg root cer

6. NDK指南中介绍的其他错误

6.1 minSdkVersion 的设置高于设备的 API 级别

使用 NDK 进行构建所依据的 API 级别与 Java 中的 compileSdkVersion 所代表的含义截然不同。NDK API 级别是您的应用支持的最低 API 级别。在 ndk-build 中,这是指 APP_PLATFORM 设置。对于 CMake,这是指 -DANDROID_PLATFORM。(如果使用的是 externalNativeBuild,系统会自动使用您的 minSdkVersion。)

系统对函数引用的解析通常发生在库加载时,而不是首次调用时,因此,对于并非始终存在并通过 API 级别检查保护对其的使用的 API,将无法引用。如果 API 被引用,那么此 API 就必须存在。

问题: NDK API 级别高于设备支持的 API 级别。

解决方案:将 NDK API 级别 (APP_PLATFORM) 设置为应用支持的最低 Android 版本。

6.2 找不到 rand 符号

对于以下错误日志消息,请参阅这条详细的 Stack Overflow 解答

UnsatisfiedLinkError: dlopen failed: cannot locate symbol "rand"

6.3 未定义对 __atomic_* 的引用

问题:某些 ABI 需要 libatomic 才能为原子操作提供一些实现。

解决方案:在链接时添加 -latomic

对于以下错误消息:

error: undefined reference to '__atomic_exchange_4'

此处的实际符号可能是前缀为 __atomic_ 的任何符号。(ndk-build 和 CMake 会替我们处理这个问题。对于其他构建系统,我们需要手动处理。)

6.3 RTTI/异常捕获功能无法跨库边界运行

问题:在跨共享库边界抛出异常或 dynamic_cast 失败时,无法捕获异常。

解决方案:为我们的类型添加一个关键函数。关键函数是某个类型的第一个非纯的外联虚拟函数。可参阅关于问题 533 的讨论。

C++ ABI 规定,当且仅当两个对象的 type_info 指针相同时,两者才具有相同的类型。只有当捕获的 type_info 与抛出的异常匹配时,系统才会捕获异常。这项规则同样适用于 dynamic_cast

如果某个类型没有关键函数,系统会将其 typeinfo 作为弱符号发出,并在加载库时合并匹配类型信息。如果在可执行文件加载完毕后动态加载库(也就是说通过 dlopenSystem.loadLibrary 加载),加载器可能无法合并已加载库的类型信息。当出现这种情况时,这两种类型就不会被视为等同。

对于非多态类型,类型不能具有关键函数。对于非多态类型,RTTI 是多余的,因为可使用 std::is_same 在编译时确定类型等同性。

6.4 使用不匹配的预构建库

在我们的应用中使用预构建库(这些库通常是第三方库)时,需要特别注意。一般来说,请注意以下规则:

  • 生成的应用的最低 API 级别是应用所有库的最大 minSdkVersion 值。

    如果我们的 minSdkVersion 是 16,但使用了依据 21 构建的预构建库,那么生成的应用的最低 API 级别为 21。如果预构建库是静态的,那么对于此规则的违反将会在构建时显示出来,但在预构建共享库运行时才会显示。

  • 应使用相同的 NDK 版本生成所有库。

    由于违反的情况极少,此规则比大多数规则的灵活性略高,但无法保证使用不同 Major 版本的 NDK 构建的库之间的兼容性。C++ ABI 并非稳定版,在过去有过变化。

  • 具有多个共享库的应用必须使用一个共享 STL

    就 STL 不匹配而言,可以通过小心谨慎地操作来避免由此引起的问题,但最好直接避免该问题。为避免该问题,最好避免在您的应用中使用多个共享库。

7. 总结

本文介绍了JNI开发过程中常见的问题,比如版本问题,定位符号失败问题以及不同平台函数实现的差异等。后续可以持续维护迭代本文档。