关于Android studio中使用NDK/JNI环境和入门:http://blog.csdn.net/quan648997767/article/details/64923143

1. C代码回调Java方法的流程

(1) 找到java对应的Class

创建一个char*数组, 然后使用jni.h中提供的FindClass方法获取jclass返回值;

  1. char* classname = "wjy/geridge/com/testndk/jni/JniUtils";
  2. jclass dpclazz = (*env)->FindClass(env, classname);

(2) 找到要调用的方法的methodID

使用jni.h中提供的GetMethodID方法, 获取jmethodID, 传入参数 ①JNIEnv指针 ②Class对象 ③ 方法名 ④方法签名, 在这里方法名和方法签名确定一个方法, 方法签名就是方法的返回值 与 参数的唯一标示;

  1. //参数介绍 : 第二个参数是Class对象, 第三个参数是方法名,第四个参数是方法的签名, 获取到调用的method
  2. 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类型 类型签名
boolean Z
byte B
char C
short S
int I
long J
float F
double D
L全限定类名
数组 [元素类型签名
 
如. long function(int n, String str, int[] arr);
该方法的签名 :(ILjava/lang/String;[I)J
上面的例子中两个参数都是int类型返回值也是int所以是:(II)I
例子中调用了GetMethodID方法去获取add方法的唯一标识,如果add是个静态方法呢?
  1. jmethodID methodID = (*env)->GetStaticMethodID(env, dpclazz,"add", "(II)I");

可以看到获取静态方法需要调用GetStaticMethodID方法,参数与GetMethodID相同

(3) 在C语言中调用相应方法

普通方法 : CallTypeMethod , 其中的Type随着返回值类型的不同而改变;
参数介绍 : ① JNIEnv指针 ②调用该native方法的对象 ③方法的methodID ④⑤... 后面是可变参数, 这些参数是
  1. jobject     (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...);
  2. jobject     (*CallObjectMethodV)(JNIEnv*, jobject, jmethodID, va_list);
  3. jobject     (*CallObjectMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
  4. jboolean    (*CallBooleanMethod)(JNIEnv*, jobject, jmethodID, ...);
  5. jboolean    (*CallBooleanMethodV)(JNIEnv*, jobject, jmethodID, va_list);
  6. jboolean    (*CallBooleanMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
  7. jbyte       (*CallByteMethod)(JNIEnv*, jobject, jmethodID, ...);
  8. jbyte       (*CallByteMethodV)(JNIEnv*, jobject, jmethodID, va_list);
  9. jbyte       (*CallByteMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
  10. jchar       (*CallCharMethod)(JNIEnv*, jobject, jmethodID, ...);
  11. jchar       (*CallCharMethodV)(JNIEnv*, jobject, jmethodID, va_list);
  12. jchar       (*CallCharMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
  13. jshort      (*CallShortMethod)(JNIEnv*, jobject, jmethodID, ...);
  14. jshort      (*CallShortMethodV)(JNIEnv*, jobject, jmethodID, va_list);
  15. jshort      (*CallShortMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
  16. jint        (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
  17. jint        (*CallIntMethodV)(JNIEnv*, jobject, jmethodID, va_list);
  18. jint        (*CallIntMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
  19. jlong       (*CallLongMethod)(JNIEnv*, jobject, jmethodID, ...);
  20. jlong       (*CallLongMethodV)(JNIEnv*, jobject, jmethodID, va_list);
  21. jlong       (*CallLongMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
  22. jfloat      (*CallFloatMethod)(JNIEnv*, jobject, jmethodID, ...);
  23. jfloat      (*CallFloatMethodV)(JNIEnv*, jobject, jmethodID, va_list);
  24. jfloat      (*CallFloatMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
  25. jdouble     (*CallDoubleMethod)(JNIEnv*, jobject, jmethodID, ...);
  26. jdouble     (*CallDoubleMethodV)(JNIEnv*, jobject, jmethodID, va_list);
  27. jdouble     (*CallDoubleMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
  28. void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
  29. void        (*CallVoidMethodV)(JNIEnv*, jobject, jmethodID, va_list);
  30. 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;
  1. jmethodID methodID2 = (*env)->GetStaticMethodID(env, dpclazz,"callNullModth", "()V");

(5) 在C中调用Java带String参数的方法

上面(4)中可以看到String类型的签名是Ljava/lang/String;上面签名规则的例子中也有说到

  1. //C中调用Java的String参数方法
  2. jmethodID methodID3 = (*env)->GetStaticMethodID(env, dpclazz,"callStringMethod", "(Ljava/lang/String;)Ljava/lang/String;");
  3. //添加一个参数
  4. jstring param = (*env)->NewStringUTF(env, "C中调用Java的String参数方法");
  5. jstring result = (*env)->CallStaticObjectMethod(env,clazz,methodID3,param);
  6. //jstring转char*(UTF-8格式)
  7. char *str = (*env)->GetStringUTFChars(env, result,0);
  8. LOGI("callStringMethod=%s",str);

上面例子的参数和返回值都是String,GetStringUTFChars方法是在jni.h中声明可以顺便查看下其他相关方法

(6)完整例子代码

Java代码:
  1. package wjy.geridge.com.testndk.jni;
  2. import android.util.Log;
  3. /**
  4. * Created by zzq on 2017/3/22 0022.
  5. */
  6. public class JniUtils {
  7. public static native int getStringFormc(int x, int y);
  8. public static native int[] getArray(int[] arr);
  9. /**
  10. * 调用带参的Java方法
  11. * @param x
  12. * @param y
  13. * @return
  14. */
  15. public static int add(int x,int y){
  16. return x + y;
  17. }
  18. /**
  19. * 调用JAVA空参数 void返回值的方法
  20. */
  21. public static void callNullMethod(){
  22. Log.e("TAG","C中调用JAVA的void返回值,空参数方法");
  23. }
  24. /**
  25. * 调用JAVA中String参数和返回值的的方法
  26. */
  27. public static String callStringMethod(String str){
  28. return str+"->调用成功";
  29. }
  30. }

在Activity中调用:

  1. package wjy.geridge.com.testndk;
  2. import android.os.Bundle;
  3. import android.support.v7.app.AppCompatActivity;
  4. import android.widget.TextView;
  5. import wjy.geridge.com.testndk.jni.JniUtils;
  6. public class MainActivity extends AppCompatActivity {
  7. private TextView textView;
  8. static {
  9. System.loadLibrary("NdkJniDemo");//之前在build.gradle里面设置的so名字,必须一致
  10. }
  11. @Override
  12. protected void onCreate(Bundle savedInstanceState) {
  13. super.onCreate(savedInstanceState);
  14. setContentView(R.layout.activity_main);
  15. textView = (TextView) findViewById(R.id.textview);
  16. textView.setText(JniUtils.getStringFormc(8,9)+"");
  17. JniUtils.getArray(new int[]{1,2,3,4,15});
  18. }
  19. }

C代码:

  1. #include "string.h"
  2. #include "wjy_geridge_com_testndk_jni_JniUtils.h"
  3. #include <android/log.h>
  4. #define LOG_TAG "System.out"
  5. #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
  6. #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
  7. /*
  8. * Class:     Java_wjy_geridge_com_testndk_jni_JniUtils
  9. * Method:    getStringFormc
  10. * Signature: ()Ljava/lang/String;
  11. */
  12. JNIEXPORT jint JNICALL Java_wjy_geridge_com_testndk_jni_JniUtils_getStringFormc
  13. (JNIEnv *env, jobject clazz,jint x,jint y){
  14. char* classname = "wjy/geridge/com/testndk/jni/JniUtils";
  15. jclass dpclazz = (*env)->FindClass(env, classname);
  16. //这里实现了互相调用,Java中调用了C的getStringFormc方法传递了x,y参数,这里C又调用了Java的add方法将x,y回传回去求和;
  17. jmethodID methodID = (*env)->GetStaticMethodID(env, dpclazz,"add", "(II)I");
  18. LOGI("调用ADD方法的结果count=%d",(*env)->CallStaticIntMethod(env,clazz,methodID,x,y));
  19. //C中调用Java的空返回值方法
  20. jmethodID methodID2 = (*env)->GetStaticMethodID(env, dpclazz,"callNullMethod", "()V");
  21. (*env)->CallStaticVoidMethod(env,clazz,methodID2);
  22. //C中调用Java的String参数方法
  23. jmethodID methodID3 = (*env)->GetStaticMethodID(env, dpclazz,"callStringMethod", "(Ljava/lang/String;)Ljava/lang/String;");
  24. //添加一个参数
  25. jstring param = (*env)->NewStringUTF(env, "C中调用Java的String参数方法");
  26. jstring result = (*env)->CallStaticObjectMethod(env,clazz,methodID3,param);
  27. //jstring转char*
  28. char *str = (*env)->GetStringUTFChars(env, result,0);
  29. LOGI("callStringMethod=%s",str);
  30. return x+y;
  31. }
  32. /**
  33. 打印一个数组
  34. */
  35. jintArray Java_wjy_geridge_com_testndk_jni_JniUtils_getArray
  36. (JNIEnv *env, jobject clazz,jintArray arr){
  37. int len = (*env)->GetArrayLength(env,arr);
  38. //在LogCat中打印出arr的大小
  39. LOGI("the length of array is %d", len);
  40. //如果长度为0, 返回arr
  41. if(len == 0){
  42. return arr;
  43. }
  44. //如果长度大于0, 那么获取数组中的每个元素
  45. jint* p = (*env)->GetIntArrayElements(env, arr, 0);
  46. //打印出数组中每个元素的值
  47. for(int i = 0; i < len; i ++)
  48. {
  49. LOGI("arr[%d] = %d", i, *(p + i));
  50. }
  51. return arr;
  52. }

运行结果:


上面代码写了一个打印数组的例子,我这里为了方便都是用的静态方法。
最后提下上面用到的打印日志的头文件
  1. #include <android/log.h>
  2. #define LOG_TAG "System.out"
  3. #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
  4. #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"
  1. android {
  2. defaultConfig {
  3. ndk {
  4. moduleName "NdkJniDemo"          //生成的so名字
  5. abiFilters "armeabi", "armeabi-v7a", "x86" //输出指定三种abi体系结构下的so库,目前可有可无。
  6. ldLibs "log", "z", "m"
  7. }
  8. }

Android JNI中C和JAVA代码之间的互相调用的更多相关文章

  1. Android应用中如何保护JAVA代码

    Java Classes字节码的反编译太容易了,有很多功能强大的反编译利器可以轻松的将Java字节码 反转为源代码,但是android中普通.apk文件可以轻松的被反编译为Java源代码吗? 答案是当 ...

  2. Android Studio中批量注释 Java代码

    •ctrl+/ 选中需要注释的多行代码,然后按 ctrl + / 实现多行快速注释: 再次按下 ctrl + / 取消注释. •ctrl+shift+/ 选中一行或几行代码,按 ctrl + shif ...

  3. Android JNI中的数据传递

    1.JNI 基本类型 当 Java 代码与本地代码 C/C++ 代码相互调用时,肯定会有参数的传递.两者属于不同的语言,数据类型有差别,此时,JNI 要保证两种语言之间的数据类型和数据空间大小的匹配. ...

  4. ZT ANDROID jni 中的事件回调机制JNIenv的使用 2012-09-10 12:53:01

    ANDROID jni 中的事件回调机制JNIenv的使用 2012-09-10 12:53:01 分类: 嵌入式 android framework 里java调用native,使用JNI机制,ja ...

  5. Jni中C++和Java的参数传递 参数对照

    Jni中C++和Java的参数传递 如何使用JNI的一些基本方法和过程在网上多如牛毛,如果你对Jni不甚了解,不知道Jni是做什么的,如何建立一个基本的jni程序,或许可以参考下面下面这些文章:利用V ...

  6. Jni中C++和Java的参数传递

    Jni中C++和Java的参数传递 如何使用JNI的一些基本方法和过程在网上多如牛毛,如果你对Jni不甚了解,不知道Jni是做什么的,如何建立一个基本的jni程序,或许可以参考下面下面这些文章:利用V ...

  7. Jni中C++和Java的参数传递(转)

    如何使用JNI的一些基本方法和过程在网上多如牛毛,如果你对Jni不甚了解,不知道Jni是做什么的,如何建立一个基本的jni程序,或许可以参考下面下面这些文章:利用VC++6.0实现JNI的最简单的例子 ...

  8. android开发中,在java中怎样使用c提供过来char*

    这个char*假设是一般的字符串的话,作为string传回去就能够了.假设是含有'\0'的buffer,最好作为bytearray传出,由于能够制定copy的length.假设copy到string, ...

  9. [Android Tips] 30.如何在 Android Studio 中一次性格式化所有代码

    在目录上面右击,有 Reformat Code Ctrl + Alt + L 参考 如何在IntelliJ IDEA或Android Studio中一次性格式化所有代码?

随机推荐

  1. mint-ui 关于有时候官网有时候打不开的问题

    难道就我只有这种情况,不知所措的,要不好看看是什么,看到别人说,是你路由的问题,换个路由的可以了,我觉得不是,那试试HTTP协议的问题 换个https试试就可以 这是什么原因呢? http是超文本传输 ...

  2. Ubuntu 安装Chrome

    apt方式安装Chrome 1.添加密匙 wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | sudo apt-key a ...

  3. BZOJ.5068.友好的生物(思路)

    题目链接 \(Description\) 求\[\max\{\sum_{i=1}^{k-1}(C_i*|a_{x,i}-a_{y,i}|)-C_k*|a_{x,k}-a_{y,k}|\}\] \(So ...

  4. Vue项目开发之打包后背景图片路径错误的坑

    在开发vue项目的过程中,使用浏览器进行预览的时候所有图片的路径是没有任何问题的,但是在打包后传到服务器上,在微信端查看背景图片时,background的图片竟然不显示,img标签里的图片却是正常展示 ...

  5. Windows 10原版ISO下载地址(持续更新)

    Windows 10本质上,它们与 Win7.XP 时代的 SP1.SP2.SP3 这样的大型更新版是一样的,只不过微软很蛋疼地为它们起上一个难记地要死的名字,仅此而已.如果你把“一周年更新”看作 S ...

  6. Ubuntu Java7 SDK环境变量配置(转)

    1.去甲骨文官网下载java7 sdk http://www.oracle.com/technetwork/java/javase/downloads/index.html 这里笔者下载了最新的jav ...

  7. 在.net core 2.0中生成exe文件

    .net core 2.0程序默认生成的是一个dll,需要通过dotnet命令来执行他. dotnet ConsoleApp1.dll 这种方式有点类似于java程序.本身这种方式没有什么问题,但在调 ...

  8. UVA 12436 - Rip Van Winkle&#39;s Code(线段树)

    UVA 12436 - Rip Van Winkle's Code option=com_onlinejudge&Itemid=8&page=show_problem&cate ...

  9. Data transfer from GPIO port to RAM buffer using DMA upon receiving a trigger signal on the timer capture input channel.

    Data transfer from GPIO port to RAM buffer using DMA upon receiving a trigger signal on the timer ca ...

  10. Android内存机制分析——堆和栈

    昨天用Gallery做了一个图片浏览选择开机画面的功能,当我加载的图片多了就出现OOM问题.以前也出现过这个问题,那时候并没有深究.这次打算好好分析一下Android的内存机制. 因为我以前是做VC+ ...