Android开发Android进阶之路Android-NDK/JNI

JNI基本操作

2018-06-26  本文已影响49人  waiwaaa

JNI(Java Native Interface)

Java调用C/C++,C/C++调用Java的一套API

windows下步骤

1.编写native方法
2.javah命令,生成.h头文件
3.复制.h头文件到CPP工程中
4.复制jni.h和jni_md.h文件到CPP工程中
5.实现.h头文件中声明的函数
6.生成dll文件
7.配置dll文件所在目录到环境变量
8.重启Eclipse

基本数据:Java基本数据类型与JNI数据类型的映射关系

Java类型->JNI类型->C类型

引用类型(对象)

String jstring
object jobject

数组,基本数据类型的数组

byte[] jByteArray

对象数组

object jobjectArray

函数的实现

C的函数名称

Java_完整类名_函数名

C的函数参数

每个native函数,都至少有两个参数(JNIEnv*,jclass或者jobject)

访问属性

  1. GetObjectClass取得对象类
  2. GetFieldID取属性名称
  3. GetObjectField取属性值
  4. SetObjectField设置属性值

访问静态属性

  1. GetObjectClass取得对象类
  2. GetStaticFieldID取属性名称
  3. GetStaticIntField取属性值(以int类型为例)
  4. SetStaticIntField设置属性值

访问java方法

  1. GetObjectClass取得对象类
  2. GetMethodID取方法id(签名规则见下面说明)
  3. CallIntMethod调用方法

访问静态方法

  1. GetObjectClass取得对象类
  2. GetStaticMethodID取方法id(签名规则见下面说明)
  3. CallStaticObjectMethod调用方法

签名

下表为签名中,类型的对应关系。

Java类型       对应的签名
boolean         Z
byte            B
char            C
shrot           S
int             I
long            L
float           F
double          D
void            V
Object  L用/分割包的完整类名;  Ljava/lang/String;
Array   [签名       [I       [Ljava/lang/String;

例:
void set(String str); 签名:"(Ljava/lang/String;)V"

其实除了自己对照手写之外,JDK也提供了一个很好用的生成签名的工具javap,cmd进入控制台到你要生成签名的那个类的目录下。在这里用 Order类打比方,敲入: javap -s -private Order。所有方法签名都会被输出,关于javap的一些参数可以在控制台下面输入 javap -help查看。(做coder的 毕竟还是要认几个单词的)

代码示例

java代码

package com.dm.jni;

import java.util.Random;
import java.util.UUID;

public class JniTest {

    public String key = "jason";
    
    public static int count = 9;
    
    public native static String getStringFromC();
    
    public native String getString2FromC(int i);
    //访问属性,返回修改之后的属性内容
    public native String accessField();
    
    public native void accessStaticField();
    
    public native void accessMethod();
    
    public native void accessStaticMethod();
    
    public static void main(String[] args) {
        String text = getStringFromC();
        System.out.println(text);
        JniTest t = new JniTest();
        text = t.getString2FromC(6);
        System.out.println(text);
        
        System.out.println("key修改前:"+t.key);
        t.accessField();
        System.out.println("key修改后:"+t.key);
        
        System.out.println("count修改前:"+count);
        t.accessStaticField();
        System.out.println("count修改后:"+count);
        
        t.accessMethod();
        t.accessStaticMethod();
    }
    
    //产生指定范围的随机数
    public int genRandomInt(int max){
        System.out.println("genRandomInt 执行了...");
        return new Random().nextInt(max); 
    }
    
    //产生UUID字符串
    public static String getUUID(){
        return UUID.randomUUID().toString();
    }
    
    //加载动态库
    static{ 
        System.loadLibrary("jni");
    }

}

C代码

#define _CRT_SECURE_NO_WARNINGS
#include "com_dm_jni_JniTest.h"
#include <string.h>
//#include <Windows.h>

//函数实现
JNIEXPORT jstring JNICALL Java_com_dm_jni_JniTest_getStringFromC
(JNIEnv *env, jclass jcls){
    //JNIEnv 结构体指针
    //env二级指针
    //代表Java运行环境,调用Java中的代码
    //简单的实现
    //将C的字符串转为一个java字符串
    return (*env)->NewStringUTF(env,"C String");
}

JNIEXPORT jstring JNICALL Java_com_dm_jni_JniTest_getString2FromC
(JNIEnv *env, jobject jobj, jint num){
    return (*env)->NewStringUTF(env,"C String2");
}


//C/C++访问Java的成员

//1.访问属性
//修改属性key
JNIEXPORT jstring JNICALL Java_com_dm_jni_JniTest_accessField
(JNIEnv *env, jobject jobj){
    //jobj是t对象,JniTest.class
    jclass cls = (*env)->GetObjectClass(env, jobj);
    //jfieldID
    //属性名称,属性签名
    jfieldID fid = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;");   

    //jason >> super jason
    //获取key属性的值
    //Get<Type>Field
    jstring jstr = (*env)->GetObjectField(env, jobj, fid);  
    printf("jstr:%#x\n",&jstr);

    //jstring -> c字符串
    //isCopy 是否复制(true代表赋值,false不复制)
    char *c_str = (*env)->GetStringUTFChars(env,jstr,JNI_FALSE);
    //拼接得到新的字符串
    char text[20] = "super ";
    strcat(text,c_str);

    //c字符串 ->jstring
    jstring new_jstr = (*env)->NewStringUTF(env, text);

    //修改key
    //Set<Type>Field
    (*env)->SetObjectField(env, jobj, fid, new_jstr);

    printf("new_jstr:%#x\n", &new_jstr);

    return new_jstr;
}

//访问静态属性
JNIEXPORT void JNICALL Java_com_dm_jni_JniTest_accessStaticField
(JNIEnv *env, jobject jobj){
    //jclass
    jclass cls = (*env)->GetObjectClass(env, jobj);
    //jfieldID
    jfieldID fid = (*env)->GetStaticFieldID(env, cls, "count", "I");
    //GetStatic<Type>Field
    jint count = (*env)->GetStaticIntField(env, cls, fid);
    count++;
    //修改
    //SetStatic<Type>Field
    (*env)->SetStaticIntField(env,cls,fid,count);
}

//2.访问java方法
JNIEXPORT void JNICALL Java_com_dm_jni_JniTest_accessMethod
(JNIEnv *env, jobject jobj){
    //jclass
    jclass cls = (*env)->GetObjectClass(env, jobj);
    //jmethodID
    jmethodID mid = (*env)->GetMethodID(env, cls, "genRandomInt", "(I)I");
    //调用
    //Call<Type>Method
    jint random = (*env)->CallIntMethod(env, jobj, mid, 200);
    printf("random num:%ld",random);

    //.....
}

//静态方法
JNIEXPORT void JNICALL Java_com_dm_jni_JniTest_accessStaticMethod
(JNIEnv *env, jobject jobj){
    //jclass
    jclass cls = (*env)->GetObjectClass(env, jobj);
    //jmethodID 
    jmethodID mid = (*env)->GetStaticMethodID(env, cls, "getUUID", "()Ljava/lang/String;");
    
    //调用
    //CallStatic<Type>Method
    jstring uuid = (*env)->CallStaticObjectMethod(env, cls, mid);

    //随机文件名称 uuid.txt
    //jstring -> char*
    //isCopy JNI_FALSE,代表java和c操作的是同一个字符串
    char *uuid_str = (*env)->GetStringUTFChars(env, uuid, JNI_FALSE);
    //拼接
    char filename[100];
    sprintf(filename, "D://%s.txt",uuid_str);
    FILE *fp = fopen(filename,"w");
    fputs("i love jason", fp);
    fclose(fp);
}
上一篇下一篇

猜你喜欢

热点阅读