因为项目需要,最近开始用Android开发,想使用一些线程的C\C++代码,所有,了解了下 NDK的使用。虽然网上的相关教程一找一大把,学习的过程中也借鉴了不少 ,不过感觉还是自己写一边,权当作是加深印象啦。
至于java的include路径,可以使用:
JNI, java nativa interface, 是通过虚拟机来实现 java 和本地代码(主要是C\C++)进行相互调用的一种技术。 鄙人对 Java 不是很熟,关于虚拟机啥的也说不上太多,就不在这里多说了。
在Android里面调用C之前,先看看在本地如何用java来调用C函数罢,操作起来也就那么几步:
编写java类 HelloJNI.java
/**
* @brief A class test jni's usage
*/
class HelloJNI
{
/**
* @brief native method to implemente with C
*/
private native void sayHello();
/**
* load the native library
*/
static {
System.loadLibrary("HelloJNI");
}
static public void main(String[] args) {
new HelloJNI().sayHello();
}
}
这里,定义了一个简单的测试类 HelloJNI, 里面会调用一个使用C实现的函数sayHello, 所以声明的时候前面有添加 native 关键字。此外,因为要用到 native code, 所以需要加载代码所在的动态链接库,System.loadLibrary("HelloJNI") 的作用就是加载 libHelloJNI.so 这个动态链接库(对应在windows下面应该是 HelloJNI.dll, 没有测试过)。也就是说,sayHello这个函数的实现最后就是在 libHelloJNI.so 这个动态链接库中, 接下来的工作就是构建这个动态链接库。
编译 java 代码
接下来,就可以编译这个java类了
viking@own:~/documents/project/jni$ javac HelloJNI.java
viking@own:~/documents/project/jni$ javah HelloJNI
第一行的作用就是编译得到 HelloJNI.class, 第二条编译命令的则是根据这个class生成对应的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
/*
* Class: HelloJNI
* Method: sayHello
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloJNI_sayHello
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
实际上就是定义了sayHello 这个函数接口在C语言中长啥样。 那么,接下来的工作想想便知,那就是用C或者C++来实现这个Java_HelloJNI_sayHello 函数,并且将之打包到libHelloJNI.so 里面去。
本地代码生成
新建一个jniHello.c, 内容如下:
#include <jni.h>
#include <stdio.h>
#include "HelloJNI.h"
JNIEXPORT void JNICALL Java_HelloJNI_sayHello
(JNIEnv * env, jobject obj)
{
printf("Hello, JNI!\n");
}
so easy的一个文件。 那么,接下来就把他编译成 libhelloJNI.so啦, 一下下的事情而已:gcc -I/usr/lib/jvm/java-6-openjdk-i386/include/ -shared -o libHelloJNI.so HelloJNI.c
至此就生成的到了libHelloJNI.so。至于java的include路径,可以使用:
viking@own:~/documents/project/jni$ ls -l `which java`
lrwxrwxrwx 1 root root 22 Jun 20 13:55 /usr/bin/java -> /etc/alternatives/java
可以看到还是一个软链接,继续使用 ls,viking@own:~/documents/project/jni$ ls -l /etc/alternatives/java
lrwxrwxrwx 1 root root 45 Jun 20 13:55 /etc/alternatives/java -> /usr/lib/jvm/java-6-openjdk-i386/jre/bin/java
可以知道当前系统中使用的 /usr/lib/jvm/java-6-openjdk-i386 下的java开发文件,所以jni路径就是对应的include目录了。
运行结果
最后,就可以运行测试结果啦:
下一步,就是将怎么样利用NDK来实现同样的功能了。
viking@own:~/documents/project/jni$ java HelloJNI
Hello, JNI!
从上面可以看出来,使用java调用C\C++其实很简单,首先定义要用的函数,生成对应的函数接口(*.h)文件,然后,就是实现这些接口并打包到一个动态链接库中。最后,就是在java代码中加载这个动态链接库,调用相应的函数了。下一步,就是将怎么样利用NDK来实现同样的功能了。