我们知道Java是一个运行在虚拟机里面的高级的编程语言,如果要调用系统的动态链接库的话,就要先声明native修饰的方法(类似接口里面的方法),再由C/C++程序来实现(类似实现接口里的方法)。这样Java调用这些native方法就相当于调用了C/C++里面实现了的方法。通常我们把这种机制叫做JNI(Java NativeInterfac),即Java 本地编程接口
Android也同理,要学会在Android上进行NDK开发,首先我们到打好java JNI的基础。现在我们暂时把Android开发丢到一边先,试试在Java之下编译一个C动态链接库,再用Java程序调用。
1)先来个最简单的打印HelloWorld例子:
Java代码(HelloJni.java):
importjava.util.*;
publicclassHelloJni{
static{
System.loadLibrary("hello");
}
publicnativestaticvoidsayHello();
publicstaticvoidmain(String [] args)
{
HelloJni.sayHello();
}
}
生成头文件(HelloJni.h):
先javac HelloJni.java编译你的Java源码,再javah–jni HelloJni生成所需的头文件
头文件内容是这样的:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class HelloJni */
#ifndef _Included_HelloJni
#define _Included_HelloJni
#ifdef __cplusplus
extern "C"{
#endif
JNIEXPORT voidJNICALL Java_HelloJni_sayHello (JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
实现头文件声明的方法(HelloJni.cpp)
#include "HelloJni.h"
JNIEXPORT voidJNICALL Java_HelloJni_sayHello(JNIEnv *env, jclass cls)
{
printf("HelloWorld");
}
编译命令: g++ -I$JAVA_HOME/include-I$JAVA_HOME/include/linux HelloJni.cpp -shared -o libhello.so
命令参数解析:-I 是指引入java虚拟机的库的路径,-shared 是指编译成动态链接库(共享库) –o 输出文件名(注意,在Linux平台下的动态链接库有一个命名格式:“lib+库名+.so”在java代码里面loadLibrary的时候不要加lib前缀和.so后缀)
由于我这里把这个动态链接库编译放在当前目录下,所以还要设置环境变量LD_LIBRARY_PATH=该so动态链接库所在的目录,才能正常运行
#############################+++华丽的分割线+++###########################
2)好了,可以打印HelloWorld出来后,我们再深入一点点,传入一个int的数,在C/C++代码里面加1后返回。
Java代码(HelloJni.java):
publicclassHelloJni{
static{
System.loadLibrary("hello");
}
publicstaticnativevoidsayHello();
publicnativeintgetInt();
publicnativevoidsetInt(inti);
publicstaticvoidmain(String args[]){
// HelloJni.sayHello();
HelloJni hello = newHelloJni();
hello.setInt(2);
System.out.println(hello.getInt());
}
}
生成头文件(HelloJni.h):
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class HelloJni */
#ifndef _Included_HelloJni
#define _Included_HelloJni
#ifdef __cplusplus
extern"C"{
#endif
JNIEXPORT voidJNICALL Java_HelloJni_sayHello (JNIEnv *, jclass);
JNIEXPORT jint JNICALL Java_HelloJni_getInt (JNIEnv *, jobject);
JNIEXPORT voidJNICALL Java_HelloJni_setInt (JNIEnv *, jobject, jint);
#ifdef __cplusplus
}
#endif
#endif
实现头文件声明的方法(HelloJni.cpp)
#include"HelloJni.h"
inti=1;
JNIEXPORT voidJNICALL Java_HelloJni_sayHello
(JNIEnv *env, jclass cls){
printf("HelloWorld/n");
}
JNIEXPORT jint JNICALL Java_HelloJni_getInt
(JNIEnv *env, jobject thiz){
returni;
}
JNIEXPORT voidJNICALL Java_HelloJni_setInt
(JNIEnv *env, jobject thiz,jint ji){
i = ji+1;
}
运行结果:打印2
3)只是传入简单的数据类型不爽,这次让C/C++生成个Java对象返回
Java代码:pojo实体类(User.java)
publicclassUser{
privatelongid;
privateString userName;
privateboolean isMan;
privateintage;
publicUser(){}
publicUser(longid, String userName, boolean isMan,intage) {
super();
this.id = id;
this.userName = userName;
this.isMan = isMan;
this.age = age;
}
publiclonggetId() {
returnid;
}
publicvoidsetId(longid) {
this.id = id;
}
publicString getUserName() {
returnuserName;
}
publicvoidsetUserName(String userName) {
this.userName = userName;
}
publicboolean isMan() {
returnisMan;
}
publicvoidsetMan(boolean isMan) {
this.isMan = isMan;
}
publicintgetAge() {
returnage;
}
publicvoidsetAge(intage) {
this.age = age;
}
}
Java代码(HelloJni.java):
publicclassHelloJni{
static{
System.loadLibrary("userbean");
}
publicstaticnativevoidsayHello();
publicnativeintgetInt();
publicnativevoidsetInt(inti);
publicnativevoidsetUser(String userName);
publicnativeUser getUser();
publicstaticvoidmain(String args[]){
// HelloJni.sayHello();
HelloJni hello = newHelloJni();
// hello.setInt(2);
// System.out.println(hello.getInt());
hello.setUser("LiangYaotian");
User user = hello.getUser();
System.out.println("user from c/c++");
System.out.println("name:"+user.getUserName());
System.out.println("isMan?:"+user.isMan());
System.out.println("age:"+user.getAge());
}
}
生成头文件(HelloJni.h):
#include
/* Header for class HelloJni */
#ifndef _Included_HelloJni
#define _Included_HelloJni
#ifdef __cplusplus
extern"C"{
#endif
JNIEXPORT voidJNICALL Java_HelloJni_sayHello (JNIEnv *, jclass);
JNIEXPORT jint JNICALL Java_HelloJni_getInt (JNIEnv *, jobject);
JNIEXPORT voidJNICALL Java_HelloJni_setInt (JNIEnv *, jobject, jint);
JNIEXPORT voidJNICALL Java_HelloJni_setUser (JNIEnv *, jobject, jstring);
JNIEXPORT jobject JNICALL Java_HelloJni_getUser (JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
实现头文件声明的方法(HelloJni.cpp)
#include
#include
usingnamespacestd;
inti=1;
jobject user;
JNIEXPORT voidJNICALL Java_HelloJni_sayHello
(JNIEnv *env, jclass cls){
printf("HelloWorld/n");
}
JNIEXPORT jint JNICALL Java_HelloJni_getInt
(JNIEnv *env, jobject thiz){
returni;
}
JNIEXPORT voidJNICALL Java_HelloJni_setInt
(JNIEnv *env, jobject thiz,jint ji){
i = ji+1;
}
JNIEXPORT voidJNICALL Java_HelloJni_setUser
(JNIEnv *env, jobject thiz, jstring name){
jclass userClass = env->FindClass("User");
jmethodID userMethod = env->GetMethodID(userClass,"","()V");
jfieldID mId = env->GetFieldID(userClass,"id","J");
jfieldID mUserName = env->GetFieldID(userClass,"userName","Ljava/lang/String;");
jfieldID mIsMan = env->GetFieldID(userClass,"isMan","Z");
jfieldID mAge = env->GetFieldID(userClass,"age","I");
jobject userObject = env->NewObject(userClass,userMethod);
env->SetObjectField(userObject,mUserName,name);
env->SetLongField(userObject,mId,1001);
env->SetBooleanField(userObject,mIsMan,1);
env->SetIntField(userObject,mAge,21);
user = userObject;
}
4)有些同学可能会说:“返回个Java对象算什么啊,C/C++和Java之间互传复杂对象才好玩呢!”正所谓“不怕做不到,就怕想不到”,我们接着重构一下上面那个例子!
这次我们传包含有User对象的List到C/C++程序里面
Java代码(User.java)
同上。
Java代码(HelloJni.java)
importjava.util.*;
publicclassHelloJni{
static{
System.loadLibrary("userbean");
}
publicnativeintget();
publicnativevoidset(inti);
publicnativevoidsetUser(String userName);
publicnativeUser getUser();
publicnativevoidsetUserList(ArrayList userList);
publicnativeArrayList getUserList();
publicstaticvoidmain(String [] args)
{
HelloJni hello = newHelloJni();
// hello.set(2);
// System.out.println(hello.get());
/*
hello.setUser("LiangYaotian");
User user = hello.getUser();
System.out.println("user from c/c++");
System.out.println("name:"+user.getUserName());
System.out.println("isMan?:"+user.isMan());
System.out.println("age:"+user.getAge());
*/
ArrayList userList = newArrayList();
for(inti=0;i<10;i++){
User u = newUser((long)(1000+i),"LiangYaotian"+i,true,21);
userList.add(u);
}
hello.setUserList(userList);
userList = null;
userList = hello.getUserList();
System.out.println("ArrayList construct from C++,then Java print it.....");
for(User u : userList){
System.out.println("id:"+u.getId()+"; name:"+u.getUserName());
}
}
}
C头文件(HelloJni.h)
#include
/* Header for class HelloJni */
#ifndef _Included_HelloJni
#define _Included_HelloJni
#ifdef __cplusplus
extern "C"{
#endif
JNIEXPORT voidJNICALL Java_HelloJni_sayHello (JNIEnv *, jclass);
JNIEXPORT jint JNICALL Java_HelloJni_getInt (JNIEnv *, jobject);
JNIEXPORT voidJNICALL Java_HelloJni_setInt (JNIEnv *, jobject, jint);
JNIEXPORT voidJNICALL Java_HelloJni_setUser (JNIEnv *, jobject, jstring);
JNIEXPORT jobject JNICALL Java_HelloJni_getUser (JNIEnv *, jobject);
头文件的实现(HelloJni.cpp)
#include"HelloJni.h"
inti=1;
jobject user;
JNIEXPORT jint JNICALL Java_HelloJni_get
(JNIEnv *env, jobject jthiz){
printf("HelloWorld/n");
}
JNIEXPORT voidJNICALL Java_HelloJni_set
(JNIEnv *env, jobject jthiz, jint ji){
i = ji+1;
}
JNIEXPORT voidJNICALL Java_HelloJni_setUser
(JNIEnv *env, jobject jthiz, jstring name){
jclass userClass = env->FindClass("User");
jmethodID userMethod = env->GetMethodID(userClass,"","()V");
jfieldID mId = env->GetFieldID(userClass,"id","J");
jfieldID mUserName = env->GetFieldID(userClass,"userName","Ljava/lang/String;");
jfieldID mIsMan = env->GetFieldID(userClass,"isMan","Z");
jfieldID mAge = env->GetFieldID(userClass,"age","I");
jobject userObject = env->NewObject(userClass,userMethod);
env->SetObjectField(userObject,mUserName,name);
env->SetLongField(userObject,mId,1001);
env->SetBooleanField(userObject,mIsMan,1);
env->SetIntField(userObject,mAge,21);
user = userObject;
}
JNIEXPORT jobject JNICALL Java_HelloJni_getUser
(JNIEnv *env, jobject jthiz){
returnuser;
}
JNIEXPORT voidJNICALL Java_HelloJni_setUserList
(JNIEnv *env, jobject jthiz, jobject userList){
inti;
//class ArrayList
jclass cls_arraylist = env->GetObjectClass(userList);
//method in class ArrayList
jmethodID arraylist_get = env->GetMethodID(cls_arraylist,"get","(I)Ljava/lang/Object;");
jmethodID arraylist_size = env->GetMethodID(cls_arraylist,"size","()I");
jint len = env->CallIntMethod(userList,arraylist_size);
printf("get java ArrayList object by C++ , then print it...../n");
for(i=0;i
jobject obj_user = env->CallObjectMethod(userList,arraylist_get,i);
jclass cls_user = env->GetObjectClass(obj_user);
jmethodID user_getId = env->GetMethodID(cls_user,"getId","()J");
jmethodID user_getUserName = env->GetMethodID(cls_user,"getUserName","()Ljava/lang/String;");
jmethodID user_isMan = env->GetMethodID(cls_user,"isMan","()Z");
jmethodID user_getAge = env->GetMethodID(cls_user,"getAge","()I");
jstring name = (jstring)env->CallObjectMethod(obj_user,user_getUserName);
jboolean b = true;
constchar*namePtr = env->GetStringUTFChars(name,&b);
jlong id = env->CallLongMethod(obj_user,user_getId);
jboolean sex = env->CallBooleanMethod(obj_user,user_isMan);
jint age = env->CallIntMethod(obj_user,user_getAge);
printf("Id:%d; ",id);
printf("Name:%s; ",namePtr);
printf("isMan? %d; ",sex);
printf("Age:%d /n ",age);
}
}
JNIEXPORT jobject JNICALL Java_HelloJni_getUserList
(JNIEnv *env, jobject jthiz){
//ArrayList Object
jclass cls_ArrayList = env->FindClass("java/util/ArrayList");
jmethodID construct = env->GetMethodID(cls_ArrayList,"","()V");
jobject obj_ArrayList = env->NewObject(cls_ArrayList,construct,"");
jmethodID arrayList_add = env->GetMethodID(cls_ArrayList,"add","(Ljava/lang/Object;)Z");
//User Object
jclass cls_user = env->FindClass("User");
//none argument construct function
jmethodID construct_user = env->GetMethodID(cls_user,"","()V");
//new a object
jobject obj_user = env->NewObject(cls_user,construct_user,"");
//get method id
/*
jmethodID user_setId = env->GetMethodID(cls_user,"setId","(J)V");
jmethodID user_setUserName = env->GetMethodID(cls_user,"setUserName","(Ljava/lang/String;)V");
jmethodID user_setMan = env->GetMethodID(cls_user,"setMan","(Z)V");
jmethodID user_setAge = env->GetMethodID(cls_user,"setAge","(I)V");
*/
inti;
for(i=0;i<10;i++){
//new a object
jobject obj_user = env->NewObject(cls_user,construct_user,"");
//get field id
jfieldID user_id = env->GetFieldID(cls_user,"id","J");
jfieldID user_name = env->GetFieldID(cls_user,"userName","Ljava/lang/String;");
jfieldID user_isMan = env->GetFieldID(cls_user,"isMan","Z");
jfieldID user_age = env->GetFieldID(cls_user,"age","I");
env->SetLongField(obj_user,user_id,i);
env->SetObjectField(obj_user,user_name,env->NewStringUTF("LiangYaoTian"));
env->SetBooleanField(obj_user,user_isMan,1);
env->SetIntField(obj_user,user_age,21);
env->CallObjectMethod(obj_ArrayList,arrayList_add,obj_user);
}
returnobj_ArrayList;
}
运行结果: