为什么选择JNI,我想大家都应该有理解。要么就是前人写好了库,自己懒了不想重写,要么就是你们项目这个技术比较核心,不想轻松被人给反编译,也有可能你需要与底层打交道,这时候由于某种理由我们可能就需要选择JNI开发。
Jni实际上就是用于JAVA与C进行通信的,NDK是用来编译C语言文件成so给Android进行调用的。所以大家需要区分清楚JNI与NDK不同的功能。C语言中的函数通过jni数据类型接收JAVA的传入参数,但是一般jni数据类型我们不能直接在C中使用,这时候就需要将jni类型,转化为C语言能够使用的数据类型了。
本文以Android Studio为例来说明一个非常简单的JNI demo。个人觉得JNI在Android开发中还是比较重要的,况且最近项目因为实时性就用到了此块。打算好好讲解一下JNI开发。凡事以基础为起点,本文首先是运行起来一个JNI demo。另外我使用的是Android mk文件,Android Studio2.2已经可以使用CMakeList文件了,变得更加方便与智能,不过我觉得你习惯使用哪个,觉得哪个更顺手自己决定就好。
1,创建一个Android Studio项目,新建一个类JniUtils
package org.eclipse.paho.android.jnidemo.jniutil;
/**
* Created by IBM on 2016/11/10.
*/
public class JniUtils {
static {
System.loadLibrary("jnilib");
}
public native int sum(int a,int b);
public native String getString(String s1,String s2);
}
System.loadLibrary(“jnilib”);这个就是加载ndk编译生成的so库,此处生出的库名字是libjnilib.so,注意名字要去掉lib和lib.so
2,使用javah命令生成JniUtils的.h文件,名字是org_eclipse_paho_android_jnidemo_jniutil_JniUtils.h
本文生成的.h文件具体内容是:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class org_eclipse_paho_android_jnidemo_jniutil_JniUtils */
#ifndef _Included_org_eclipse_paho_android_jnidemo_jniutil_JniUtils
#define _Included_org_eclipse_paho_android_jnidemo_jniutil_JniUtils
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: org_eclipse_paho_android_jnidemo_jniutil_JniUtils
* Method: sum
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_org_eclipse_paho_android_jnidemo_jniutil_JniUtils_sum
(JNIEnv *, jobject, jint, jint);
/*
* Class: org_eclipse_paho_android_jnidemo_jniutil_JniUtils
* Method: getString
* Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_org_eclipse_paho_android_jnidemo_jniutil_JniUtils_getString
(JNIEnv *, jobject, jstring, jstring);
#ifdef __cplusplus
}
#endif
#endif
3,新建jniUtils.c文件,就是开始写C代码实现
//
// Created by IBM on 2016/11/10.
//
#include "org_eclipse_paho_android_jnidemo_jniutil_JniUtils.h"
#include<stdlib.h>
// 引入log头文件
#include <android/log.h>
// log标签
#define TAG "JniUtils"
// 定义info信息
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG,__VA_ARGS__)
// 定义debug信息
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
// 定义error信息
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG,__VA_ARGS__)
JNIEXPORT jint JNICALL Java_org_eclipse_paho_android_jnidemo_jniutil_JniUtils_sum
(JNIEnv * env, jobject thiz, jint a, jint b){
int re = a + b;
return (jint)re;
}
JNIEXPORT jstring JNICALL Java_org_eclipse_paho_android_jnidemo_jniutil_JniUtils_getString
(JNIEnv * env, jobject thiz, jstring str1, jstring str2){
char *str;
//char result[256];
str = (*env)->GetStringUTFChars(env,str1,NULL);
//strcpy(result,str);
(*env)->ReleaseStringUTFChars(env,str1,str);
char *re = "jni to java";
jstring ret = (*env)->NewStringUTF(env,re);
return ret;
}
具体的代码如果你是刚接触jni可以先不要搞懂,先照着例子做,我一般都是先看现象,再去看如何实现,这样能一开始放心能出结果。
3,编写mk文件
在Android Studio的jni目录下新建Android.mk文件和Application.mk文件
Android.mk:
LOCAL_PATH :=
(callmy−dir)include
(CLEAR_VARS)
LOCAL_LDLIBS := -L
(SYSROOT)/usr/lib−llogLOCALMODULE:=jnilibLOCALSRCFILES:=jniUtils.cinclude
(BUILD_SHARED_LIBRARY)
Application.mk文件
APP_ABI := armeabi
4,然后你就可以使用ndk进行编译了。
现在回到MainActivity文件里,加入几行代码:
JniUtils jniUtils = new JniUtils();
int re = jniUtils.sum(2,3);
Log.i(TAG, "onCreate: Sum result is "+re);
String r = jniUtils.getString("hehe","haha");
Log.i(TAG, "onCreate: getString result is"+r);
运行虚拟机即可看到控制台打印输出结果:
11-10 09:54:54.509 2546-2546/? I/MainActivity: onCreate: Sum result is 5
11-10 09:54:54.509 2546-2546/? I/MainActivity: onCreate: getString result isjni to java
说明我们的JNI例子是跑通的。
下一篇明天再写基本JNI数据类型与C语言的数据类型之间的转换关系。