关于so文件的编写
编译So文件的过程文章:https://blog.csdn.net/carson_ho/article/details/73250163
原理:利用 RegisterNatives 方法来注册 java 方法与 JNI 函数的一一对应关系
这里实现一个计算功能的代码!
实现流程:
1、利用结构体 JNINativeMethod 数组记录 java 方法与 JNI 函数的对应关系
JNINativeMethod的结构体:
typedef struct { const char* name; const char* signature; void* fnPtr; } JNINativeMethod;
第一个变量name是Java中函数的名字
第二个变量signature是用来字符串是描述了函数的参数和返回值
第三个变量fnPtr是函数指针 指向C函数
第二个signature参数需要讲下,因为我在写这篇文章的时候不懂(这些也会在smali中看到)
举个例(实际上这些字符是与函数的参数类型一一对应的):
"()V" "()" 中的字符表示参数,后面的则代表返回值。例如"()V" 就表示void Func(); "(II)V" 那么"(II)V" 表示 void Func(int, int); "(Ljava/lang/String;Ljava/lang/String;)V" 那么就表示 void Func(String, String),前面的 L表示类的实例
具体的对应关系:
字符 Java类型 C类型
V void void Z jboolean boolean I jint int J jlong long D jdouble double F jfloat float B jbyte byte C jchar char S jshort short
数组则以"["开始,用两个字符表示
[I jintArray int[] [F jfloatArray float[] [B jbyteArray byte[] [C jcharArray char[] [S jshortArray short[] [D jdoubleArray double[] [J jlongArray long[] [Z jbooleanArray boolean[]
如果JAVA函数位于一个嵌入类,则用$作为类名间的分隔符
例如 "(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z"
结尾返回值为布尔值
这里注册的有加减乘除4个函数,如下实现:
JNINativeMethod nativeMethod[] = { //java层的方法名;Java层的签名;指针指向C的函数 {"addTest","(FF)F",(void*)addTest}, // 那么这里的意思 参数两个float类型 返回值为float {"subTest","(FF)F",(void*)subTest}, {"mulTest","(FF)F",(void*)mulTest}, {"divTest","(FF)F",(void*)divTest} };
2、实现 JNI_OnLoad 方法,在加载动态库后,执行动态注册
JNIEXPORT jint JNI_OnLoad(JavaVm* vm, void* reserved)
个人理解:可以理解为DllMain入口, 在System.loadLibrary(xxx)函数时被调用
3、调用 FindClass 方法,获取 java 对象
jclass _jclass = (*env)->FindClass(env, "com/example/myjnitest/MainActivity");
4、调用 RegisterNatives 方法,传入 java 对象,以及 JNINativeMethod 数组,以及注册数目完成注册
if((*env)->RegisterNatives(env, _jclass, nativeMethod, sizeof(nativeMethod)/sizeof(nativeMethod[0]))!=JNI_OK){ return JNI_ERR; }
坑点:jni注册的方法,一定要在java层进行声明,否则会报错,这尼玛巨坑。。。
计算器的实现:
.c文件:
#include <com_example_myjnitest_MainActivity.h> jfloat addC(JNIEnv* env, jobject obj,jfloat a, jfloat b){ return a+b; } jfloat subC(JNIEnv* env,jobject obj,jfloat a,jfloat b){ return a-b; } jfloat mulC(JNIEnv* env,jobject obj,jfloat a,jfloat b){ return a*b; } jfloat divC(JNIEnv* env,jobject obj,jfloat a,jfloat b){ return a/b; } static JNINativeMethod nativeMethod[] = { //java层的方法名;Java层的签名;指针指向C的函数 {"addTest","(FF)F",(void*)addC},// 那么这里的意思 参数两个float类型 返回值为float {"subTest","(FF)F",(void*)subC}, {"mulTest","(FF)F",(void*)mulC}, {"divTest","(FF)F",(void*)divC} }; JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){ // 可以直接理解为dll的 dllmain 在System.loadLibrary(xxx)函数时被调用 JNIEnv* env; //从JavaVM获取JNIEnv,一般使用1.4的版本,若未提供JNI_OnLoad()函数,VM会默认该使用最老的JNI 1.1版 if((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_4)!=JNI_OK){ return JNI_ERR; } //在MainActivity中进行注册Native数组方法 jclass _jclass = (*env)->FindClass(env, "com/example/myjnitest/MainActivity"); if((*env)->RegisterNatives(env, _jclass, nativeMethod, sizeof(nativeMethod)/sizeof(nativeMethod[0]))!=JNI_OK){ return JNI_ERR; } return JNI_VERSION_1_4; //必须返回该值 }
头文件
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_example_myjnitest_MainActivity */ #ifndef _Included_com_example_myjnitest_MainActivity #define _Included_com_example_myjnitest_MainActivity #ifdef __cplusplus extern "C" { #endif /* * Class: com_example_myjnitest_MainActivity * Method: getFirstJniText * Signature: ()Ljava/lang/String; */ jfloat addTest(JNIEnv* env,jobject obj,jfloat a,jfloat b); jfloat subTest(JNIEnv* env,jobject obj,jfloat a,jfloat b); jfloat mulTest(JNIEnv* env,jobject obj,jfloat a,jfloat b); jfloat divTest(JNIEnv* env,jobject obj,jfloat a,jfloat b); #ifdef __cplusplus } #endif #endif
MainActivity:
package com.example.myjnitest; import android.app.Activity; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.Toast; public class MainActivity extends Activity { static{ System.loadLibrary("com_example_myjnitest_MainActivity"); } public native float addTest(float a, float b); // 声明native方法 public native float subTest(float a, float b); // 声明native方法 public native float mulTest(float a, float b); // 声明native方法 public native float divTest(float a, float b); // 声明native方法 Button btn01; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn01 = (Button) findViewById(R.id.button1); btn01.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View arg0) { // TODO Auto-generated method stub float a = addTest(Float.parseFloat("1.1".toString()), Float.parseFloat("2.2".toString())); Toast.makeText(getApplicationContext(), "" + a, Toast.LENGTH_SHORT).show(); } }); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY