从.lib切换到.so后,Android应用程序崩溃了

时间:2015-04-11 21:11:59

标签: android c++ android-ndk

我在我的原生android应用程序中使用了许多静态预构建的静态库,一切正常。现在我想将我的一个静态库切换为.so。我成功地能够通过在其android.mk中用BUILD_SHARED_LIBRARY替换BUILD_STATIC_LIBRARY并添加所需的依赖项来构建.so库。

我还可以通过在其android.mk中用PREBUILT_SHARED_LIBRARY替换相应的PREBUILT_STATIC_LIBRARY来构建我的应用程序。生成的应用程序现在无法启动。我甚至无法指出调试器附加到应用程序的位置。

除此之外,我不明白的是构建系统如何知道应该从库中导入该函数。我的库应该导出一个函数,但我没有将它声明为dllexport / import或其他东西。我的应用程序中仍然没有未解析的符号(当我从列表中删除预构建的库时,未解析的符号按预期显示)。

另一个问题是我看到生成了两个.so文件。 obj / local / $(TARGET_ARCH_ABI)文件夹中的一个大文件和libs / $(TARGET_ARCH_ABI)中的另一个小文件。在声明我的预建库时,我引用了libs文件夹中的第二个。

我确实尝试在stackoverflow中搜索答案,并找到了不少相关帖子:

但我不知道这些帖子与我的问题有什么关系,因为我可以成功构建甚至链接我的应用程序。

4 个答案:

答案 0 :(得分:4)

您需要在java代码中以反向依赖顺序加载库。你以前可能有这样的事情:

System.loadLibrary("mylib");

现在,如果您的预构建库(以前是静态库,现在是共享库)名为dependencylib,则需要更改用于将库加载到此的代码:

System.loadLibrary("dependencylib");
System.loadLibrary("mylib");

关于你的问题链接器如何解决它;链接libmylib.so时,它会在您指定的所有其他库中查找所有未定义的符号(即在libdependencylib.solibc.so以及其他系统库中)。只要在某处找到所有未定义的符号,链接器就可以了。然后在运行时,加载libmylib.so时,它再次执行相同的例程;在当前进程中加载​​的符号列表中查找所有未定义的符号。在linux上,您通常不需要像在Windows上那样手动将符号标记为dllexport - 默认情况下会导出所有非静态符号。

答案 1 :(得分:2)

更改STATIC -> SHARED后,应用无法启动可能有两个原因。

  1. 未安装预构建的库。连接设备后,运行adb ls -l /data/your.package.name/lib/。你看到那里的图书馆吗?

  2. 未加载预构建的库。在主Java类中,尝试

    static { System.loadLibrary("prebuiltname"); System.loadLibrary("yourlib"); }

    这是一个静态构造函数,是加载JNI库的最安全的地方。

答案 2 :(得分:1)

如果您使用的是Linux,则会看到使用nm -D导出的符号。例如nm -D libzip.so:

...
0000000000009dc0 T zip_unchange
0000000000009dd0 T zip_unchange_all
0000000000009e30 T zip_unchange_archive
0000000000009e60 T _zip_unchange_data

如果要控制函数的可见性,请使用__attribute__ ((visibility ("default")))和命令行-fvisibility = hidden。更多信息here

答案 3 :(得分:0)

  

现在我想将我的一个静态库切换为.so。我成功地能够通过在其android.mk中用BUILD_SHARED_LIBRARY替换BUILD_STATIC_LIBRARY并添加所需的依赖项来构建.so库。

我认为你不能如果它是一个C ++库。来自<doc>/CPLUSPLUS-SUPPORT.html

  

请记住给定C ++的静态库变体   运行时只能连接到单个二进制文件中以获得最佳效果   条件。

  这意味着如果你的项目包含一个   单个共享库,您可以链接到例如stlport_static和   一切都会正常工作。

  另一方面,如果你有两个   项目中的共享库(例如libfoo.so和libbar.so)   两者都链接到相同的静态运行时,每一个都会   在最终的二进制映像中包含运行时代码的副本。这个   是有问题的,因为使用/提供了某些全局变量   内部由运行时复制。

  这可能导致代码无法正常工作,例如:

  *在一个库中分配的内存,在另一个库中释放的内存会泄漏       甚至腐败堆。
  * libfoo.so中引发的异常无法在libbar.so中捕获(并且可能       只是让程序崩溃)   * cout的缓冲不能正常工作

  如果要链接可执行文件和共享,也会发生此问题   库到同一个静态库。

  换句话说,如果您的项目需要多个共享库模块,   然后使用C ++运行时的共享库变体。

从上面来看,这意味着所有需要链接到相同的C ++标准运行时共享对象。