Mac上JNI入门

入手Mac不久
命令环境设置、文件权限修改 都不熟
这里记录下实现hello-jni的步骤,方便以后查阅

一、NDK下载

当前AndroidStudio中已有集成:android-sdk-macosx/ndk-bundle
没有点此下载官方最新版

二、ndk-build环境配置

跟Window一样,Mac也需要设置环境
需要在bash_profile文件中进行设置:

  1. pico .bash_profile进入命令行(关闭:先 control+x 再 y)
     或者open .bash_profile打开文件
  2. 添加命令:
    export NDK_ROOT=/Users/LuckyTurbo/Documents/android-sdk-macosx/ndk-bundle
    export PATH= PATH: NDK_ROOT/
  3. 特别注意:source .bash_profile刷新刚配置的环境
    开始设置了好多遍,都无效,蛋疼死了
  4. 最后输入ndk-build,验证是否配制成功
export NDK_ROOT=/Users/LuckyTurbo/Documents/android-sdk-macosx/ndk-bundle
export PATH=$PATH:$NDK_ROOT/

三、谷歌JNI Demo下载

四、JNI的实现流程

这里写图片描述

  1. *.java -> *.class 使用Eclipse直接编译,会在/bin/classes中
  2. *.class -> *.h 使用java命令:
// javah命令介绍
javah -classpath <path> :从中加载类的路径
javah -d <dir> :输出目录

 
 (1) 创建jni目录
 (2)进入到当前项目目录下

javah -classpath bin/classes:somtime/android.jar  -d jni com.example.jnitest.MainActivity

注意:

用mac或linux系统:要写成javah -classpath bin/classes:/home/xxx/android-sdk-linux/platforms/android-21/android.jar -d jni com.example.hellondk.MainActivity 注意是bin/classes冒号不是分号,*nix系的分隔符是冒号不是分号,如设置环境变量一样,不然会出现以下错误: 错误: 未指定类 bash: /home/xxx/android-sdk-linux/platforms/android-21/android.jar: 权限不够

window:将classes后面的冒号改为分号

 这里顺便讲下Mac如何修改文件的权限:

chmod a+x ./android.jar

 
3. *.h -> *.c 直接创建*.c文件

#include<stdio.h>
#include<stdlib.h>
#include "生成文件.h"

JNIEXPORT jstring JNICALL Java_com_xxx_xxx
  (JNIEnv * env, jclass jclass){
    return (*env)->NewStringUTF(env, "I'm from JNI!");
}

 
4. *.c -> *.so :
这里需要将讲下Android.mk文件:

// 生成的module名,后面java文件中加载so文件,用的就是这个名称
LOCAL_MODULE := hello-jin        
// 编译的c文件
LOCAL_SRC_FILES := hello-jni.c

再说下Application.mk:

// 不写Application.mk文件,或者此文件为空,默认只会编译出armeabi
// 如果需要编译出所有平台的文件,如下:
APP_ABI := all

最后java环境中

// 加载so文件
static{
        System.loadLibrary("hello-jin");
}

交叉编译:就是在一个平台上生成另一个平台上的可执行代码

五、JNI交互处理、Eclipse环境配置

由于c部分代码不熟悉,这里提供视频讲解地址

<think>好的,用户问如何编写JNI接口,我需要详细回答这个问题。首先,我得回忆一下JNI的基本流程和步骤。用户可能对Java和C/C++交互不太熟悉,所以得从基础讲起。 首先,应该分步骤说明。第一步是编写带有native方法的Java类。这部分需要给出示例代码,让用户明白如何声明native方法。然后,生成对应的C/C++头文件,使用javah工具,或者现代方法用javac -h。这里需要注意JDK版本的不同,因为从JDK8开始推荐用javac -h代替javah。 接下来是实现C/C++代码。这里需要解释JNIEXPORT和JNICALL的作用,以及如何将Java的native方法与C函数关联起来。参数JNIEnv和jobject的意义也需要说明,可能还要提到如何操作Java对象,比如字符串转换或者访问字段。 然后是如何编译生成的.so或.dll文件。这里需要给出具体的编译命令,比如用gcc生成共享库,并指定正确的包含路径和链接参数。可能还要提到不同平台下的差异,比如Windows用dll,Linux用so,Mac用dylib。 之后是加载库和运行Java程序。需要提醒用户使用System.loadLibrary或者绝对路径加载,以及运行时的注意事项,比如类路径和库路径的设置。 可能还需要补充一些高级内容,比如异常处理、访问Java字段和方法,以及性能优化建议。这些虽然不是入门必须,但能帮助用户避免常见问题。 然后要检查是否覆盖了所有关键步骤,有没有遗漏的地方。比如,生成头文件的方法是否有两种方式,是否提到了JDK版本的影响。另外,示例代码是否正确,特别是包名和类名是否一致,函数签名是否正确。 用户可能遇到的常见问题,比如找不到符号链接、库路径问题、参数类型转换错误等,可以在相关问题中提到。这样用户在实际操作时遇到问题可以参考这些相关问题。 最后,确保回答结构清晰,步骤明确,示例代码正确,并且相关问题覆盖了可能的后续疑问。比如如何调试JNI代码,如何处理Java和C之间的数据类型转换,以及如何避免内存泄漏等。</think>编写 JNI(Java Native Interface)接口允许 Java 代码调用 C/C++ 编写的本地方法。以下是完整步骤和示例代码: --- ### 1. **编写 Java 类(声明 native 方法)** ```java // HelloJNI.java public class HelloJNI { // 声明 native 方法 public native void sayHello(); // 加载本地库(Windows 下为 HelloJNI.dll,Linux 下为 libHelloJNI.so) static { System.loadLibrary("HelloJNI"); } public static void main(String[] args) { new HelloJNI().sayHello(); // 调用本地方法 } } ``` --- ### 2. **生成 C/C++ 头文件** #### 方法一:使用 `javac -h`(推荐,JDK 8+) ```bash javac -h . HelloJNI.java ``` 生成的头文件 `HelloJNI.h` 内容如下: ```c /* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class HelloJNI */ #ifndef _Included_HelloJNI #define _Included_HelloJNI #ifdef __cplusplus extern "C" { #endif /* * Class: HelloJNI * Method: sayHello * Signature: ()V */ JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif ``` #### 方法二:使用 `javah`(旧版 JDK) ```bash javah -jni HelloJNI ``` --- ### 3. **实现 C/C++ 代码** 根据头文件实现本地方法: ```c // HelloJNI.c #include <jni.h> #include <stdio.h> #include "HelloJNI.h" JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *env, jobject obj) { printf("Hello from C!\n"); } ``` --- ### 4. **编译生成共享库** #### Linux/macOS: ```bash # 编译为共享库 gcc -shared -fpic -I"${JAVA_HOME}/include" -I"${JAVA_HOME}/include/linux" HelloJNI.c -o libHelloJNI.so ``` #### Windows: ```bash # 使用 MinGW 编译为 DLL gcc -shared -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" HelloJNI.c -o HelloJNI.dll ``` --- ### 5. **运行 Java 程序** ```bash # 设置库路径并运行 java -Djava.library.path=. HelloJNI ``` 输出结果: ``` Hello from C! ``` --- ### 6. **JNI 关键概念** #### (1) **函数签名规则** - 函数名格式:`Java_<类全限定名>_<方法名>` - 类名用下划线替换点号(如 `com_example_MyClass`) - 参数类型映射: - `JNIEnv*`:JNI 环境指针,用于调用 JNI 函数 - `jobject`:当前对象(非静态方法)或类(静态方法) #### (2) **数据类型转换** - Java 到 C 的类型映射: | Java 类型 | JNI 类型 | C 类型 | |----------|----------|--------| | boolean | jboolean | uint8_t | | int | jint | int32_t | | String | jstring | char* (需转换) | 示例:处理字符串参数 ```c JNIEXPORT void JNICALL Java_HelloJNI_printMessage(JNIEnv *env, jobject obj, jstring msg) { const char *str = (*env)->GetStringUTFChars(env, msg, NULL); printf("Message: %s\n", str); (*env)->ReleaseStringUTFChars(env, msg, str); } ``` #### (3) **访问 Java 字段和方法** - 访问字段: ```c jfieldID fid = (*env)->GetFieldID(env, cls, "fieldName", "fieldSignature"); jint value = (*env)->GetIntField(env, obj, fid); ``` - 调用方法: ```c jmethodID mid = (*env)->GetMethodID(env, cls, "methodName", "(paramTypes)returnType"); (*env)->CallVoidMethod(env, obj, mid); ``` --- ### 7. **常见错误及解决** 1. **`UnsatisfiedLinkError`** - 原因:库未找到或函数签名不匹配 - 解决: - 检查库路径(`-Djava.library.path`) - 使用 `nm libHelloJNI.so | grep sayHello` 验证符号存在 2. **类型转换错误** - 使用 `GetStringUTFChars` 和 `ReleaseStringUTFChars` 成对操作字符串 3. **跨平台兼容性** - Windows 使用 `__declspec(dllexport)`,Linux/macOS 使用 `-fPIC` --- ### 完整编译命令示例(Linux) ```bash # 1. 编译 Java javac HelloJNI.java # 2. 生成头文件 javac -h . HelloJNI.java # 3. 编译共享库 gcc -shared -fpic -I"${JAVA_HOME}/include" -I"${JAVA_HOME}/include/linux" HelloJNI.c -o libHelloJNI.so # 4. 运行程序 java -Djava.library.path=. HelloJNI ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值