Android-NDK/JNIJNI和【NDK】Android收藏集

JNI 基础 - JNIEnv 的实现原理

2018-09-15  本文已影响3人  Peakmain

JNI的一般开发流程

1.1 定义好本地的 native 方法

 public static void main(String[] args) {
        NdkSimple ndkSimple=new NdkSimple();
        String singnaturePassword = ndkSimple.getSingnaturePassword();
        System.out.println("秘钥=="+singnaturePassword);
    }
    //定义好本地的native方法
    public native String getSingnaturePassword();

1.2 javah 命令生成 .h 头文件
我用的是idea工具,在Terminal下首先定位到src目录下,然后在输入

javah -jni com.example.demo.NdkSimple

1.3 拷贝 xxx.h、jni_md.h、jni.h 到 VS 的工程目录并添加依赖进来


image.png

jni.h和jni_md.h到自己安装的jdk目录下搜索jni即可

image.png

1.4 实现我们头文件中的 native 方法

#include "com_example_demo_NdkSimple.h"

JNIEXPORT jstring JNICALL Java_com_example_demo_NdkSimple_getSingnaturePassword(JNIEnv * env, jobject jobj){

    return (*env)->NewStringUTF(env, "9931005");
}

1.5 生成 dll 动态,java 引入 dll 动态库运行即可
代码写完后选择调试->最后一个选项xx属性,我的项目名字叫jni.


image.png

配置属性->常规->项目默认值->配置类型->选择dll选项


image.png
生成->配置项目管理器
image.png
平台->新建
image.png

我的是x64


image.png

生成->生成解决方案


image.png

java中引入动态库即可,动态库在自己的ndk项目x64目录下


image.png
 static {
     //引入加载我们的动态库
     //System.loadLibrary():android中加载apk中的.so库
     //System.load()加载一个具体路径上的so库,去服务器下载再进行加载
     System.load("D:/visual studio 2013/Projects/jni/x64/Debug/jni.dll");
 }

详解.h头文件和实现文件

/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class com_example_demo_NdkSimple */
//用来打印一个标记,在c编译的时候会把头文件拷贝到你引入的地方,不管是重复引用还是相互引用都只会copy一次
#ifndef _Included_com_example_demo_NdkSimple
#define _Included_com_example_demo_NdkSimple
#ifdef __cplusplus//相当于if语句 c++
//不管是c还是c++统一都是采用c的编译方式,因为在c里面不允许函数重载的,但是c++里面是可以的
extern "C" {
#endif
/*
 * Class:     com_example_demo_NdkSimple
 * Method:    getSingnaturePassword
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_demo_NdkSimple_getSingnaturePassword
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

#include "com_example_demo_NdkSimple.h"
//JNIEXPORT JNI一个关键字,不能少。如果少了,编译能通过,但是运行出错。标记该方法可以被外部调用
//jstring代表java中string
//JNICALL 也是一个关键字,可以没有。jni call
//JNIEnv:这个是java和c相互调用的桥梁
//jobject:java传递下来的对象,就是本项目JniSimple.class对象
//jclass:java传递下来的class对象,就是本项目JniSimple.class对象
JNIEXPORT jstring JNICALL Java_com_example_demo_NdkSimple_getSingnaturePassword(JNIEnv * env, jobject jobj){
    return (*env)->NewStringUTF(env, "9931005");
}

c访问java属性

首先java方法

public class NdkSimple1 {
    static {
        //引入加载我们的动态库
        //System.loadLibrary():android中加载apk中的.so库
        //System.load()加载一个具体路径上的so库,去服务器下载再进行加载
        System.load("D:/visual studio 2013/Projects/jni/x64/Debug/jni.dll");
    }

    private String name = "peakmain";
    public static int age = 18;

    public static void main(String[] args) {
        NdkSimple1 ndkSimple1 = new NdkSimple1();
        System.out.println("alert before:" + ndkSimple1.name);
        System.out.println("alert before:" + ndkSimple1.age);
        ndkSimple1.changeName();
        ndkSimple1.changeAge();
        System.out.println("alert after:" + ndkSimple1.name);
        System.out.println("alert after:" + ndkSimple1.age);
    }

    public native void changeName();
    public static native void changeAge();
}

生成头文件,获得类的签名文件如我的项目结构是,定位到test目录下,若不记得命令输入javap即可

javap -p -s com.example.demo.NdkSimple1
image.png

我的结果图


image.png

c实现代码

JNIEXPORT void JNICALL Java_com_example_demo_NdkSimple1_changeName
(JNIEnv *env, jobject jobj){
    //获取name属性,修改名字为Treasure


    // 3.获取 jclass 
    jclass j_clz = (*env)->GetObjectClass(env, jobj);
    // 获取 jfieldId (JNIEnv *env, jclass clazz, const char *name, const char *sig)
    // name 获取哪个属性的属性名 
    // 2.sig 属性的签名,**javap -p -s com.example.demo.NdkSimple1**
    jfieldID j_fid = (*env)->GetFieldID(env, j_clz, "name", "Ljava/lang/String;");
    // 1.获取 name 属性的值
    jstring j_str = (*env)->GetObjectField(env, jobj, j_fid);
    //打印字符串
    char* c_str = (*env)->GetStringUTFChars(env, j_str, NULL);

    printf("name is %s", c_str);
    //修改名字为Treasure
    jstring TreaureName = (*env)->NewStringUTF(env, "Treasure");

    (*env)->SetObjectField(env, jobj, j_fid, TreaureName);
}

JNIEXPORT void JNICALL Java_com_example_demo_NdkSimple1_changeAge
(JNIEnv *env, jclass jclass){
    // 首先获取原来的
    jfieldID j_fid = (*env)->GetStaticFieldID(env, jclass, "age", "I");
    // Static 获取静态的
    jint age=(*env)->GetStaticIntField(env, jclass, j_fid);
    // jint -> int
    age += 6;
    // 设置新的 age 参数
    (*env)->SetStaticIntField(env, jclass, j_fid, age);
}

生成动态库即可

c访问java方法

添加方法

  public native void callAddMethod();
   public int add(int number1, int number2) {
        return number1 + number2;
    }

c实现代码

JNIEXPORT void JNICALL Java_com_example_demo_NdkSimple1_callAddMethod
(JNIEnv *env, jobject jobj){
    jclass jclass=(*env)->GetObjectClass(env, jobj);
    //获取method
    jmethodID j_mid = (*env)->GetMethodID(env, jclass,"add" , "(II)I");
    //去调用java的方法
    jint sum = (*env)->CallIntMethod(env, jobj, j_mid, 2, 3);
    printf("sum = %d", sum);
}
 public static String getUUID() {
        return UUID.randomUUID().toString();
    }
    public static native void callGetUUID();

c实现代码

JNIEXPORT void JNICALL Java_com_example_demo_NdkSimple1_callGetUUID
(JNIEnv *env, jclass jclz){
    // 2.获取 methodId
    jmethodID j_mid = (*env)->GetStaticMethodID(env, jclz, "getUUID", "()Ljava/lang/String;");
    // 1. 调用 getUUID 的方法 static 
    jstring j_uuid = (*env)->CallStaticObjectMethod(env, jclz, j_mid);
    //jstring->c_str
    char* c_uuid=(*env)->GetStringUTFChars(env, j_uuid, NULL);
    // 回收,字符串回收 
    //(*env)->ReleaseStringUTFChars(env, j_uuid, c_uuid);
    printf("c_uuid = %s", c_uuid);
}   
上一篇 下一篇

猜你喜欢

热点阅读