Eclipse + ADT(包括NDK Plugin) + CDT 搭建Android NDK开发环境
2014-01-18 21:05
495 查看
Android应用程序的开发环境比较容易搭建,下载完Android SDK,在Eclipse中安装ADT插件就好了。前段时间由于要在Android上做三维程序的开发,三维开发的资源(例如几何算法等)大多数都是C++写,如果想开发出高效的程序,那就必须用到NDK,一部分代码用C/C++编写,生成本地动态链接库libXXX.so,通过Android的JNI接口调用动态库中的本地方法。NDK实际上就是一些开发包和工具包的集合,辅助开发者能够方便的编写本地动态链接库并打包到Android应用的程序的APK包中,尤其是使用ADT中的NDK
plugin,可以方便的调试本地C/C++代码,这一点非常重要,如果不能调试,那在正式的项目中使用是很危险的,如果是我,肯定不会采用这种技术。
虽说,NDK已经推出很多版了,但是环境搭建过程中会遇到各种各样的问题,市面上的资料基本上没有说得很透彻的,我这次花了大概2天的时间才将环境搭建好,解决了许多问题,但是也走了许多弯路,这里特将过程记录下来。
我用的环境是Windows 7旗舰版、Java EE Kepler版本(build id: 20130614-0229)、ADT-22.0.1、CDT-8.2.0、NDK-r8e。很多地方说需要安装交叉编译工具Cgywin,我也确实安装了,事实证明应该是不需要,我在另外一台电脑试过了,下面具体说如何搭建NDK开发环境,Eclipse和Android SDK的安装就不说了,注意顺序,尤其是CDT和ADT的安装顺序。
(1)下载NDK包,并解压http://developer.android.com/tools/sdk/ndk/index.html
(2)下载CDT(C/C++开发环境插件),在Eclipse中安装此插件。
(3)下载ADT,在Ecplise中安装此插件,(http://developer.android.com/sdk/installing/installing-adt.html)一定要选中NDK Plugin
(4)在Ecplise中配置Android SDK和NDK SDK的路径,在Eclipse的Window->Preferences->Android中设置,我本机的设置为Android SDK Location为“C:\Android\android-sdk”,NDK Location为“F:\hexm_private\Android\android-ndk-r8e”。
环境搭建完了,看着很简单吧,实际上还会遇到很多问题,下面一一讲解,为了便于说明问题,做一个HelloWorld程序吧。
(1)首先在Eclipse中创建一个Android工程
(2)在Package Explorer中选中刚才创建的Android工程,右键选择Android Tools -> Add Native Support..,填写需要生成的动态库的名称,比如“HelloWorld”,也可以是别的,你会发现工程中多了一个叫做“jni”的目录,并自动生成了一个C++文件和一个Androd.MK文件,所有的C++源代码就放在这个目录中。这样,就跟Eclipse工程添加了C/C++属性,就能够用CDT插件在Eclipse中进行C/C++开发了。
(3)在Java代码中声明本地方法并调用,部分代码如下(点击屏幕的时候,调用本地方法stringFromJNI,该方法在动态链接库HelloWorld中实现):
@Override
public boolean onTouch(View
v, MotionEvent event)
{
switch(event.getAction())
{
case MotionEvent. ACTION_DOWN:
textView.setText(stringFromJNI());
break;
}
return true;
}
public native String stringFromJNI();
static
{
System.loadLibrary("HelloWorld" );
}
(4)在C/C++代码中实现本地方法
在jni目录的HelloWorld.cpp中添加如下代码,并添加NDK SDK的头文件目录到工程设置中,在Package Explorer中选中刚才创建的Android工程,右键选择Properties,在弹出的界面中选择C/C++ General-〉Paths and Symbols,在包含文件路径中添加即可,我本机上是,”F:\hexm_private\Android\android-ndk-r8e\platforms\android-14\arch-arm\usr\include“
JNIEXPORT jstring JNICALL Java_com_hexmdemo_helloworld_MainActivity_stringFromJNI( JNIEnv*
env, jobject thiz)
{
return env->NewStringUTF("Hello
from JNI");
}
看上面的代码,应该跟许多例子中给得不一样,例如官方给出的hello-jni中的例子
jstring
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
jobject
thiz )
{
return (*env)->NewStringUTF(env, "Hello from JNI");
}
你会发现env参数使用的参数完全不一样,这是因为前者是C++源文件,后者是C源文件,结构JNIEnv在两种环境中定义方式不一样,如果按照C的方式去写肯定没法编译通过。
不仅如此,如果是C源代码,做到这一步,程序就已经写完了,但是我们现在还需要做一些额外的工作,即注册本地方法,如下:
static JNINativeMethod methods[]
= {
{ "stringFromJNI", "()Ljava /lang/String;" ,
(void *)Java_com_hexmdemo_helloworld_MainActivity_stringFromJNI },
[align=left]};[/align]
[align=left]
[/align]
[align=left]//Java代码中本地方法所属的类[/align]
static const char *classPathName
= "com/example/helloworld1/MainActivity" ;
[align=left]
[/align]
static int registerNativeMethods (JNIEnv *
env, const char*
className,
JNINativeMethod*
gMethods, int numMethods)
[align=left]{[/align]
[align=left] jclass clazz;[/align]
[align=left] clazz = env->FindClass(className);[/align]
if (clazz
== NULL) {
[align=left] return JNI_FALSE;[/align]
[align=left] }[/align]
if (env->RegisterNatives(clazz,
gMethods, numMethods) < 0) {
[align=left] return JNI_FALSE;[/align]
[align=left] }[/align]
[align=left]
[/align]
[align=left] return JNI_TRUE;[/align]
[align=left]}[/align]
[align=left]
[/align]
static int registerNatives( JNIEnv*
env)
[align=left]{[/align]
if (!registerNativeMethods(env,
classPathName,
methods, sizeof (methods)
/ sizeof(methods[0]))) {
[align=left] return JNI_FALSE;[/align]
[align=left] }[/align]
[align=left]
[/align]
[align=left] return JNI_TRUE;[/align]
[align=left]}[/align]
[align=left]
[/align]
[align=left]typedef union {[/align]
[align=left] JNIEnv* env;[/align]
[align=left] void* venv;[/align]
[align=left]} UnionJNIEnvToVoid;[/align]
[align=left]
[/align]
jint JNI_OnLoad( JavaVM*
vm, void * reserved)
[align=left]{[/align]
[align=left] UnionJNIEnvToVoid uenv;[/align]
uenv. venv =
NULL;
jint result
= -1;
JNIEnv*
env = NULL;
if (vm->GetEnv(&uenv. venv,
JNI_VERSION_1_4) != JNI_OK) {
[align=left] goto bail;[/align]
[align=left] }[/align]
[align=left] env = uenv. env;[/align]
if (registerNatives(env)
!= JNI_TRUE) {
[align=left] goto bail;[/align]
[align=left] }[/align]
[align=left] result = JNI_VERSION_1_4;[/align]
[align=left]bail:[/align]
[align=left] return result;[/align]
[align=left]}[/align]
[align=left]
[/align]
[align=left] 为什么有这么多麻烦事情呢,这是因为C语言里面由于没有函数重载,函数签名不会被编译器改变,这样本地方法stringFromJNI直接按照规则调用Java_com_example_hellojni_HelloJni_stringFromJNI方法就好了。C++则不同,由于需要支持函数重载,C++函数的签名都会根据参数类型进行改写,因此java虚拟机就无法找到相应的本地函数了。这样就糟糕了,总不能只能用C语言,不能用C++吧,那这样的话大量的C++资源都没法用了。当然,事实不是这样的,java虚拟机允许开发人员自己注册本地函数,一个字符串和一个函数指针关联起来,这样就没有函数签名对应的问题了。上面的代码就是将”stringFromJNI”和函数Java_com_hexmdemo_helloworld_MainActivity_stringFromJNI对应起来。当动态链接库加载的时候,JNI_OnLoad函数就会调用,完成注册过程即可。[/align]
[align=left]
[/align]
[align=left](4)如何调试C/C++代码,还是比较方便的,直接在C++代码中下断点,右键点击Debug As-〉Android Native Application即可。不过,还需要改一个设置。在Package Explorer中选中工程,右键选择Properties,在弹出的界面中选择C/C++ Build,设置build command为“ndk-build NDK_DEBUG=1”,NDK_DEBUG和1之间不要有空格。[/align]
[align=left]
[/align]
[align=left](5)至此,这个HelloWorld程序就已经做完了,从理论上应该是可以运行了,但是这里会出现一个编译错误,类似于:Android NDK: WARNING: APP_PLATFORM android-14 is larger than Android: minSdkVersion 8,网上有一些解决方法,这里采用了一种比较[/align]
[align=left]粗暴的方法,就是讲NDK目录uild\core下的add-application.mk文件中的第125~130行注释掉,如下:[/align]
#ifdef APP_MANIFEST
# APP_MIN_PLATFORM_LEVEL := $(shell $(HOST_AWK) -f $(BUILD_AWK)/extract-minsdkversion.awk $(call host-path,$(APP_MANIFEST)))
# ifneq (,$(call gt,$(APP_PLATFORM_LEVEL),$(APP_MIN_PLATFORM_LEVEL)))
# $(call __ndk_info,WARNING: APP_PLATFORM $(APP_PLATFORM) is larger than android:minSdkVersion $(APP_MIN_PLATFORM_LEVEL) in $(APP_MANIFEST))
# endif
#endif
[align=left]最近看到有网友说在工程的Application.MK文件中添加“APP_PLATFORM := android-8”即可,我还没有试验过。[/align]
[align=left]
[/align]
(6) 如果要在NDK中做一些实用的事情,使用STL是必不可少的。NDK开发包中带了STL Port,设置一下就可以使用,我比较习惯使用STL的静态库版本。需要做两件事情,一是将STL的头文件目录添加到工程中,这个步骤和第(4)步一样,我本机的目录是“F:\hexm_private\Android\android-ndk-r8e\sources\cxx-stl\stlport\stlport”,二是在jni目录中新增Application.MK文件,添加内容“APP_STL
: = stlport_static”。
[align=left] [/align]
[align=left]总体来说,过程并不算太复杂,但是NDK的使用毕竟比较小众,摸索过程还是有些痛苦,希望此文对需要进行NDK开发的广大码农有所帮助。[/align]
plugin,可以方便的调试本地C/C++代码,这一点非常重要,如果不能调试,那在正式的项目中使用是很危险的,如果是我,肯定不会采用这种技术。
虽说,NDK已经推出很多版了,但是环境搭建过程中会遇到各种各样的问题,市面上的资料基本上没有说得很透彻的,我这次花了大概2天的时间才将环境搭建好,解决了许多问题,但是也走了许多弯路,这里特将过程记录下来。
我用的环境是Windows 7旗舰版、Java EE Kepler版本(build id: 20130614-0229)、ADT-22.0.1、CDT-8.2.0、NDK-r8e。很多地方说需要安装交叉编译工具Cgywin,我也确实安装了,事实证明应该是不需要,我在另外一台电脑试过了,下面具体说如何搭建NDK开发环境,Eclipse和Android SDK的安装就不说了,注意顺序,尤其是CDT和ADT的安装顺序。
(1)下载NDK包,并解压http://developer.android.com/tools/sdk/ndk/index.html
(2)下载CDT(C/C++开发环境插件),在Eclipse中安装此插件。
(3)下载ADT,在Ecplise中安装此插件,(http://developer.android.com/sdk/installing/installing-adt.html)一定要选中NDK Plugin
(4)在Ecplise中配置Android SDK和NDK SDK的路径,在Eclipse的Window->Preferences->Android中设置,我本机的设置为Android SDK Location为“C:\Android\android-sdk”,NDK Location为“F:\hexm_private\Android\android-ndk-r8e”。
环境搭建完了,看着很简单吧,实际上还会遇到很多问题,下面一一讲解,为了便于说明问题,做一个HelloWorld程序吧。
(1)首先在Eclipse中创建一个Android工程
(2)在Package Explorer中选中刚才创建的Android工程,右键选择Android Tools -> Add Native Support..,填写需要生成的动态库的名称,比如“HelloWorld”,也可以是别的,你会发现工程中多了一个叫做“jni”的目录,并自动生成了一个C++文件和一个Androd.MK文件,所有的C++源代码就放在这个目录中。这样,就跟Eclipse工程添加了C/C++属性,就能够用CDT插件在Eclipse中进行C/C++开发了。
(3)在Java代码中声明本地方法并调用,部分代码如下(点击屏幕的时候,调用本地方法stringFromJNI,该方法在动态链接库HelloWorld中实现):
@Override
public boolean onTouch(View
v, MotionEvent event)
{
switch(event.getAction())
{
case MotionEvent. ACTION_DOWN:
textView.setText(stringFromJNI());
break;
}
return true;
}
public native String stringFromJNI();
static
{
System.loadLibrary("HelloWorld" );
}
(4)在C/C++代码中实现本地方法
在jni目录的HelloWorld.cpp中添加如下代码,并添加NDK SDK的头文件目录到工程设置中,在Package Explorer中选中刚才创建的Android工程,右键选择Properties,在弹出的界面中选择C/C++ General-〉Paths and Symbols,在包含文件路径中添加即可,我本机上是,”F:\hexm_private\Android\android-ndk-r8e\platforms\android-14\arch-arm\usr\include“
JNIEXPORT jstring JNICALL Java_com_hexmdemo_helloworld_MainActivity_stringFromJNI( JNIEnv*
env, jobject thiz)
{
return env->NewStringUTF("Hello
from JNI");
}
看上面的代码,应该跟许多例子中给得不一样,例如官方给出的hello-jni中的例子
jstring
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
jobject
thiz )
{
return (*env)->NewStringUTF(env, "Hello from JNI");
}
你会发现env参数使用的参数完全不一样,这是因为前者是C++源文件,后者是C源文件,结构JNIEnv在两种环境中定义方式不一样,如果按照C的方式去写肯定没法编译通过。
不仅如此,如果是C源代码,做到这一步,程序就已经写完了,但是我们现在还需要做一些额外的工作,即注册本地方法,如下:
static JNINativeMethod methods[]
= {
{ "stringFromJNI", "()Ljava /lang/String;" ,
(void *)Java_com_hexmdemo_helloworld_MainActivity_stringFromJNI },
[align=left]};[/align]
[align=left]
[/align]
[align=left]//Java代码中本地方法所属的类[/align]
static const char *classPathName
= "com/example/helloworld1/MainActivity" ;
[align=left]
[/align]
static int registerNativeMethods (JNIEnv *
env, const char*
className,
JNINativeMethod*
gMethods, int numMethods)
[align=left]{[/align]
[align=left] jclass clazz;[/align]
[align=left] clazz = env->FindClass(className);[/align]
if (clazz
== NULL) {
[align=left] return JNI_FALSE;[/align]
[align=left] }[/align]
if (env->RegisterNatives(clazz,
gMethods, numMethods) < 0) {
[align=left] return JNI_FALSE;[/align]
[align=left] }[/align]
[align=left]
[/align]
[align=left] return JNI_TRUE;[/align]
[align=left]}[/align]
[align=left]
[/align]
static int registerNatives( JNIEnv*
env)
[align=left]{[/align]
if (!registerNativeMethods(env,
classPathName,
methods, sizeof (methods)
/ sizeof(methods[0]))) {
[align=left] return JNI_FALSE;[/align]
[align=left] }[/align]
[align=left]
[/align]
[align=left] return JNI_TRUE;[/align]
[align=left]}[/align]
[align=left]
[/align]
[align=left]typedef union {[/align]
[align=left] JNIEnv* env;[/align]
[align=left] void* venv;[/align]
[align=left]} UnionJNIEnvToVoid;[/align]
[align=left]
[/align]
jint JNI_OnLoad( JavaVM*
vm, void * reserved)
[align=left]{[/align]
[align=left] UnionJNIEnvToVoid uenv;[/align]
uenv. venv =
NULL;
jint result
= -1;
JNIEnv*
env = NULL;
if (vm->GetEnv(&uenv. venv,
JNI_VERSION_1_4) != JNI_OK) {
[align=left] goto bail;[/align]
[align=left] }[/align]
[align=left] env = uenv. env;[/align]
if (registerNatives(env)
!= JNI_TRUE) {
[align=left] goto bail;[/align]
[align=left] }[/align]
[align=left] result = JNI_VERSION_1_4;[/align]
[align=left]bail:[/align]
[align=left] return result;[/align]
[align=left]}[/align]
[align=left]
[/align]
[align=left] 为什么有这么多麻烦事情呢,这是因为C语言里面由于没有函数重载,函数签名不会被编译器改变,这样本地方法stringFromJNI直接按照规则调用Java_com_example_hellojni_HelloJni_stringFromJNI方法就好了。C++则不同,由于需要支持函数重载,C++函数的签名都会根据参数类型进行改写,因此java虚拟机就无法找到相应的本地函数了。这样就糟糕了,总不能只能用C语言,不能用C++吧,那这样的话大量的C++资源都没法用了。当然,事实不是这样的,java虚拟机允许开发人员自己注册本地函数,一个字符串和一个函数指针关联起来,这样就没有函数签名对应的问题了。上面的代码就是将”stringFromJNI”和函数Java_com_hexmdemo_helloworld_MainActivity_stringFromJNI对应起来。当动态链接库加载的时候,JNI_OnLoad函数就会调用,完成注册过程即可。[/align]
[align=left]
[/align]
[align=left](4)如何调试C/C++代码,还是比较方便的,直接在C++代码中下断点,右键点击Debug As-〉Android Native Application即可。不过,还需要改一个设置。在Package Explorer中选中工程,右键选择Properties,在弹出的界面中选择C/C++ Build,设置build command为“ndk-build NDK_DEBUG=1”,NDK_DEBUG和1之间不要有空格。[/align]
[align=left]
[/align]
[align=left](5)至此,这个HelloWorld程序就已经做完了,从理论上应该是可以运行了,但是这里会出现一个编译错误,类似于:Android NDK: WARNING: APP_PLATFORM android-14 is larger than Android: minSdkVersion 8,网上有一些解决方法,这里采用了一种比较[/align]
[align=left]粗暴的方法,就是讲NDK目录uild\core下的add-application.mk文件中的第125~130行注释掉,如下:[/align]
#ifdef APP_MANIFEST
# APP_MIN_PLATFORM_LEVEL := $(shell $(HOST_AWK) -f $(BUILD_AWK)/extract-minsdkversion.awk $(call host-path,$(APP_MANIFEST)))
# ifneq (,$(call gt,$(APP_PLATFORM_LEVEL),$(APP_MIN_PLATFORM_LEVEL)))
# $(call __ndk_info,WARNING: APP_PLATFORM $(APP_PLATFORM) is larger than android:minSdkVersion $(APP_MIN_PLATFORM_LEVEL) in $(APP_MANIFEST))
# endif
#endif
[align=left]最近看到有网友说在工程的Application.MK文件中添加“APP_PLATFORM := android-8”即可,我还没有试验过。[/align]
[align=left]
[/align]
(6) 如果要在NDK中做一些实用的事情,使用STL是必不可少的。NDK开发包中带了STL Port,设置一下就可以使用,我比较习惯使用STL的静态库版本。需要做两件事情,一是将STL的头文件目录添加到工程中,这个步骤和第(4)步一样,我本机的目录是“F:\hexm_private\Android\android-ndk-r8e\sources\cxx-stl\stlport\stlport”,二是在jni目录中新增Application.MK文件,添加内容“APP_STL
: = stlport_static”。
[align=left] [/align]
[align=left]总体来说,过程并不算太复杂,但是NDK的使用毕竟比较小众,摸索过程还是有些痛苦,希望此文对需要进行NDK开发的广大码农有所帮助。[/align]
相关文章推荐
- Eclipse + ADT(包括NDK Plugin) + CDT 搭建Android NDK开发环境
- Eclipse + ADT(包括NDK Plugin) + CDT 搭建Android NDK开发环境
- Eclipse + ADT(包括NDK Plugin) + CDT 搭建Android NDK开发环境
- Eclipse + ADT(包括NDK Plugin) + CDT 搭建Android NDK开发环境
- Eclipse + ADT(包括NDK Plugin) + CDT 搭建Android NDK开发环境
- Eclipse + ADT(包括NDK Plugin) + CDT 搭建Android NDK开发环境
- Android SDK+Eclipse+ADT+CDT+NDK 开发环境在windows 7下的搭建
- Android SDK+Eclipse+ADT+CDT+NDK 开发环境在windows 7下的搭建
- Android SDK +Eclipse+ADT+CDT+NDK 开发环境在windows 7下的搭建
- Android SDK+Eclipse+ADT+CDT+NDK开发环境在win7下的搭建
- Android SDK +Eclipse+ADT+CDT+NDK 开发环境在windows 7下的搭建
- Android SDK +Eclipse+ADT+CDT+NDK 开发环境在windows 7下的搭建
- Eclipse+CDT+NDK开发环境搭建(Win+Ubuntu)
- 使用Android NDK开发(二):搭建Android NDK开发环境(android-ndk-r10+eclipse+NDK plugin,不需安装Cygwin)
- 环境: ubuntu 12.04 上面Android ndk 开发环境一键配置(cdt, adt, ndk 及Ogre工程移植)
- Android开发搭环境步骤, ADT, JDK, SDK, NDK, Eclipse, CDT
- 搭建Android NDK环境,包括NDK.cygwin.CDT.example(经过验证)
- Android NDK开发之旅(1): Eclipse中NDK环境搭建与JNI开发流程
- Windows下Android+NDK开发环境搭建(JDK[8u45]+Eclipse+Android SDK[r24.1.2]+ADT+NDK[r10d])
- Android开发搭环境步骤, ADT, JDK, SDK, NDK, Eclipse, CDT