jni文件的编译和使用

在RK3288平台的Ubuntu 12.04 64位系统上,为了实现串口功能,采用JNI编程。首先从特定网站下载并安装NDK编译器,然后解压并配置环境变量。接着编写`android.mk`文件和C源码`serialport.c`。通过`ndk-build`命令编译JNI文件,生成库文件`libserial_port.so`。

安装环境:RK3288编译环境搭建时,已经安装好了jdk1.6,使用ubunt12.04 64位OS,为了开发使用串口功能,需要使用JNI编程。

1.下载NDK编译器

    为了能打开android官网,需要使用该网站登陆:http://wear.techbrood.com, 登陆后进入:develop/tools/NDK下载 对应的包:

android-ndk32-r10b-linux-x86_64.tar.bz2

2,解压安装

    解压后参照 android-ndk-r10b/docs/Getting_Started/html/index.html  来进行安装。比如我的root用户下的.bashrc 文件中添加:

 

  #cd  ~/

  # vim .bashrc
  export  PATH=$PATH:/home/wigooe/project/document/android-ndk-r10b/


  #source  .bashrc

3.编写android.mk文件和对应的serialport.c

4.进入JNI文件夹编译该文件

   ndk-build

  编译后会生成Lib文件

root@wigooePc:/home/wigooe/project/document/myserial/jni# ndk-build
Android NDK: WARNING: APP_PLATFORM android-9 is larger than android:minSdkVersion 2 in /home/wigooe/project/document/myserial/AndroidManifest.xml    
[armeabi] Install        : libserial_port.so => libs/armeabi/libserial_port.so


<think> 我们正在讨论的是JNI(Java Native Interface)被编译为静态库(.a文件)后的使用方法。根据用户提供的引用之前的对话,用户已经知道静态库是编译后的归档文件,现在想知道如何在实际的JNI开发中使用这些静态库。 从引用中我们可以看到: - 引用[1]展示了一个demo.c文件,该文件实现了JNI方法,并在其中调用了静态库welcome.a中的welcome方法。 - 引用[2]提到了在Android中JNI调用静态库时可能遇到的静态方法找不到的问题,以及如何在Windows下使用CMakeNDK编译动态库静态库。 - 引用[3]则展示了在Android.mk中如何生成静态库,然后在生成动态库的时候引用这个静态库,并最终生成一个动态库供上层使用。 因此,我们可以总结出JNI编译为静态库后的两种主要使用方式: 1. **直接链接到JNI动态库(.so)中**:这是Android开发中常见的方式。因为Java层通过System.loadLibrary加载的是动态库,所以我们需要将静态库链接到一个动态库中,然后Java层加载这个动态库。 2. **在独立的可执行文件使用**:但这种情况在Android应用开发中较少见,因为Android应用通常通过JNI与本地代码交互,而JNI需要动态库作为桥梁。 在Android开发中,静态库通常不能直接被Java层加载,因为Java层只能加载动态库(.so)。所以,我们需要将静态库链接到动态库中,然后由Java层加载动态库。 下面我们详细说明这两种方式,但重点在第一种,因为它是Android应用开发中的标准做法。 ### 方法1:将静态库链接到JNI动态库中(推荐) 这是Android开发中最常用的方法。步骤包括: 1. 将JNI代码(实现JNI函数的部分)编译成一个动态库(.so)。 2. 在编译这个动态库的过程中,链接你已有的静态库(.a文件)。 3. 在Java代码中加载这个动态库。 #### 代码示例(使用Android.mk) 假设我们有一个静态库`libwelcome.a`,它包含一个函数`welcome()`。我们有一个JNI文件`demo.c`,它实现了JNI函数,并调用了`welcome()`。 **Android.mk**: ```makefile LOCAL_PATH := $(call my-dir) # 第一步:导入预编译的静态库 include $(CLEAR_VARS) LOCAL_MODULE := welcome_prebuilt LOCAL_SRC_FILES := libwelcome.a # 假设静态库文件放在当前目录 include $(PREBUILT_STATIC_LIBRARY) # 第二步:编译动态库,并链接静态库 include $(CLEAR_VARS) LOCAL_MODULE := demo_jni # 生成的动态库名为libdemo_jni.so LOCAL_SRC_FILES := demo.c # JNI文件 LOCAL_STATIC_LIBRARIES := welcome_prebuilt # 链接静态库 include $(BUILD_SHARED_LIBRARY) ``` **demo.c**的内容如引用[1]所示: ```c #include <stdio.h> #include "JT.h" // JNI文件,由javah生成 #include "welcome.h" // 静态库的头文件 JNIEXPORT void JNICALL Java_JT_call(JNIEnv *env, jobject obj) { printf("hello, demo!\n"); welcome(); // 调用静态库中的函数 } ``` 然后,在Java代码中加载动态库: ```java public class JT { static { System.loadLibrary("demo_jni"); // 加载libdemo_jni.so } public native void call(); } ``` #### 使用CMake(现代Android项目推荐) 如果你使用CMake,可以在`CMakeLists.txt`中这样配置: ```cmake cmake_minimum_required(VERSION 3.4.1) # 添加静态库 add_library(welcome STATIC IMPORTED) set_target_properties(welcome PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libwelcome.a) # 创建动态库 add_library(demo_jni SHARED demo.c) target_include_directories(demo_jni PRIVATE ${CMAKE_SOURCE_DIR}/include) # 包含头文件路径 target_link_libraries(demo_jni welcome) # 链接静态库 ``` ### 方法2:将静态库链接到可执行文件(非JNI场景) 在Android中,我们也可以编译一个可执行文件(例如命令行工具),并在其中使用静态库。但这通常不是用于JNI,而是用于独立的本地程序。 **Android.mk示例**: ```makefile LOCAL_PATH := $(call my-dir) # 导入静态库 include $(CLEAR_VARS) LOCAL_MODULE := welcome_prebuilt LOCAL_SRC_FILES := libwelcome.a include $(PREBUILT_STATIC_LIBRARY) # 编译可执行文件 include $(CLEAR_VARS) LOCAL_MODULE := demo_tool LOCAL_SRC_FILES := main.c LOCAL_STATIC_LIBRARIES := welcome_prebuilt include $(BUILD_EXECUTABLE) ``` ### 注意事项 1. **头文件包含**:在使用静态库中的函数时,必须包含相应的头文件(例如`welcome.h`),否则编译器无法识别函数声明。 2. **符号冲突**:如果多个静态库中有相同的符号(函数名或全局变量名),链接时可能会出错。 3. **ABI兼容性**:静态库必须与目标动态库或可执行文件使用相同的ABI(如armeabi-v7a, arm64-v8a等)。 4. **链接顺序**:在链接多个静态库时,需要注意链接顺序,因为链接器是按顺序解析符号的。 ### 常见问题解决 - **静态方法找不到?** 确保在编译静态库时,函数定义正确且没有使用`static`关键字(否则函数为文件内私有,不可见)。确保在链接时,静态库被正确链接,并且函数声明在头文件中被正确包含。 - **未定义引用错误**:通常是因为链接顺序问题或者忘记链接某个静态库。调整链接顺序,或者检查是否所有依赖的静态库都已链接。 ### 总结 JNI编译为静态库后,通常需要被链接到一个动态库中,然后由Java层加载该动态库。在Android.mk或CMake中配置静态库的导入链接即可。 --- §§相关问题§§ 1. 如何在CMake中同时链接多个静态库? 2. 如何解决JNI静态库链接时的符号冲突问题? 3. 如何确保静态库的ABI与目标平台兼容? 4. 在Android Studio中如何配置CMake以使用编译的静态库? 5. 静态库动态库在JNI开发中的优缺点比较? </think> ### JNI静态库使用方法详解 在Android开发中,将JNI代码编译为静态库(`.a`文件)后,需要通过**动态库(`.so`)封装**才能被Java层调用,以下是具体实现方法: --- ### 核心步骤 #### 1. 创建静态库(`.a`) 在`Android.mk`中配置静态库编译: ```makefile # 静态库配置(Android.mk) LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := jni_static # 模块名 LOCAL_SRC_FILES := jni_impl.c # JNI实现文件 include $(BUILD_STATIC_LIBRARY) # 关键指令[^3] ``` 编译后生成`libjni_static.a` --- #### 2. 创建封装动态库(`.so`) 动态库配置中链接静态库: ```makefile # 动态库配置(Android.mk) include $(CLEAR_VARS) LOCAL_MODULE := jni_wrapper # 动态库名称 LOCAL_STATIC_LIBRARIES := jni_static # 链接静态库[^3] LOCAL_LDLIBS := -llog # 依赖系统库 include $(BUILD_SHARED_LIBRARY) ``` 编译后生成`libjni_wrapper.so` --- #### 3. JNI代码实现 静态库中的函数调用示例: ```c // jni_impl.c #include <jni.h> #include "native_helper.h" // 静态库头文件 void Java_com_example_NativeHelper_callStatic(JNIEnv* env, jobject obj) { // 调用静态库内部函数 static_lib_function(); } ``` --- #### 4. Java层加载动态库 ```java public class NativeHelper { static { System.loadLibrary("jni_wrapper"); // 加载封装库[^1] } public native void callStatic(); } ``` --- ### 关键原理 $$ \text{Java层} \xrightarrow{\text{加载}} \text{动态库(.so)} \xRightarrow{\text{链接}} \text{静态库(.a)} $$ 1. **静态库特性** - 仅包含编译后的目标代码(`.o`文件集合) - 无法直接通过`System.loadLibrary()`加载 2. **动态库作用** - 作为Java与静态库的桥梁 - 在链接阶段嵌入静态库代码 - 提供JNI标准的`JNI_OnLoad`入口 --- ### 对比动态库直接实现 | 方案 | 优点 | 缺点 | |---------------------|-------------------------------|-----------------------| | **静态库+动态封装** | 代码复用性强,模块化清晰 | 需要两级构建过程 | | **纯动态库** | 编译流程简单,直接加载 | 复用性差 | --- ### 常见问题解决 1. **符号未找到错误** - 确保静态库头文件在`LOCAL_C_INCLUDES`中声明 - 检查函数是否被`static`修饰(应改为全局可见) 2. **ABI兼容问题** - 静态库需与动态库使用相同的ABI(如armeabi-v7a/arm64-v8a) - 在`Application.mk`中声明`APP_ABI := armeabi-v7a arm64-v8a` 3. **CMake实现方式** ```cmake # CMakeLists.txt add_library(jni_static STATIC jni_impl.c) add_library(jni_wrapper SHARED wrapper.c) target_link_libraries(jni_wrapper jni_static) # 关键链接指令[^2] ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值