本文首先发表在http://www.hikyson.cn/archives/771
你可以在http://download.csdn.net/detail/hikyson/8850293找到文章的实例demo。
准备工作
下载ndk r9d版本
地址:http://pan.baidu.com/s/1o6MKL2y 密码: usb2
r10版本的ndk会有些变化导致原来的一些库不能用
配置ndk环境变量
把下载的ndk解压到任意目录下,比如我解压到了
D:\application\AndroidDevelopTools\android-ndk-r9d
目录下配置环境变量
在计算机 -> 属性 -> 高级系统设置 -> 环境变量
下找到path
。在后面追加D:\application\AndroidDevelopTools\android-ndk-r9d;
path的每个环境变量配置都需要用
;
分隔开的
配置完成之后启动cmd,输入ndk-build。如果看到下面这样就说明成功了。
hello jni
接下来我们先拿ndk自带的sample练练手。
- 导入sample中的hellojni
hellojni
项目在android-ndk-r9d\samples\hello-jni
目录下,导入到eclipse。
导入成功之后可以先看一下项目结构,这时候除了普通的项目文件外还有一个jni的文件夹,里面是mk文件和.c文件,如图:
打开cmd,cd到项目目录下
执行ndk-build命令
这个命令是用来编译我们的ndk项目的,编译之后就会生成可以用的so库文件
这时候我们刷新一下项目,会发现多了两个文件夹。一个是lib,一个是obj。如图:
- 运行hello-jni,如图:
注:如果是用genemotion运行的话需要安装arm-translation包,这个就自己网上找一个吧,很多。
注:如果刚才步骤出错了,可以运行ndk-build clean
,删除不用的二进制文件(就是so之类的),重新编译。
到这里,我们没有自己写过代码,也不知道hello-jni做了什么,仅仅就是一步步编译了一下别人的项目。不急,接下来我们就要自己写一个简单的ndk项目了。
创建自己的NDK项目
1. 创建项目
创建一个普通的项目HelloNative
,在mainactivity
里添加一个native
方法:
public static native String getStringFromNative();
这时候可以回头看看sample下的
hello-jni
了,你可以在它的代码中看到native方法public native String stringFromJNI();
和public native String unimplementedStringFromJNI();
2. 创建jni目录
在项目根目录下创建一个文件夹,叫jni
。
3. 生成.h头文件
我们就需要用一个工具javah
用来生成头文件。
在项目目录下,执行:
javah -classpath bin/classes;D:application\AndroidDevelopTools\sdktools\platform\android-22\android.jar -d jni com.example.hellonative.MainActivity
如图:
这个命令的确很麻烦,一个个参数解释下
-classpath
就是指定用于生成头文件的class文件,大家都知道java代码会首先编译成class文件,这边就是用的这个。android项目编译之后的class文件都放在bin下的。-d
就是输出目录到jni。- 最后一个指定需要编译的类路径,我们这里是编译的
MainActivity
类。
这里还需要指定android.jar,因为我们的mainactivity是在android开发包下的
运行完javah命令cmd不会打印任何信息就说明可以了。
这时候刷新项目,我们会惊喜地看到,jni文件夹下有了一个.h文件。文件是由包名+类名以下划线分隔组成的。
javah
应该在你安装java的时候就已经配置好了环境变量了的,如果cmd运行javah告诉你识别不了命令,就网上找个教程配置一下吧,这边不赘述了。
4. 编写C代码
- 我们从刚才生成的头文件中拷贝需要实现的函数头
JNIEXPORT jstring JNICALL Java_com_example_hellonative_MainActivity_getStringFromNative
(JNIEnv * env, jclass jcls) - include需要的头文件
#include <string.h>
#include <jni.h>
#include "com_example_hellonative_MainActivity.h"
- 实现函数
Java_com_example_hellonative_MainActivity_getStringFromNative
- 整个c文件的代码
#include <string.h>
#include <jni.h>
#include "com_example_hellonative_MainActivity.h"
JNIEXPORT jstring JNICALL Java_com_example_hellonative_MainActivity_getStringFromNative
(JNIEnv * env, jclass jcls){
return (*env)->NewStringUTF(env, "www.hikyson.cn from native");
}
5. 编写mk文件
mk文件是用来编译c代码的脚本文件,一般来说我们直接把sample中的android.mk拿过来就可以了
android.mk是主要的配置,还有一个application.mk用来指定需要编译的平台。
android.mk拿过来之后需要修改一点东西,如下:
LOCAL_MODULE := hello-native
LOCAL_SRC_FILES := hello-native.c
LOCAL_MODULE
指定了生成的so文件名,最后生成的so文件是lib+LOCAL_MODULE+.so
LOCAL_SRC_FILES
指定了需要编译的文件
6. 生成so库
前面已经有了头文件,根据头文件写好了对应的c代码,而且也有了mk编译脚本,所有事情都已经完备,只欠编译,就像上文编译sample一样,我们执行ndk-build
命令。
我们也可以看出来,刚才的
hello-jni
sample是已经把当前步骤之前的所有东西都做了的。
编译得到so文件。
7. 使用so库
这个就比较简单了,看代码:
static{
System.loadLibrary("hello-native");
}
静态加载hello-native
,这个名字是LOCAL_MODULE
指定的,不要写错。
然后就可以调用我们写的native方法了,整个代码是这样的:
public class MainActivity extends Activity {
static {
System.loadLibrary("hello-native");
}
public static native String getStringFromNative();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((TextView) this.findViewById(R.id.text))
.setText(getStringFromNative());
}
}
运行结果如下:
写完收工~