简析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代码
6、开始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包中
#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没有配置好可以到我前面的博客看下怎么配置。