【问题标题】:Android cmake subdirectory dependencies on api 21Android cmake子目录依赖api 21
【发布时间】:2019-09-10 09:54:39
【问题描述】:

在 api 上带有子目录“dlopen failed: cannot locate symbol”的 Android cmake 项目

同样的代码在 api >= 23 上运行良好

如果成功,这个测试程序将打印一个日志

项目结构

src/main/cpp
  +foo
    CMakeLists.txt
    foo.cpp
  +utils
    CMakeLists.txt
    log-utils.h
    log-utils.cpp
  CMakeLists.txt

CMakeLists.txt(foo)

project(foo)

add_library(foo SHARED foo.cpp)

target_link_libraries(foo PUBLIC utils)

foo.cpp

#include <jni.h>
#include <utils/log-utils.h>
#include <cstring>

jint JNI_OnLoad(JavaVM* vm, void * reserved) {
    const char * test = "test message";
    printByte("JNI_OnLoad", test, strlen(test));
    JNIEnv *env;
    if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK) {
        return -1;
    }
    return JNI_VERSION_1_6;
}

CMakeLists.txt(utils)

project(utils)

find_library(log-lib log)

add_library(utils SHARED log-utils.cpp)

target_link_libraries(utils ${log-lib})

log-utils.h

#ifndef LOG_UTILS_H
#define LOG_UTILS_H

void printByte(const char*tag, const char* content, unsigned int len);

#endif //LOG_UTILS_H

log-utils.cpp

#include "log-utils.h"

#include <cstring>
#include <cstdio>
#include <android/log.h>

static const char * TAG = "LOG_UTILS";

void printByte(const char*tag, const char* content, unsigned int len) {
    char buff[len*2+1];
    memset(buff, '\0', len*2+1);
    for (int i = 0; i<len; ++i) {
        sprintf(buff+i*2, "%02x", content[i] & 0xff);
    }
    __android_log_print(ANDROID_LOG_DEBUG, TAG, "%-12s%04d: %s\n", tag, len, buff);
}

CMakeLists.txt(根)

cmake_minimum_required(VERSION 3.4.1)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer")

include_directories(./)

add_subdirectory(utils)
add_subdirectory(foo)

MainActivity.kt

class MainActivity : AppCompatActivity() {
    companion object {
        init {
            System.loadLibrary("utils")
            System.loadLibrary("foo")
        }
    }
    // ...
}

两个库:libfoo.so、libutils.so、libfoo.so 依赖于 libutils.so。在 Android 4.4 和 5.0 上,它只是崩溃,系统日志:

09-10 17:36:33.388 10908-10908/? E/art: dlopen("/data/app/com.example.nativefoo-2/lib/x86/libfoo.so", RTLD_LAZY) failed: dlopen failed: cannot locate symbol "_Z9printBytePKcS0_j" referenced by "libfoo.so"...
09-10 17:36:33.388 10908-10908/? D/AndroidRuntime: Shutting down VM
09-10 17:36:33.389 10908-10908/? E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.nativefoo, PID: 10908
    java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol "_Z9printBytePKcS0_j" referenced by "libfoo.so"...
        at java.lang.Runtime.loadLibrary(Runtime.java:371)
        at java.lang.System.loadLibrary(System.java:989)
        at com.example.nativefoo.MainActivity.<clinit>(MainActivity.kt:18)
        at java.lang.reflect.Constructor.newInstance(Native Method)
        at java.lang.Class.newInstance(Class.java:1572)
        at android.app.Instrumentation.newActivity(Instrumentation.java:1065)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2199)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2360)
        at android.app.ActivityThread.access$800(ActivityThread.java:144)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1278)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:135)
        at android.app.ActivityThread.main(ActivityThread.java:5221)
        at java.lang.reflect.Method.invoke(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:372)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)

Update1 2019.09.11 07:23 UTC 时间

app/build.gradle

    compileSdkVersion 29
    buildToolsVersion "29.0.2"
    defaultConfig {
        applicationId "com.example.nativefoo"
        minSdkVersion 18
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                cppFlags ""
                abiFilters "armeabi-v7a", "x86"
            }
        }
    }

我认为 libutils.so 加载正确,请参阅 logcat

09-11 15:27:29.622 D/dalvikvm: Not late-enabling CheckJNI (already on)
09-11 15:27:29.672 D/dalvikvm: Trying to load lib /data/app-lib/com.example.nativefoo-1/libutils.so 0x9d061440
09-11 15:27:29.672 D/dalvikvm: Added shared lib /data/app-lib/com.example.nativefoo-1/libutils.so 0x9d061440
09-11 15:27:29.672 D/dalvikvm: No JNI_OnLoad found in /data/app-lib/com.example.nativefoo-1/libutils.so 0x9d061440, skipping init
09-11 15:27:29.672 D/dalvikvm: Trying to load lib /data/app-lib/com.example.nativefoo-1/libfoo.so 0x9d061440
09-11 15:27:29.672 E/dalvikvm: dlopen("/data/app-lib/com.example.nativefoo-1/libfoo.so") failed: dlopen failed: cannot locate symbol "_Z9printBytePKcS0_j" referenced by "libfoo.so"...

你可以在githubNativeFoo看到整个项目

【问题讨论】:

  • 你的minSdkVersion 是什么?你能分享你的 build.gradle 吗?
  • 日志是否显示 libutils.so 已加载?
  • 请注意,System.loadLibrary("utils") 在 4.4 上是必需的,但在 5 上不需要。
  • @DanAlbert minSdkVersion 为 18
  • @AlexCohn logcat 确实显示 libutils.so 已添加

标签: cmake android-ndk


【解决方案1】:

这是一个非常愚蠢的错误,我的库名称是 libutils.so,但是 Android 系统也有一个内部库,叫做 libutils.so。所以,当我尝试加载 libutils.so 时,它会先加载系统库,不是我的!因此,我的应用程序永远不会按预期工作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多