我们知道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):
- import java.util.*;
- public class HelloJni{
- static{
- System.loadLibrary("hello");
- }
- public native static void sayHello();
- public static void main(String [] args)
- {
- HelloJni.sayHello();
- }
- }
生成头文件(HelloJni.h):
先javac HelloJni.java编译你的Java源码,再javah–jni HelloJni生成所需的头文件
头文件内容是这样的:
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include <jni.h>
- /* Header for class HelloJni */
- #ifndef _Included_HelloJni
- #define _Included_HelloJni
- #ifdef __cplusplus
- extern "C" {
- #endif
- JNIEXPORT void JNICALL Java_HelloJni_sayHello (JNIEnv *, jclass);
- #ifdef __cplusplus
- }
- #endif
- #endif
实现头文件声明的方法(HelloJni.cpp)
- #include "HelloJni.h"
- JNIEXPORT void JNICALL 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):
- public class HelloJni{
- static{
- System.loadLibrary("hello");
- }
- public static native void sayHello();
- public native int getInt();
- public native void setInt(int i);
- public static void main(String args[]){
- // HelloJni.sayHello();
- HelloJni hello = new HelloJni();
- hello.setInt(2);
- System.out.println(hello.getInt());
- }
- }
生成头文件(HelloJni.h):
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include <jni.h>
- /* Header for class HelloJni */
- #ifndef _Included_HelloJni
- #define _Included_HelloJni
- #ifdef __cplusplus
- extern "C" {
- #endif
- JNIEXPORT void JNICALL Java_HelloJni_sayHello (JNIEnv *, jclass);
- JNIEXPORT jint JNICALL Java_HelloJni_getInt (JNIEnv *, jobject);
- JNIEXPORT void JNICALL Java_HelloJni_setInt (JNIEnv *, jobject, jint);
- #ifdef __cplusplus
- }
- #endif
- #endif
实现头文件声明的方法(HelloJni.cpp)
- #include "HelloJni.h"
- int i=1;
- JNIEXPORT void JNICALL Java_HelloJni_sayHello
- (JNIEnv *env, jclass cls){
- printf("HelloWorld/n");
- }
- JNIEXPORT jint JNICALL Java_HelloJni_getInt
- (JNIEnv *env, jobject thiz){
- return i;
- }
- JNIEXPORT void JNICALL Java_HelloJni_setInt
- (JNIEnv *env, jobject thiz,jint ji){
- i = ji+1;
- }
运行结果:打印2
3)只是传入简单的数据类型不爽,这次让C/C++生成个Java对象返回
Java代码:pojo实体类(User.java)
- public class User{
- private long id;
- private String userName;
- private boolean isMan;
- private int age;
- public User(){}
- public User(long id, String userName, boolean isMan, int age) {
- super();
- this.id = id;
- this.userName = userName;
- this.isMan = isMan;
- this.age = age;
- }
- public long getId() {
- return id;
- }
- public void setId(long id) {
- this.id = id;
- }
- public String getUserName() {
- return userName;
- }
- public void setUserName(String userName) {
- this.userName = userName;
- }
- public boolean isMan() {
- return isMan;
- }
- public void setMan(boolean isMan) {
- this.isMan = isMan;
- }
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
- }
Java代码(HelloJni.java):
- public class HelloJni{
- static{
- System.loadLibrary("userbean");
- }
- public static native void sayHello();
- public native int getInt();
- public native void setInt(int i);
- public native void setUser(String userName);
- public native User getUser();
- public static void main(String args[]){
- // HelloJni.sayHello();
- HelloJni hello = new HelloJni();
- // 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 <jni.h>
- /* Header for class HelloJni */
- #ifndef _Included_HelloJni
- #define _Included_HelloJni
- #ifdef __cplusplus
- extern "C" {
- #endif
- JNIEXPORT void JNICALL Java_HelloJni_sayHello (JNIEnv *, jclass);
- JNIEXPORT jint JNICALL Java_HelloJni_getInt (JNIEnv *, jobject);
- JNIEXPORT void JNICALL Java_HelloJni_setInt (JNIEnv *, jobject, jint);
- JNIEXPORT void JNICALL Java_HelloJni_setUser (JNIEnv *, jobject, jstring);
- JNIEXPORT jobject JNICALL Java_HelloJni_getUser (JNIEnv *, jobject);
- #ifdef __cplusplus
- }
- #endif
- #endif
实现头文件声明的方法(HelloJni.cpp)
- #include <iostream>
- #include <stdio.h>
- using namespace std;
- int i=1;
- jobject user;
- JNIEXPORT void JNICALL Java_HelloJni_sayHello
- (JNIEnv *env, jclass cls){
- printf("HelloWorld/n");
- }
- JNIEXPORT jint JNICALL Java_HelloJni_getInt
- (JNIEnv *env, jobject thiz){
- return i;
- }
- JNIEXPORT void JNICALL Java_HelloJni_setInt
- (JNIEnv *env, jobject thiz,jint ji){
- i = ji+1;
- }
- JNIEXPORT void JNICALL Java_HelloJni_setUser
- (JNIEnv *env, jobject thiz, jstring name){
- jclass userClass = env->FindClass("User");
- jmethodID userMethod = env->GetMethodID(userClass,"<init>","()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)
- import java.util.*;
- public class HelloJni{
- static{
- System.loadLibrary("userbean");
- }
- public native int get();
- public native void set(int i);
- public native void setUser(String userName);
- public native User getUser();
- public native void setUserList(ArrayList<User> userList);
- public native ArrayList<User> getUserList();
- public static void main(String [] args)
- {
- HelloJni hello = new HelloJni();
- // 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<User> userList = new ArrayList<User>();
- for(int i=0;i<10;i++){
- User u = new User((long)(1000+i),"LiangYaotian"+i,true,21);
- userList.add(u);
- }
- hello.setUserList(userList);
- userList = null;
- userList = hello.getUserList();
- System.out.println("ArrayList<User> construct from C++,then Java print it.....");
- for(User u : userList){
- System.out.println("id:"+u.getId()+"; name:"+u.getUserName());
- }
- }
- }
C头文件(HelloJni.h)
- #include <jni.h>
- /* Header for class HelloJni */
- #ifndef _Included_HelloJni
- #define _Included_HelloJni
- #ifdef __cplusplus
- extern "C" {
- #endif
- JNIEXPORT void JNICALL Java_HelloJni_sayHello (JNIEnv *, jclass);
- JNIEXPORT jint JNICALL Java_HelloJni_getInt (JNIEnv *, jobject);
- JNIEXPORT void JNICALL Java_HelloJni_setInt (JNIEnv *, jobject, jint);
- JNIEXPORT void JNICALL Java_HelloJni_setUser (JNIEnv *, jobject, jstring);
- JNIEXPORT jobject JNICALL Java_HelloJni_getUser (JNIEnv *, jobject);
头文件的实现(HelloJni.cpp)
- #include "HelloJni.h"
- int i=1;
- jobject user;
- JNIEXPORT jint JNICALL Java_HelloJni_get
- (JNIEnv *env, jobject jthiz){
- printf("HelloWorld/n");
- }
- JNIEXPORT void JNICALL Java_HelloJni_set
- (JNIEnv *env, jobject jthiz, jint ji){
- i = ji+1;
- }
- JNIEXPORT void JNICALL Java_HelloJni_setUser
- (JNIEnv *env, jobject jthiz, jstring name){
- jclass userClass = env->FindClass("User");
- jmethodID userMethod = env->GetMethodID(userClass,"<init>","()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){
- return user;
- }
- JNIEXPORT void JNICALL Java_HelloJni_setUserList
- (JNIEnv *env, jobject jthiz, jobject userList){
- int i;
- //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<User> object by C++ , then print it...../n");
- for(i=0;i<len;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;
- const char *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,"<init>","()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,"<init>","()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");
- */
- int i;
- 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);
- }
- return obj_ArrayList;
- }
运行结果: