【问题标题】:Loading a native library in an Android JUnit test在 Android JUnit 测试中加载本机库
【发布时间】:2013-05-08 12:39:56
【问题描述】:

我使用ndk-build 生成了一个本机库,我可以在我的Android 应用程序中加载和使用它。但是,我想针对我的应用程序的这一部分编写一些测试。

在我的测试中调用本机函数时,我收到以下异常消息:

java.lang.UnsatisfiedLinkError: no process in java.library.path

...process 是我要导入的本地库,名为 libprocess.so

我正在使用 Roboelectric 进行测试,并使用 RobolectricTestRunner 运行这个特定的测试,如果它有所作为的话。

如何让我的测试项目“看到”本机库?


编辑: 我正在我的应用程序中加载库,如下所示:

static {
    System.loadLibrary("process");
}
public static native int[] process(double[][] data);

调用Process.process(array) 在应用程序中工作正常(库已加载),但在从测试运行时失败,除了上面给出的异常。


编辑 2: 如果我将-Djava.library.path="<the directory of libprocess.so>" 设置为VM 参数,那么:

System.out.println(System.getProperty("java.library.path"));

确实显示了我设置的路径,但我仍然遇到相同的异常。我将目录设置为:

<project-name>/libs/x86

...但作为绝对路径。

【问题讨论】:

  • 你的包名正确吗?你是如何加载你的图书馆的?
  • 我已经添加到我的问题中,见上文。
  • 你会用这个测试吗:-Djava.library.path="&lt;the directory of libprocess.so&gt;"?
  • 感谢您的帮助,但将其添加到 Eclipse 运行配置选项下的“VM 参数”会导致同样的错误。
  • 您使用任何存储库吗?有没有可能我可以看到你的代码?

标签: java android junit android-ndk


【解决方案1】:

对于仍在寻找的人来说,blork 的想法是正确的 - 您需要为您的“原生”平台(Windows、Linux、Mac)编译原生库。 Android NDK 为 Android 平台构建库(.so 文件 - 也可能在 Linux 上运行),这就是在 Activity Test Cases 中运行没有问题的原因(因为它加载了一个 Android 实例)。

要运行低级、快速的 JUnit 测试,您需要支持 JVM。在 Windows 上,这可能正在构建 DLL,在 Apple 上,它正在构建 dylib(假设共享库)。

我刚刚在我的 android-ndk-swig-example 存储库 (https://github.com/sureshjoshi/android-ndk-swig-example/issues/9) 中完成了一个示例。

基本上,在我的 CMakeLists 中,我添加了一个 Apple 警告:

# Need to create the .dylib and .jnilib files in order to run JUnit tests
if (APPLE)
    # Ensure jni.h is found
    find_package(JNI REQUIRED)
    include_directories(${JAVA_INCLUDE_PATH})

然后我确保 Gradle 运行单元测试,但使用 Mac 构建系统(不是 NDK)。

def osxDir = projectDir.absolutePath + '/.externalNativeBuild/cmake/debug/osx/'

task createBuildDir() {
    def folder = new File(osxDir)
    if (!folder.exists()) {
        folder.mkdirs()
    }
}

task runCMake(type: Exec) {
    dependsOn createBuildDir
    workingDir osxDir // Jump to future build directory
    commandLine '/usr/local/bin/cmake' // Path from HomeBrew installation
    args '../../../../' // Relative path for out-of-source builds
}

task runMake(type: Exec) {
    dependsOn runCMake
    workingDir osxDir
    commandLine 'make'
}

 project.afterEvaluate {
    // Not sure how much of a hack this is - but it allows CMake/SWIG to run before Android Studio
    // complains about missing generated files
    // TODO: Probably need a release hook too?
    javaPreCompileDebug.dependsOn externalNativeBuildDebug
    if (org.gradle.internal.os.OperatingSystem.current().isMacOsX()) {
        javaPreCompileDebugAndroidTest.dependsOn runMake
    }
 }

注意时间!!!

当您使用此方法时,从技术上讲,您并不是在测试 NDK 生成的库。您正在测试相同的代码,但使用不同的编译器(msvc、xcode、gcc、clang,无论您在主机上使用什么)编译。

这实际上意味着,大多数测试结果都是有效的 - 除非您遇到由每个编译器的怪癖或 STL 实现等引起的问题...这并不像 10+ 时那么糟糕多年前,但您不能 100% 肯定地说,使用主机库进行 JUnit 测试的结果与 Android 库相同。不过,您可以说它相当接近。

再说一次,除非您使用 Android NDK 为每个受支持的架构运行您的本机单元测试,否则您也不能说任何关于确定性的事情......所以,从它那里得到你想要的。

一个矫枉过正的方法(但如果自动化真的很酷)是编写你的原生单元测试,不管你做什么(谷歌测试、Catch 等),然后编译并运行你的原生库和每个使用 Android NDK 的单元测试建筑学。这为您的潜在目标架构提供了 C/C++ 覆盖。

从这里,您可以将上述主机库与 JUnit 一起使用,以快速对您的 JNI 层与您的本机库交互进行单元测试。在您的 CI 系统中,您可能仍然应该运行这些相同的单元测试 - 但作为 Android Instrumentation 测试(或运行模拟 Android 环境的其他东西)。

与所有事情一样,只要有接口,您就可以创建模拟 - 但在某些时候,您也需要系统/功能/集成测试。

更新:

在博客文章中更全面地解释上述内容(http://www.sureshjoshi.com/mobile/android-junit-native-libraries/

【讨论】:

  • 为了在 Windows 上获得它,我应该改变什么? (64 位)
  • @Pregunton 好吧,可能大部分构建脚本 - 因为一切都是使用 *nix 样式参数和目录结构设置的。您必须在 PATH 中有 cmake,然后设置正确的 Windows 命令行调用。我确信有一种跨平台的方式可以做到这一点。 CMakeLists 可能也需要一些调整 - 只是不记得,因为我在几年前做过。
  • 我喜欢这个答案,并想添加一个更正。 Android NDK 为 Android 平台构建库(.so 文件 - 可能也适用于 Linux),>> 这不是真的,因为 .so 是纯平台特定的,即 x86 或 ARM 或其他平台。
【解决方案2】:

以防万一,对于正在寻找解决方案的其他人,一种方法可以为 JUnit 测试正确设置 lib 查找路径(尽管我仍然没有成功运行测试,不同的问题集):

需要将路径放在LD_LIBRARY_PATH(或在Windows 上为PATH)环境变量中。我有完全相同的经历:我首先设置了java.library.path,但似乎它不会以任何方式影响本机加载程序的行为。

如果您需要有关如何使用 Gradle/Android Studio 执行此操作的详细信息,欢迎查看 my answer here

【讨论】:

    【解决方案3】:

    我已切换到默认测试样式(使用 ActivityUnitTestCase 而不是 RoboElectric),它现在运行良好。可惜我不得不牺牲测试运行的速度,但在模拟器上运行测试确实有效。您还可以为 JNI 类创建一个影子类,如下所述:

    Robolectric tanks on Application objects that load JNI libraries. Can I get a workaround?

    也许为我的机器编译库会奏效,但我不能再花时间在上面了。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-09-20
      • 1970-01-01
      • 1970-01-01
      • 2016-04-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多