Android cmake 子目录依赖于 api 21

Android cmake subdirectory dependencies on api 21

Android 在 api < 23

上具有子目录 "dlopen failed: cannot locate symbol" 的 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(实用程序)

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, '[=17=]', 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.access0(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"...

您可以在 github NativeFoo

查看整个项目

这是一个非常愚蠢的错误,我的库名称是 libutils.so,但是 Android 系统还有一个名为 libutils.so 的内部库。所以,当我尝试加载 libutils.so 时,它首先加载系统库, 不是我的 !因此,我的应用程序从未按预期运行。