Android JNI中C和JAVA代码之间的互相调用
来源:互联网 发布:完美芦荟胶淘宝官网 编辑:程序博客网 时间:2024/05/01 10:35
关于Android studio中使用NDK/JNI环境和入门:http://blog.csdn.net/quan648997767/article/details/64923143
1. C代码回调Java方法的流程
(1) 找到java对应的Class
创建一个char*数组, 然后使用jni.h中提供的FindClass方法获取jclass返回值;char* classname = "wjy/geridge/com/testndk/jni/JniUtils";jclass dpclazz = (*env)->FindClass(env, classname);
(2) 找到要调用的方法的methodID
使用jni.h中提供的GetMethodID方法, 获取jmethodID, 传入参数 ①JNIEnv指针 ②Class对象 ③ 方法名 ④方法签名, 在这里方法名和方法签名确定一个方法, 方法签名就是方法的返回值 与 参数的唯一标示;//参数介绍 : 第二个参数是Class对象, 第三个参数是方法名,第四个参数是方法的签名, 获取到调用的method jmethodID methodID = (*env)->GetMethodID(env, dpclazz,"add", "(II)I");
关于JNI方法签名规则
JNI识别Java方法 : JNI依靠函数名 和 方法签名 识别方法, 函数名是不能唯一识别一个方法的, 因为方法可以重载, 类型签名代表了 参数 和 返回值;
-- 签名规则 : (参数1类型签名参数2类型签名参数3类型签名参数N类型签名...)返回值类型签名, 注意参数列表中没有任何间隔;
Java类型 与 类型签名对照表 : 注意 boolean 与 long 不是大写首字母, 分别是 Z 与 J, 类是L全限定类名, 数组是[元素类型签名;
-- 类的签名规则 :L + 全限定名 + ;三部分, 全限定类名以 / 分割;
Java类型类型签名booleanZbyteBcharCshortSintIlongJfloatFdoubleD类L全限定类名数组[元素类型签名
如. long function(int n, String str, int[] arr);
该方法的签名 :(ILjava/lang/String;[I)J
上面的例子中两个参数都是int类型返回值也是int所以是:(II)I
例子中调用了GetMethodID方法去获取add方法的唯一标识,如果add是个静态方法呢?
jmethodID methodID = (*env)->GetStaticMethodID(env, dpclazz,"add", "(II)I");可以看到获取静态方法需要调用GetStaticMethodID方法,参数与GetMethodID相同
(3) 在C语言中调用相应方法
普通方法 : CallTypeMethod , 其中的Type随着返回值类型的不同而改变;
参数介绍 : ① JNIEnv指针 ②调用该native方法的对象 ③方法的methodID ④⑤... 后面是可变参数, 这些参数是
jobject (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...); jobject (*CallObjectMethodV)(JNIEnv*, jobject, jmethodID, va_list); jobject (*CallObjectMethodA)(JNIEnv*, jobject, jmethodID, jvalue*); jboolean (*CallBooleanMethod)(JNIEnv*, jobject, jmethodID, ...); jboolean (*CallBooleanMethodV)(JNIEnv*, jobject, jmethodID, va_list); jboolean (*CallBooleanMethodA)(JNIEnv*, jobject, jmethodID, jvalue*); jbyte (*CallByteMethod)(JNIEnv*, jobject, jmethodID, ...); jbyte (*CallByteMethodV)(JNIEnv*, jobject, jmethodID, va_list); jbyte (*CallByteMethodA)(JNIEnv*, jobject, jmethodID, jvalue*); jchar (*CallCharMethod)(JNIEnv*, jobject, jmethodID, ...); jchar (*CallCharMethodV)(JNIEnv*, jobject, jmethodID, va_list); jchar (*CallCharMethodA)(JNIEnv*, jobject, jmethodID, jvalue*); jshort (*CallShortMethod)(JNIEnv*, jobject, jmethodID, ...); jshort (*CallShortMethodV)(JNIEnv*, jobject, jmethodID, va_list); jshort (*CallShortMethodA)(JNIEnv*, jobject, jmethodID, jvalue*); jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...); jint (*CallIntMethodV)(JNIEnv*, jobject, jmethodID, va_list); jint (*CallIntMethodA)(JNIEnv*, jobject, jmethodID, jvalue*); jlong (*CallLongMethod)(JNIEnv*, jobject, jmethodID, ...); jlong (*CallLongMethodV)(JNIEnv*, jobject, jmethodID, va_list); jlong (*CallLongMethodA)(JNIEnv*, jobject, jmethodID, jvalue*); jfloat (*CallFloatMethod)(JNIEnv*, jobject, jmethodID, ...); jfloat (*CallFloatMethodV)(JNIEnv*, jobject, jmethodID, va_list); jfloat (*CallFloatMethodA)(JNIEnv*, jobject, jmethodID, jvalue*); jdouble (*CallDoubleMethod)(JNIEnv*, jobject, jmethodID, ...); jdouble (*CallDoubleMethodV)(JNIEnv*, jobject, jmethodID, va_list); jdouble (*CallDoubleMethodA)(JNIEnv*, jobject, jmethodID, jvalue*); void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); void (*CallVoidMethodV)(JNIEnv*, jobject, jmethodID, va_list); void (*CallVoidMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
同样的如果是调用静态方法应该使用对应的CallStaticTypeMethod, 其中的Type随着返回值类型不同而改变;
上面的方法都在jni.h中声明(D:\android-sdk-windows\ndk-bundle\platforms\android-24\arch-arm\usr\include\jni.h)
可以自己去查找
(4) 在C中调用Java的void返回值方法
-- 返回值null, 参数null : void callNullModth() 方法的签名是 "()V", 括号里什么都没有代表参数为null, V代表返回值是void;
-- 返回值int, 参数两个int : int add(int x,int y) 方法的签名是 "(II)I", 括号中II表示两个int类型参数, 右边括号外的I代表返回值是int类型;
-- 返回值null, 参数String : void printString(String s) 方法签名是 "(Ljava/lang/String;)V", 括号中的Ljava/lang/String; 表示参数是String类型, V表示返回值是void;
jmethodID methodID2 = (*env)->GetStaticMethodID(env, dpclazz,"callNullModth", "()V");
(5) 在C中调用Java带String参数的方法
上面(4)中可以看到String类型的签名是Ljava/lang/String;上面签名规则的例子中也有说到//C中调用Java的String参数方法 jmethodID methodID3 = (*env)->GetStaticMethodID(env, dpclazz,"callStringMethod", "(Ljava/lang/String;)Ljava/lang/String;"); //添加一个参数 jstring param = (*env)->NewStringUTF(env, "C中调用Java的String参数方法"); jstring result = (*env)->CallStaticObjectMethod(env,clazz,methodID3,param); //jstring转char*(UTF-8格式) char *str = (*env)->GetStringUTFChars(env, result,0); LOGI("callStringMethod=%s",str);上面例子的参数和返回值都是String,GetStringUTFChars方法是在jni.h中声明可以顺便查看下其他相关方法
(6)完整例子代码
Java代码:
package wjy.geridge.com.testndk.jni;import android.util.Log;/** * Created by zzq on 2017/3/22 0022. */public class JniUtils { public static native int getStringFormc(int x, int y); public static native int[] getArray(int[] arr); /** * 调用带参的Java方法 * @param x * @param y * @return */ public static int add(int x,int y){ return x + y; } /** * 调用JAVA空参数 void返回值的方法 */ public static void callNullMethod(){ Log.e("TAG","C中调用JAVA的void返回值,空参数方法"); } /** * 调用JAVA中String参数和返回值的的方法 */ public static String callStringMethod(String str){ return str+"->调用成功"; }}在Activity中调用:
package wjy.geridge.com.testndk;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.widget.TextView;import wjy.geridge.com.testndk.jni.JniUtils;public class MainActivity extends AppCompatActivity { private TextView textView; static { System.loadLibrary("NdkJniDemo");//之前在build.gradle里面设置的so名字,必须一致 } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView) findViewById(R.id.textview); textView.setText(JniUtils.getStringFormc(8,9)+""); JniUtils.getArray(new int[]{1,2,3,4,15}); }}
C代码:
#include "string.h"#include "wjy_geridge_com_testndk_jni_JniUtils.h"#include <android/log.h>#define LOG_TAG "System.out"#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)/** Class: Java_wjy_geridge_com_testndk_jni_JniUtils* Method: getStringFormc* Signature: ()Ljava/lang/String;*/JNIEXPORT jint JNICALL Java_wjy_geridge_com_testndk_jni_JniUtils_getStringFormc (JNIEnv *env, jobject clazz,jint x,jint y){ char* classname = "wjy/geridge/com/testndk/jni/JniUtils"; jclass dpclazz = (*env)->FindClass(env, classname); //这里实现了互相调用,Java中调用了C的getStringFormc方法传递了x,y参数,这里C又调用了Java的add方法将x,y回传回去求和; jmethodID methodID = (*env)->GetStaticMethodID(env, dpclazz,"add", "(II)I"); LOGI("调用ADD方法的结果count=%d",(*env)->CallStaticIntMethod(env,clazz,methodID,x,y)); //C中调用Java的空返回值方法 jmethodID methodID2 = (*env)->GetStaticMethodID(env, dpclazz,"callNullMethod", "()V"); (*env)->CallStaticVoidMethod(env,clazz,methodID2); //C中调用Java的String参数方法 jmethodID methodID3 = (*env)->GetStaticMethodID(env, dpclazz,"callStringMethod", "(Ljava/lang/String;)Ljava/lang/String;"); //添加一个参数 jstring param = (*env)->NewStringUTF(env, "C中调用Java的String参数方法"); jstring result = (*env)->CallStaticObjectMethod(env,clazz,methodID3,param); //jstring转char* char *str = (*env)->GetStringUTFChars(env, result,0); LOGI("callStringMethod=%s",str); return x+y;}/** 打印一个数组*/jintArray Java_wjy_geridge_com_testndk_jni_JniUtils_getArray (JNIEnv *env, jobject clazz,jintArray arr){ int len = (*env)->GetArrayLength(env,arr); //在LogCat中打印出arr的大小 LOGI("the length of array is %d", len); //如果长度为0, 返回arr if(len == 0){ return arr; } //如果长度大于0, 那么获取数组中的每个元素 jint* p = (*env)->GetIntArrayElements(env, arr, 0); //打印出数组中每个元素的值 for(int i = 0; i < len; i ++) { LOGI("arr[%d] = %d", i, *(p + i)); } return arr;}运行结果:
上面代码写了一个打印数组的例子,我这里为了方便都是用的静态方法。
最后提下上面用到的打印日志的头文件
#include <android/log.h>#define LOG_TAG "System.out"#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
头文件介绍 : log.h 是关于调用 LogCat日志文件;
-- log.h头文件路径 : android-ndk-r9c\platforms\android-9\arch-arm\usr\include\android\log.h;
-- 主要方法 : __android_log_write, 下面有该方法的解析, 传入参数 日志等级 日志标签 日志内容;
-- 宏定义 : __android_log_write 方法太麻烦, 这里做出一个映射, LOGD(...) 输出debug级别的日志, LOGI(...) 输出Info级别的日志;
--LogCat日志级别 : verbose < debug < info < warn < error < assert;
要使用log日志还需要在app目录下的build.gradle中配置如下代码: ldLibs "log", "z", "m"
android { defaultConfig { ndk { moduleName "NdkJniDemo" //生成的so名字 abiFilters "armeabi", "armeabi-v7a", "x86" //输出指定三种abi体系结构下的so库,目前可有可无。 ldLibs "log", "z", "m" } }
还在继续研究中....
0 0
- Android JNI中C和JAVA代码之间的互相调用
- Android Ndk中C与JAVA之间的互相调用
- Android JNI之Java和C互相调用
- Js代码和Java代码之间的互相调用了
- 汇编和C代码之间的互相调用
- JNI JAVA与C的互相调用
- JNI学习(一)(c和java层对象互相调用)
- android jni中C++与java互相调用小结
- Jni 参数传递与操作——(C/C++ 代码与 java 代码的互相调用)
- Android WebView中javascript和java的互相调用
- Android中java和javascript方法的互相调用示例
- 安卓学习笔记---JNI实现Java与C之间进行互相调用
- android中实现JavaScript与Java之间实现互相调用
- android Java与JNI层互相调用
- Android JNI 在C中调用Java(包括自定义的Java方法和Log)
- JNI--java和C++互相调用
- C/C++与Java之间的互相调用
- C和C++之间库的互相调用
- QML自动循环轮播图
- Android AlarmManager使用
- ASP.Net MVC3安全升级导致程序集从3.0.0.0变为3.0.0.1
- vue.js加载页面出现闪烁问题的解决
- 运维角度浅谈MySQL数据库优化
- Android JNI中C和JAVA代码之间的互相调用
- 一张图看完语法知识
- 线程与线程类
- GPS工作原理
- JAVA相关基础知识
- [整理]PHP/HTML混写的四种方式
- 1040. 有几个PAT(25)----暴力
- Python之动态检测编码chardet
- 23.Merge k Sorted Lists