关于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. 纯CSS3实现蜡烛(冒烟)效果

    1. 闲来无事时在网上看一些前辈的博客文章,自己尝试了一下.学习到最重要的一点就是box-shadow的叠加使用,受益非线.先上一下效果图: 其中有以下重要的几点: 1. 蜡烛底座的border-ra ...

  2. 关于XML解析中的CDATA的简单介绍

    所有 XML 文档中的文本均会被解析器解析. 只有 CDATA 区段(CDATA section)中的文本会被解析器忽略. PCDATA PCDATA 指的是被解析的字符数据(Parsed Chara ...

  3. 网页图表Highcharts实践教程标之添加题副标题版权信息

    网页图表Highcharts实践教程标之添加题副标题版权信息 Highcharts辅助元素 辅助元素图表的非必要元素,如标题.版权信息.标签.载入动态.它们不和图表数据发生关联,只是额外说明一些基本信 ...

  4. codevs 1077 多源最短路

    题目描述 Description 已知n个点(n<=100),给你n*n的方阵,a[i,j]表示从第i个点到第j个点的直接距离. 现在有Q个询问,每个询问两个正整数,a和b,让你求a到b之间的最 ...

  5. Loj10154 选课

    试题描述: 大学实行学分制.每门课程都有一定的学分,学生只要选修了这门课并通过考核就能获得相应学分.学生最后的学分是他选修各门课的学分总和.每个学生都要选择规定数量的课程.其中有些课程可以直接选修,有 ...

  6. c# RSA 加密解密 java.net公钥私钥转换 要解密的模块大于128字节

    有一个和接口对接的任务,对方使用的是java,我方使用的是c#,接口加密类型为RSA,公钥加密私钥解密. 然后就是解决各种问题. 1.转换对方的密钥字符串 由于c#里面需要使用的是xml各式的密钥字符 ...

  7. [原创]Linux下网络性能测试Netperf工具介绍及安装

    [原创]Linux下网络性能测试Netperf工具介绍及安装 1 官方网站 http://www.netperf.org/netperf/ 2 Netperf介绍 Netperf是一种网络性能的测试工 ...

  8. 用.Net如何访问Linux下目录

    很多Windows下的应用需要访问和监控Linux下的目录,本文便介绍如何实现. 只需要搭建配置samba服务,即可将Linux下的目录变得如同Windows下共享可写. 1.服务查询 默认情况下,L ...

  9. Optimizing Oracle RAC

    Oracle Real Application Clusters (RAC) databases form an increasing proportion of Oracle database sy ...

  10. WCF技术我们应该如何以正确的方式去学习掌握

    一.WCF技术我该如何学习? 阿笨的回答是:作为初学者的我们,那么请跟着阿笨一起玩WCF吧,阿笨将带领大家如何以正确的姿势去掌握WCF技术.由于WCF技术知识点太多了,就纯基础概念性知识都可以单独出一 ...