JNI 最佳实践

阅读本文前,请先阅读java

  1. JNI-NDK 在AndroidStudio3.2.1版本集成方法(ndk-build方式)
  2. JNI相关概念的理解

★表明难度等级android

★ 实践一:从C里返回String给java

以前咱们实现了从C代码里返回了一个字符串,代码以下:数组

  1. java代码,定义native函数bash

    public class Jni {
        static {
            System.loadLibrary("best");
        }
        public native String sayHello();
    }
    
    复制代码
  2. javah 生成jni样式的标准头文件ide

    这里略去了生成jni_study_com_jnibsetpractice_Jni.h的具体,下面会再次提到函数

  3. c代码,实现java里的native方法工具

    # include "jni_study_com_jnibsetpractice_Jni.h"
    
    JNIEXPORT jstring JNICALL Java_jni_study_com_jnibsetpractice_Jni_sayHello
            (JNIEnv *env, jobject instance) {
        return (*env)->NewStringUTF(env, "Hello from C");
    }
    
    复制代码
  4. java里调用native方法post

    bt_1.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    String hello = jni.sayHello();
                    Toast.makeText(MainActivity.this,hello,Toast.LENGTH_LONG).show();
                }
            });
    复制代码

★ 实践二:java传int参数给C处理,而且返回int

  1. java代码,定义native函数ui

    public class Jni {
        static {
            System.loadLibrary("best");
        }
    
        public native int add(int x,int y);
    }
    
    复制代码
  2. javah 生成jni样式的标准头文件this

    切换到src/main/java目录下执行

    javah -d ../jni jni.study.com.jnibsetpractice.Jni
    
    复制代码

    注意:javah 后半段跟的是 这个native方法所在的类的全路径名称

    打开这个文件,发现生成了java里的这个native方法的jni方法签名

    JNIEXPORT jint JNICALL Java_jni_study_com_jnibsetpractice_Jni_add
      (JNIEnv *, jobject, jint, jint);
    复制代码
    public native int add(int x,int y);
    复制代码

    对比一下:

    1. 返回值:int对应jint
    2. 方法名:add对应Java_jni_study_com_jnibsetpractice_Jni_add
    3. 参数列表(int ,int )对应(JNIEnv *, jobject, jint, jint)

      这里说明一下

      参数JNIEnv * :jni的环境,经过它来调用内置的不少jni方法

      参数jobject:表明在java里调用这个native方法的实例对象(若是是静态方法表明的是类) 参数jint, jint:对应 int x,int y

      JNIEnv是什么在JNI相关概念的理解文章里有详细解释

      jint和int关系在JNI相关概念的理解文章里也有详细解释

  3. c代码,实现java里的native方法

    # include "jni_study_com_jnibsetpractice_Jni.h"
    JNIEXPORT jint JNICALL Java_jni_study_com_jnibsetpractice_Jni_add
            (JNIEnv * env, jobject instance, jint x, jint y){
        return x+y;
    }
    复制代码
  4. Java调用native方法

    bt_2.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int add = jni.add(1, 2);
                    Toast.makeText(MainActivity.this, "1+2=" + add, Toast.LENGTH_LONG).show();
                }
            });
    复制代码

ok,完成了,是否是很简单,若是java传递string给C也是这么简单吗,记得吗java里的string与c里的string但是不同哟

★★ 实践三:java传String参数给C处理,而且返回String

  1. java代码,定义native函数
public class Jni {
    static {
        System.loadLibrary("best");
    }
    public native String transe_string(String str);
}
复制代码
  1. javah 生成jni样式的标准头文件

生成文件的方法略,直接分析这个文件

JNIEXPORT jstring JNICALL Java_jni_study_com_jnibsetpractice_Jni_transe_1string
  (JNIEnv *, jobject, jstring);
复制代码
public native String transe_string(String str);
复制代码

对比一下: jstring对应String

看看jstring是什么

  1. c代码,实现java里的native方法

java传给c一个string,javah生成了方法名后, 发现传递来的是一个jstring(由于在c里,是没有string的), jstring实际上是void*(任意类型), 咱们须要调用一个方法,把jstring转为C语言的char*类型,先看下这个工具方法:

#include <stdlib.h>

/**
 * 把一个jstring转换成一个c语言的char* 类型.
 */
char* _JString2CStr(JNIEnv* env, jstring jstr) {
    char* rtn = NULL;
    jclass clsstring = (*env)->FindClass(env, "java/lang/String");
    jstring strencode = (*env)->NewStringUTF(env,"GB2312");
    jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes", "(Ljava/lang/String;)[B");
    jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid, strencode); // String .getByte("GB2312");
    jsize alen = (*env)->GetArrayLength(env, barr);
    jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);
    if(alen > 0) {
        rtn = (char*)malloc(alen+1); //"\0"
        memcpy(rtn, ba, alen);
        rtn[alen]=0;
    }
    (*env)->ReleaseByteArrayElements(env, barr, ba,0);
    return rtn;
}

复制代码

实现native方法

JNIEXPORT jstring JNICALL Java_jni_study_com_jnibsetpractice_Jni_transe_1string
        (JNIEnv *env, jobject instance, jstring jstr) {

    //把一个jstring转换成一个c语言的char* 类型
    char *cStr = _JString2CStr(env, jstr);
    //c语言拼接字符串
    char *cNewStr = strcat(cStr, "简单加密一下哈哈哈!!!");
    // 把c语言里的char* 字符串转成java认识的字符串
    return (*env)->NewStringUTF(env, cNewStr);
}
复制代码
  1. Java调用native方法
bt_3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String str = jni.transe_string("abc");
                Toast.makeText(MainActivity.this, "abc ====c转换=====> " + str, Toast.LENGTH_LONG).show();
            }
        });
复制代码

★★实践四:java传递给C int数组

  1. java代码,定义native函数
public class Jni {
    static {
        System.loadLibrary("best");
    }

    public native int[] transeIntArray(int[] intArray);
}
复制代码
  1. javah 生成jni样式的标准头文件

生成jni头文件的方法 略

JNIEXPORT jintArray JNICALL Java_jni_study_com_jnibsetpractice_Jni_transeIntArray
  (JNIEnv *, jobject, jintArray);
复制代码
public native int[] transeIntArray(int[] intArray);
复制代码

jintArray对应int[]

  1. c代码,实现java里的native方法
JNIEXPORT jintArray JNICALL Java_jni_study_com_jnibsetpractice_Jni_transeIntArray
        (JNIEnv *env, jobject instance, jintArray jArray) {

//    获得从java传递来的int[]的长度
    jsize length = (*env)->GetArrayLength(env, jArray);

//    获得数组指针
    int *arrayPointer = (*env)->GetIntArrayElements(env, jArray, NULL);
//    开始遍历数组,把每一个元素加100
    int i;
    for (i = 0; i < length; i++) {
        *(arrayPointer + i) += 100;
    }
// ★★★★将arrayPointer这个int *中值复制到jArray数组中,别忘了这一步骤★★★
    (*env)->SetIntArrayRegion(env,jArray, 0, length, arrayPointer);

//    返回数组
    return jArray;
}
复制代码
  1. Java调用native方法
bt_4.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int[] intArray = new int[]{1, 2, 3};
                int[] intArrayTranse = jni.transeIntArray(intArray);

                for (int i = 0; i < intArray.length; i++) {
                    //发如今这里遍历旧的数组,打印出来也是加过100后的数字,101,102,103
                    // 由于C是经过指针操做内存地址,改变了intArray的内存地址的值,
                    // 因此C里面不返回int[]也是能够实现目的的
                    Log.e("abc",intArray[i]+"old");
                }

                for (int i = 0; i < intArrayTranse.length; i++) {
                    Log.e("abc",intArrayTranse[i]+"new");
                }//这里打印的是101,102,103

            }
        });
复制代码

log以下

实践五:C调Java

前面讲的都是java调用C的方法,有时候咱们需C里调用Java的方法,

例如:咱们有一个操做图片的方法是C写的,在C操做图片是,须要让android弹出一个进度条,展现图片操做的进度,这时候,就须要C代码里调用java代码,来改变UI了

待续。。。。。。。。。。。。。。。。

相关文章
相关标签/搜索