• Android JNI中C和JAVA代码之间的互相调用


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

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

    (1) 找到java对应的Class

    创建一个char*数组, 然后使用jni.h中提供的FindClass方法获取jclass返回值;
    [cpp] view plain copy
     
    1. char* classname = "wjy/geridge/com/testndk/jni/JniUtils";  
    2. jclass dpclazz = (*env)->FindClass(env, classname);  

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

    使用jni.h中提供的GetMethodID方法, 获取jmethodID, 传入参数 ①JNIEnv指针 ②Class对象 ③ 方法名 ④方法签名, 在这里方法名和方法签名确定一个方法, 方法签名就是方法的返回值 与 参数的唯一标示;
    [cpp] view plain copy
     
    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是个静态方法呢?
    [cpp] view plain copy
     
    1. jmethodID methodID = (*env)->GetStaticMethodID(env, dpclazz,"add", "(II)I");  
    可以看到获取静态方法需要调用GetStaticMethodID方法,参数与GetMethodID相同

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

    普通方法 : CallTypeMethod , 其中的Type随着返回值类型的不同而改变;
    参数介绍 : ① JNIEnv指针 ②调用该native方法的对象 ③方法的methodID ④⑤... 后面是可变参数, 这些参数是
    [cpp] view plain copy
     
    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 dk-bundleplatformsandroid-24arch-armusrincludejni.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;
    [cpp] view plain copy
     
    1. jmethodID methodID2 = (*env)->GetStaticMethodID(env, dpclazz,"callNullModth", "()V");  

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

    上面(4)中可以看到String类型的签名是Ljava/lang/String;上面签名规则的例子中也有说到
    [cpp] view plain copy
     
    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代码:
    [cpp] view plain copy
     
    1. package wjy.geridge.com.testndk.jni;  
    2.   
    3. import android.util.Log;  
    4.   
    5. /** 
    6.  * Created by zzq on 2017/3/22 0022. 
    7.  */  
    8.   
    9. public class JniUtils {  
    10.     public static native int getStringFormc(int x, int y);  
    11.     public static native int[] getArray(int[] arr);  
    12.     /** 
    13.      * 调用带参的Java方法 
    14.      * @param x 
    15.      * @param y 
    16.      * @return 
    17.      */  
    18.     public static int add(int x,int y){  
    19.         return x + y;  
    20.     }  
    21.   
    22.     /** 
    23.      * 调用JAVA空参数 void返回值的方法 
    24.      */  
    25.     public static void callNullMethod(){  
    26.         Log.e("TAG","C中调用JAVA的void返回值,空参数方法");  
    27.     }  
    28.     /** 
    29.      * 调用JAVA中String参数和返回值的的方法 
    30.      */  
    31.     public static String callStringMethod(String str){  
    32.        return str+"->调用成功";  
    33.     }  
    34. }  
    在Activity中调用:
    [cpp] view plain copy
     
    1. package wjy.geridge.com.testndk;  
    2.   
    3. import android.os.Bundle;  
    4. import android.support.v7.app.AppCompatActivity;  
    5. import android.widget.TextView;  
    6.   
    7. import wjy.geridge.com.testndk.jni.JniUtils;  
    8.   
    9. public class MainActivity extends AppCompatActivity {  
    10.   
    11.     private TextView textView;  
    12.   
    13.     static {  
    14.         System.loadLibrary("NdkJniDemo");//之前在build.gradle里面设置的so名字,必须一致  
    15.     }  
    16.   
    17.     @Override  
    18.     protected void onCreate(Bundle savedInstanceState) {  
    19.         super.onCreate(savedInstanceState);  
    20.         setContentView(R.layout.activity_main);  
    21.         textView = (TextView) findViewById(R.id.textview);  
    22.         textView.setText(JniUtils.getStringFormc(8,9)+"");  
    23.         JniUtils.getArray(new int[]{1,2,3,4,15});  
    24.     }  
    25. }  

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

    上面代码写了一个打印数组的例子,我这里为了方便都是用的静态方法。
    最后提下上面用到的打印日志的头文件
    [cpp] view plain copy
     
    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-r9cplatformsandroid-9arch-armusrincludeandroidlog.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"

    [cpp] view plain copy
     
    1. android {  
    2.       
    3.     defaultConfig {  
    4.           
    5.         ndk {  
    6.             moduleName "NdkJniDemo"          //生成的so名字  
    7.             abiFilters "armeabi", "armeabi-v7a", "x86" //输出指定三种abi体系结构下的so库,目前可有可无。  
    8.             ldLibs "log", "z", "m"  
    9.         }  
    10.     }  


  • 相关阅读:
    线程实现的两种方式
    webhook功能概述
    Linux 常用高频命令
    Mac安装Homebrew的正确姿势
    Mac 环境变量配置
    详解k8s零停机滚动发布微服务
    详解k8s原生的集群监控方案(Heapster+InfluxDB+Grafana)
    白话kubernetes的十万个为什么(持续更新中...)
    一个典型的kubernetes工作流程
    k8s实战之从私有仓库拉取镜像
  • 原文地址:https://www.cnblogs.com/wanghuaijun/p/9131137.html
Copyright © 2020-2023  润新知