简析JNI的使用

来源:互联网 发布:尚德自考靠谱 知乎 编辑:程序博客网 时间:2024/06/06 00:11

简析JNI的使用

1、创建一个JNI的demo,JNIDemo在main中创建一个jni包,在jni包中创建一个TestJNI.c的C文件,在java中创建一个JNI的类。注意:创建的TestJNI的后缀是.c一定不能搞错。


2、接下来再到build.grald中配置ndk,这里的moduleName可以不配置,没有配置就是默认的包的名字。不过我建议这里配置好,改动了到时候需要改几个地方。
defaultConfig {        ...        ndk{            moduleName "World"      //so文件:lib + moduleName + .so            abiFilters "armeabi","armeabi-v7a","x86"   //cpu的类型            ldLibs "log"        //在C代码中打印日志        }}

3、做完这些我们就可以在JNITest中添加一些方法来测试是否能调用C代码
public class JNITest {    {        System.loadLibrary("World");    }    /**     * 让C代码执行加法运算,返回结果     * @param x     * @param y     * @return     */    public native int add(int x,int y);    /**     * 传入字符串,C代码进行拼接     * @param str     * @return     */    public native String sayHello(String str);    /**     * 让C代码给每个元素都加上10     * @param intArrary     * @return     */    public native int[] increaseArrayEles(int[] intArrary);    /**     * 判断字符串是否相同,返回200说明正确,返回400说明错误     * @param str     * @return     */    public native int stringIsEqual(String str);}

4、接下来点击build >> rebuild project,如下图;
如果不执行此操作不会生成ndk等一系列文件,这时候我们发现生成的so库的名字就是我们配置的名字,如下图:

第一张图是没有rebuild project之前。

5、生成头文件(至于啥是头文件百度哈),选中JNI类名右键copy reference获取全类名 >> java包右键 >> show in Explorer >> 弹出了main包 >> 进入java >> 在此处打开命令窗口 >> javah 全类名 >> 打开工程java报下如果有一个.h的文件就完成了。如下图:


把.h文件拖到jni包中

6、开始C代码的操作了,导入刚刚生成的头文件
#include "com_valiantman_javacallc_JNITest.h"
接下来把JNI类中的方法一一在TestJNI中实现,添加一些简单的逻辑。
#include "com_valiantman_javacallc_JNITest.h"#include <string.h>char* _JavaStringToCString(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;}jint Java_com_valiantman_javacallc_JNITest_add(JNIEnv *env, jobject jobj, jint ji, jint jy) {    int result = ji + jy;    return result;}jstring Java_com_valiantman_javacallc_JNITest_sayHello(JNIEnv *env, jobject jobj, jstring str) {    char* java = _JavaStringToCString(env,str);//拼接后的结果也在这个字段    char* text = " World";    //拼接函数strcat, 结果放在第一个参数中    strcat(java,text);    jstring result = (*env) ->NewStringUTF(env,java);    return result;}jintArray Java_com_valiantman_javacallc_JNITest_increaseArrayEles(JNIEnv *env, jobject jobj, jintArray arr) {    int size = (*env)->GetArrayLength(env,arr);    jint* intArray = (*env)->GetIntArrayElements(env,arr,JNI_FALSE);    //遍历数组,给每个元素加上10    int i;    for (int i = 0; i < size; ++i) {        *(intArray + i) += 10;    }    return arr;}jint Java_com_valiantman_javacallc_JNITest_checkPassword (JNIEnv *env, jobject jobj, jstring jpswd){    char * password = "123456";    char * jpassword = _JavaStringToCString(env,jpswd);    //函数比较字符串是否相同    int code = strcmp(password,jpassword);    if(code == 0){        return 200;    } else{        return 400;    }}
第一个方法是把从java中传入的字符串转换成C代码,其中的逻辑我不太懂。这个我们不用太在意。在C代码中方法的组成是 :
返回值类型 Java_全类名_方法名(JNIEnv* env, jobject instance, ...){
...
}

7、最后一步最简单了,在MainActivity中添加几个对应的点击事件就可以了,然后打印Log或者Toast验证是否执行。
 @Override    public void onClick(View v) {        switch (v.getId()){            case R.id.btn1:                int result1 = jniTest.add(1, 1);                Toast.makeText(this, "result1= " + result1, Toast.LENGTH_SHORT).show();                Log.e(TAG, "onClick: result= " + result1);                break;            case R.id.btn2:                String result2 = jniTest.sayHello("Hello");                Toast.makeText(this, "result2= " + result2, Toast.LENGTH_SHORT).show();                break;            case R.id.btn3:                int[] arr1 = {1,2,3};                int[] arr = jniTest.increaseArrayEles(arr1);                Toast.makeText(this, "result1= " + arr[0] + " / " + arr[1] + " / " + arr[2], Toast.LENGTH_SHORT).show();                break;            case R.id.btn4:                int code = jniTest.stringIsEqual("123456");                Toast.makeText(this, "code= " + (code==200?"相同":"不相同"), Toast.LENGTH_SHORT).show();                break;        }    }

 到最后才发现没有在C代码中打印log,哈哈.... 打印日志很简单,在C代码中导入打印日志的包就可以了。如下
#include <android/log.h>#define LOG_TAG "JNI"#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG, __VA_ARGS__)#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG, __VA_ARGS__)#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG, __VA_ARGS__)
LOGE("value=%d\n ",value);
想打印啥就打印啥吧,哈哈...

如果代码不通,有啥问题给我留言。我会尽最大努力给大家解决。谢谢...

1+1运算太难了,我用C代码算出结果了,真心不简单。如果ndk没有配置好可以到我前面的博客看下怎么配置。












原创粉丝点击