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);)

③通过字节码文件创建一个object对象(该方法可选,方法中已经传递一个object,如果需要调用的方法与本地方法不在同一个文件夹则需要新创建object(jobject (AllocObject)(JNIEnv, jclass);),如果需要反射调用的java方法与本地方法不在同一个类中,需要创建该方法,但是如果是这样,并且需要跟新UI操作,例如打印一个Toast 会报空指针异常,因为这时候调用的方法只是一个方法,没有actiivty的生命周期。(下面有解决方案))

④通过对象调用方法,可以调用空参数方法,也可以调用有参数方法,并且将参数通过调用的方法传入(void (CallVoidMethod)(JNIEnv, jobject, jmethodID, ...);)

首先,也是按照前面的步骤新建一个 import C++ 工程,新建ccalljava.c 和一个JNI.java文件(别忘了修改CMakeLists.txt对应C方法的名字和路径)

project.png

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)

project—获取方法签名目录.png
方法签名.png

上面步骤二中提到的没有生命周期的解决方法:
报空指针,主要就是没上下文环境,反射调用的方法是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"    
        }
}

接下来在工程编译通过后可以该目录下找到不同处理器架构的动态链接库文件

不同架构动态链接库文件.png

最后最后,上几张运行成功的截图:

反调java String方法.png
c调java空方法.png
调用int和静态方法在logcat中打印.png

如果有错我没发现的大家可以帮我指出来,大家一起进步。
例子源码地址:https://github.com/maoqitian/CcallJava

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容