NDK 10分钟入门教程

你可以在http://git.oschina.net/cocobaby/HelloNative找到文章的实例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。如果看到下面这样就说明成功了。

ndk-build-cmd

hello jni

接下来我们先拿ndk自带的sample练练手。

  • 导入sample中的hellojni

hellojni项目在android-ndk-r9d\samples\hello-jni目录下,导入到eclipse。

导入成功之后可以先看一下项目结构,这时候除了普通的项目文件外还有一个jni的文件夹,里面是mk文件和.c文件,如图:

hellojniproject

  • 打开cmd,cd到项目目录下

  • 执行ndk-build命令

ndk-build-result

这个命令是用来编译我们的ndk项目的,编译之后就会生成可以用的so库文件

这时候我们刷新一下项目,会发现多了两个文件夹。一个是lib,一个是obj。如图:

ndk-hello-jni-pro-2

  • 运行hello-jni,如图:
    run-result

注:如果是用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

如图:
javah-jni-h

这个命令的确很麻烦,一个个参数解释下

  • -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需要的头文件
1
2
3
#include <string.h>
#include <jni.h>
#include "com_example_hellonative_MainActivity.h"
  • 实现函数

Java_com_example_hellonative_MainActivity_getStringFromNative

  • 整个c文件的代码
1
2
3
4
5
6
7
#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拿过来之后需要修改一点东西,如下:

1
2
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库

这个就比较简单了,看代码:

1
2
3
static{
System.loadLibrary("hello-native");
}

静态加载hello-native,这个名字是LOCAL_MODULE指定的,不要写错。

然后就可以调用我们写的native方法了,整个代码是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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());
}
}

运行结果如下:

hello-native-result

写完收工~