System.loadLibrary("cocos2dcpp"); 的问题

本文探讨了Android应用如何在不同ABI(如ARMv5和ARMv7)间选择最佳机器码的问题。通过实例说明了如何正确配置Application.mk来生成对应的so文件,并放置于正确的目录。还讨论了在特定条件下,不同版本Android系统的包管理服务行为差异。

Android应用中使用到C代码,编译成so文件打包进apk的libs路径下。因为要支持ARMv5和ARMv7,libs下有armeabi和armeabi-v7a两个目录。安全的做法是编译库C代码时,Application.mk中打开,

APP_ABI := armeabi armeabi-v7a

编译生成的两份so文件各自放到项目工程的armeabi/armeabi-v7a目录中。有时还需要针对v7a平台编译支持NEON和不支持NEON的库。譬如这个例子,

in armeabi/: libp.so, libd.so, libvh.so, libvn.so in armeabi-v7a/: libp.so, libd.so, libd_neon.so, libvh.so, libvn.so

需要注意这一点:The 'armeabi-v7a' machine code will not run on ARMv5 or ARMv6 based devices. 如果损失一些应用的性能是可以接受的,不希望保留库的两份拷贝,可以移除armeabi-v7a目录和其下的库文件,只保留armeabi目录。在安装应用时,行为是这样的,

When installing an application, the package manager service will scan the .apk and look for any shared library of the form:

lib/<primary-abi>/lib<name>.so

If one is found, then it is copied under $APPDIR/lib/lib.so, where $APPDIR corresponds to the application's specific data directory. If none is found, and a secondary ABI is defined, the service will then scan for shared libraries of the form:

lib/<secondary-abi>/lib<name>.so

If anything is found, then it is copied under $APPDIR/lib/lib.so.

什么是primary abi和secondary abi呢,

The Android system knows at runtime which ABI(s) it supports. More precisely, up to two build-specific system properties are used to indicate: the 'primary' ABI for the device, corresponding to the machine code used in the system image itself. an optional 'secondary' ABI, corresponding to another ABI that is also supported by the system image. For example, a typical ARMv5TE-based device would only define the primary ABI as 'armeabi' and not define a secondary one. On the other hand, a typical ARMv7-based device would define the primary ABI to 'armeabi-v7a' and the secondary one to 'armeabi' since it can run application native binaries generated for both of them. This mechanism ensures that the best machine code for the target device is automatically extracted from the package at installation time.

考虑这样一个情况,在打包apk时,armeabi-v7a目录下放了所需的部分so文件。还是上面那个例子,

in armeabi/: libp.so, libd.so, libvh.so, libvn.so in armeabi-v7a/: libd.so, libd_neon.so

如此,安装运行在v7a的设备上,会发生什么? 尝试了一些手机和平板,CPU都是ARMv7的,Android都是4.x的。行为是拷贝以下库文件到$APPDIR/lib/目录下,

armeabi-v7a/libd.so, armeabi-v7a/libd_neon.so, armeabi/libp.so, armeabi/libvh.so, armeabi/libvn.so

符合特殊需求,libd对性能影响较大,针对ARMv7设备,使用armeabi-v7a的库,其他都使用armeabi的库。 但是遇到在一台ARMv7+Android2.3.4的手机上,加载libp.so报错。 再尝试另一台ARMv7+Android2.3.5的手机,一样的现象。 针对这两台手机,如果打包apk时移除armeabi-v7a目录,不会报错。 难道Android2.3的设备,package manager service的行为有异?

### 关于 `System.loadLibrary` 的用法 #### 1. **基本概念** `System.loadLibrary` 是 Java 提供的一个静态方法,用于加载本地库(Native Library)。该方法会自动根据操作系统平台寻找对应的共享库文件,并将其加载到 JVM 中。通常情况下,开发者不需要关心具体的文件路径或扩展名,只需指定库的名字即可。 #### 2. **方法签名** ```java public static void loadLibrary(String libname) ``` - 参数 `libname`: 表示要加载的本地库的名称,不包含前缀(如 `lib`)和后缀(如 `.so`, `.dll`, `.dylib`)[^1]。 #### 3. **工作原理** 当调用 `System.loadLibrary("yourlibrary")` 时,JVM 会在默认的库路径下查找名为 `libyourlibrary.so`(Linux)、`yourlibrary.dll`(Windows)或 `libyourlibrary.dylib`(macOS)的文件,并尝试加载它。如果找不到对应文件,则抛出 `UnsatisfiedLinkError` 异常[^1]。 #### 4. **代码示例** 以下是一个典型的使用案例: ```java public class LoadLibraryExample { // 声明本地方法 public native void nativeMethod(); static { // 加载本地库 try { System.loadLibrary("yourlibrary"); } catch (UnsatisfiedLinkError e) { System.err.println("Failed to load library: " + e.getMessage()); } } public static void main(String[] args) { LoadLibraryExample example = new LoadLibraryExample(); example.nativeMethod(); // 调用本地方法 } } ``` 此代码展示了如何通过 `System.loadLibrary` 加载一个名为 `yourlibrary` 的本地库,并声明了一个本地方法 `nativeMethod()` 进行调用。 #### 5. **常见问题及解决方案** ##### (1)**未找到库文件** 如果运行程序时报错 `UnsatisfiedLinkError`,可能是由于目标库文件不存在于系统的库路径中。解决办法包括: - 将库文件放置在 JVM 默认的库目录中。 - 设置环境变量 `-Djava.library.path=<path_to_library>` 指定自定义路径[^3]。 ##### (2)**跨平台兼容性** 不同操作系统对库文件有不同的命名约定。例如,在 Linux 上为 `lib<name>.so`,而在 Windows 上为 `<name>.dll`。因此,建议开发时遵循统一的标准命名规则。 ##### (3)**重复加载** 多次调用 `System.loadLibrary` 对同一个库不会引发异常,因为 JVM 只允许每个库被加载一次。后续调用会被忽略[^2]。 #### 6. **与 `System.load` 的区别** 虽然两者都可用于加载本地库,但它们的功能有所不同: - `System.loadLibrary` 接受库名字作为参数,由 JVM 自动解析具体路径。 - `System.load` 则需要提供完整的文件路径,适合处理非标准位置的库文件。 #### 7. **高级应用场景** 对于复杂的场景,比如动态获取 SO 文件路径并通过 JNI 层面操作,可以参考如下实现方式: ```java private static final String LIB_NAME = "test-lib"; private void test() { try { BaseDexClassLoader dexPathClassLoader = (BaseDexClassLoader) Binder.class.getClassLoader(); String targetSoAbsolutePath = dexPathClassLoader.findLibrary(LIB_NAME); Log.e("mLogU", "ClassLoader#findLibrary: " + targetSoAbsolutePath); testdlopen(targetSoAbsolutePath); // 动态 dlopen 并执行函数 } catch (Exception e) { Log.e("mLogU", e.toString()); } } private native void testdlopen(String targetSoAbsolutePath); ``` 这段代码演示了如何利用 `BaseDexClassLoader.findLibrary` 查找 SO 文件的具体路径,并传递给 C/C++ 层进行进一步处理[^4]。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值