简析JNI的使用

简析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没有配置好可以到我前面的博客看下怎么配置。












  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值