JNI C反射调用java方法
前面记录了调用C的学习笔记,现在来记录一下C反射调用Java的笔记。
JNI开发学习之调用C方法
Android开发中调用一个类中没有公开的方法,可以进行反射调用,而JNI开发中C调用java的方法也是反射调用。
C代码回调Java方法步骤:
①获取字节码对象(jclass (FindClass)(JNIEnv, const char*);)
②通过字节码对象找到方法对象(jmethodID (GetMethodID)(JNIEnv, jclass, const char, const char);)
④通过对象调用方法,可以调用空参数方法,也可以调用有参数方法,并且将参数通过调用的方法传入(void (CallVoidMethod)(JNIEnv, jobject, jmethodID, ...);)
首先,也是按照前面的步骤新建一个 import C++ 工程,新建ccalljava.c 和一个JNI.java文件(别忘了修改CMakeLists.txt对应C方法的名字和路径)
JNI.java中编写本地方法:
//C调用java空方法 public native void callbackmethod(); //C调用java中的带两个int参数的方法 public native void callbackIntmethod(); //C调用java中参数为string的方法 public native void callbackStringmethod(); //C调用java中静态方法 public native void callStaticmethod();
并且编写被C反调的java方法:
//C调用java空方法 public void helloFromJava(){ Toast.makeText(context, "C调用了java的空方法",Toast.LENGTH_SHORT ).show();} //C调用java中的带两个int参数的方法 public int add(int x,int y) { return x+y;} //C调用java中参数为string的方法 public void printString(String s){ Toast.makeText(context, s, Toast.LENGTH_SHORT).show();} //C调用java中静态方法 public static void staticmethod(String s){ Log.w("毛麒添",s+",我是被C调用的静态方法");}
下面来编写ccalljava.c中的C方法
/**C函数反射调用java中的空方法 */ JNIEXPORT void JNICALLJava_com_mao_ccalljava_JNI_callbackmethod(JNIEnv *env, jobject object) { jclass jclazz = (*env)->FindClass(env, "com/mao/ccalljava/JNI"); jmethodID methodID = (*env)->GetMethodID(env, jclazz, "helloFromJava", "()V"); (*env)->CallVoidMethod(env,object,methodID);} /** 调用java中Int方法 */ JNIEXPORT void JNICALLJava_com_mao_ccalljava_JNI_callbackIntmethod(JNIEnv *env, jobject object) { jclass clzz=(*env)->FindClass(env,"com/mao/ccalljava/JNI"); jmethodID methodID=(*env)->GetMethodID(env,clzz,"add","(II)I"); int result=(*env)->CallIntMethod(env,object,methodID,3,4); //logcat 打印相加返回的结果 LOGD("RESLUT = %d",result); } /** 调用java中String方法 */ JNIEXPORT void JNICALLJava_com_mao_ccalljava_JNI_callbackStringmethod(JNIEnv *env, jobject object) { //先获取字节码对象 jclass (*FindClass)(JNIEnv*, const char*); jclass clzz=(*env)->FindClass(env,"com/mao/ccalljava/JNI"); //获取method对象 jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); jmethodID methodID=(*env)->GetMethodID(env,clzz,"printString","(Ljava/lang/String;)V"); //将要传递的字符串先转换成jstring类型 ,然后在传递给java方法 int result=(*env)->NewStringUTF(env,"hello form C/C++ "); (*env)->CallVoidMethod(env,object,methodID,result); } /** 调用Java中的静态方 */ JNIEXPORT void JNICALLJava_com_mao_ccalljava_JNI_callStaticmethod(JNIEnv *env, jobject instance) { jclass clzz=(*env)->FindClass(env,"com/mao/ccalljava/JNI"); jmethodID methodID=(*env)->GetStaticMethodID(env,clzz,"staticmethod","(Ljava/lang/String;)V"); jstring str = (*env)->NewStringUTF(env, "C调用java"); (*env)->CallStaticVoidMethod(env,clzz,methodID,str); }
通过字节码对象找到方法对象,该方法中的第四个参数是方法签名
jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
获取方法签名的方法是进入工程目的 ..../build/classes/debug 进入控制台,
输入命令 javap -s 要获取方法的路径(例如本例 javap -s com.mao.ccalljava.JNI)
上面步骤二中提到的没有生命周期的解决方法:
报空指针,主要就是没上下文环境,反射调用的方法是new出来的,也会没有生命周期.这时候就可以将本地方法和调用的方法都放在同一个类中,没有上下文环境就在创建方法的时候在构造方法中接收一个。
private Context context; public JNI(Context context){ this.context=context; }
最后,别忘了添加在JNI.java中添加动态链接库文件(布局和MianActiivty中逻辑比较简单,这里
static { System.loadLibrary("ccalljava"); }
在gradle 配置一些处理器架构
externalNativeBuild { cmake { cppFlags "" // Clang是一个C语言、Objective-C、C++语言的轻量级编译器。 arguments "-DANDROID_TOOLCHAIN=clang" // 生成.so库的目标平台 abiFilters "armeabi-v7a" , "armeabi" ,"x86" } }
接下来在工程编译通过后可以该目录下找到不同处理器架构的动态链接库文件
最后,上几张运行成功的截图:
例子源码地址:https://github.com/maoqitian/CcallJava
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
2015-03-27 在老项目中使用Gradle:更改默认目录结构