1.什么是NDK
NDK全称:Native Development Kit :原生库开发工具包。
1.1 NDK是一系列工具的集合。
NDK提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,
并能自动将so和java应用一起打包成apk。这些工具对开发者的帮助是巨大的。
NDK集成了交叉编译器,并提供了相应的mk文件隔离CPU、平台、ABI等差异,
开发人员只需要简单修改mk文件(指出“哪些文件需要编译”、“编译特性要求”等),就可以创建出so。
NDK可以自动地将so和Java应用一起打包,极大地减轻了开发人员的打包工作。
1.2 NDK提供了一份稳定、功能有限的API头文件声明。
Google明确声明该API是稳定的,在后续所有版本中都稳定支持当前发布的API。
从该版本的NDK中看出,这些API支持的功能非常有限,
包含有:C标准库(libc)、标准数学库(libm)、压缩库(libz)、Log库(liblog)。
大家可以自行百度,以免被我粗浅的见解所影响。话不多说,接下来开始从配置环境到撸代码跑通。
2.安装NDK
对于Windows环境下NDK的开发,如果使用的NDK是r7之前的版本,必须要安装Cygwin才能使用NDK,
所以为Eclipse需要配置的builder,其实是执行Cygwin,然后传递ndk-build作为参数。
在NDKr7开始,Google的Windows版的NDK提供了一个ndk-build.cmd的脚本,
这样,就可以直接利用这个脚本编译,而不需要使用Cygwin了。
只需要为Eclipse Android工程添加一个Builders,就能让Eclipse自动编译NDK。
注:本文是讲述NDK-r10e下的实现实例。
接下来是使用NDK-r7在windows下配置自动编译的builders的过程。
(实际上对于Linux系统,只需要修改ndk-build.cmd为ndk-build就可以了)
2.1下载安装Ndk_r10e。
下载地址:
win_64:https://dl.google.com/android/ndk/android-ndk-r10e-windows-x86_64.exe
win_32:https://dl.google.com/android/ndk/android-ndk-r10e-windows-x86.exe
linux_64:https://dl.google.com/android/ndk/android-ndk-r10e-linux-x86_64.bin
linux_32:https://dl.google.com/android/ndk/android-ndk-r10e-linux-x86.bin
Mac OS X 64:https://dl.google.com/android/ndk/android-ndk-r10e-darwin-x86_64.bin
2.2新建项目
我的项目名:NdkTest / 包名:com.andy.ndktest (仅做参考,所有用到的地方请根据自己的修改)
然后在工程目录NdkTest 下新建jni文件夹,该文件夹就用来保存NDK需要编译的文件代码等。
2.3新建并配置Builder:
(a)Project->Properties->Builders->New,新建一个Builder。
(b)在弹出的【Choose configuration type】对话框,选择【Program】,点击【OK】:
(c)在弹出的【Edit Configuration】对话框中,配置选项卡【Main】。
在“Name“中输入新builders的名称(我取名为Ndk_Builder)。
在“Location”中输入nkd-build.cmd的路径。 (我的是E:\android-ndk-r10e\ndk-build.cmd,
根据各自的ndk解压之后存放的路径设置, 也可以点击“Browser File System…”来选取这个路径)。
在“Working Diretcoty”中输入${workspace_loc:/NdkTest } (也可以点击“Browse Workspace”来选取NdkTest 目录)。
(d)【Edit Configuration】对话框中,配置选项卡【Refresh】。
勾选“Refresh resources upon completion”, 勾选“The entire workspace”, 勾选“Recuresively include sub-folders”。
(e)【Edit Configuration】对话框中,配置选项卡【Build options】。
勾选“After a “Clean””, 勾选“During manual builds”, 勾选“During auto builds”, 勾选“Specify working set of relevant resources”。
点击“Specify Resources…”, 勾选TestNdk工程的“jni“目录,点击”finish“。 点击“OK“,完成配置。
OK,到这里Eclipse就能够自动调用NDK编译jin目录下的C/C++代码了。
3.撸代码
3.1 在NdkTest工程中新建一个JniClient.java(为了调用C/C++代码),其内容如下:
package com.andy.jni;
public class JniClient {
static public native String ShowStr();
static public native int AddInt(int a, int b);
}
3.2 android studio:用cmd命令定位到JniClient.java 所在目录,输入“javac JniClient.java“后回车,
生成JniClinet.class文件,将JniClinet.class拷贝到NdkTest\bin\classes\com\andy\jni目录。
Eclipse:在NdkTest\bin\classes\com\andy\jni目录下就已经有JniClinet.class文件了。
3.3将cmd命令定位到NdkTest\bin\classes目录,输入”javah com.andy.jni.JniClient“后回车,
在NdkTest\bin\classes目录下就生成了C++头文件com_andy_jni_JniClient.h。
com_andy_jni_JniClient.h的文件内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_andy_jni_JniClient */
#ifndef _Included_com_andy_jni_JniClient
#define _Included_com_andy_jni_JniClient
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_andy_jni_JniClient
* Method: ShowStr
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_andy_jni_JniClient_ShowStr
(JNIEnv *, jclass);
/*
* Class: com_andy_jni_JniClient
* Method: AddInt
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_andy_jni_JniClient_AddInt
(JNIEnv *, jclass, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
3.4在jni目录下新建一个Android.mk文件,其内容如下:
LOCAL_PATH := $(call my-dir) //编译路径,该工程的根目录
include $(CLEAR_VARS)
LOCAL_MODULE := NdkTest//编译后的模块名字,会自动添加lib前缀和.so后缀
LOCAL_SRC_FILES := com_andy_jni_JniClient.c //需要编译的.c文件
include $(BUILD_SHARED_LIBRARY) //编译成模块
3.5将com_andy_jni_JniClient.h拷贝到NdkTest工程的jni目录下,
然后新建一个com_andy_jni_JniClient.c文件完成头文件中函数的实现,
其内容如下:
#include "com_andy_jni_JniClient.h"
#include <stdlib.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C"
{
#endif
/*
* Class: com_andy_jni_JniClient
* Method: ShowStr
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_andy_jni_JniClient_ShowStr
(JNIEnv *env, jclass arg)
{
jstring str = (*env)->NewStringUTF(env, "HelloWorld from JNI !");
return str;
}
/*
* Class: com_ndk_test_JniClient
* Method: AddInt
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_andy_jni_JniClient_AddInt
(JNIEnv *env, jclass arg, jint a, jint b)
{
return a + b;
}
#ifdef __cplusplus
}
#endif
编辑com_andy_jni_JniClient.c并保存后,
可以看到NdkTest工程下的obj/local/armeabi目录下将自动生成libNdkTest.so库。3.6在MainActivity.java中完成对JniClient.java中函数的调用:
package com.andy.ndktest;
import com.andy.jni.JniClient;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main);
String str = JniClient.ShowStr();
int iSum = JniClient.AddInt(13, 14);
String strSum = "13 + 14 = " + iSum;
TextView tv1 = new TextView(this);
tv1.setText(str + "\n" + strSum);
setContentView(tv1);
}
//静态方法块,加载.so文件
static {
System.loadLibrary("NdkTest");
}
}
3.7运行NdkTest工程
在设备或者模拟器中可以看到界面输出来自com_ndk_test_JniClient.c 文件中的“HelloWorld from JNI ! “以及加法运算结果。
OK,NDK实例到此完成。
后续就可以深入的学习NDK/JNI了,比如C/C++与Java的数据类型转换,Android.mk文件的编写格式等。
欢迎各位大牛批阅指正,谢谢!