这两天在倒腾NDK, 算是小告一段落,下面就说说Eclipse下面 NDK plugin的配置以及如何在 Eclipse下面调试 native 代码。
PS: 也是参照网上的文章做的,本来打算直接摘抄他的,结果当时没有保存,算了,直接自己写一篇了。
准备工作
- 安装Android SDK 和 NDK, 这个不多说啦,网上很多
- 安装Eclipse插件
- Help -> Install New Software -> Add, 添加软件源, location 为 https://dl-ssl.google.com/android/eclipse/, Name自己决定。
- 选中刚才添加的源,把出来的两个都安装上。上面那个是ADT插件,下面那个是NDK对应的插件。
- 同样的,安装CDT, 软件源为 http://download.eclipse.org/tools/cdt/releases/indigo. 我用的Eclipse是 indigo版本,如果你的不是的话,那么找到对应的源并安装就好了。
工具配置
接下来配置 ADT路径和 NDK 路径, 如果ADT的已经配置好了就不用管啦。Elipse菜单, Window -> Preference, 左边,点开 Android标签页,把Android SDK路径填上,对应的还有 NDK 的路径:
至此,全局的工作已经搞定了。
NDK项目
创建项目
新建一个Android Application项目, Application Name 配置如下,后面全部默认。
添加 NDK 支持
选中项目,右键, Android Tools->Add Native Supports, 会冒出来一个对话框,教你输入一个lib名字,实际上就是最后生成的动态链接库的名字,默认就是之前设置的项目名称,当然你也可以自己修改。
native 代码调用
- 上一步完成之后,会在项目根目录下面创建一个 jni目录,下面有一个 NdkTest.cpp文件和一个Android.mk 文件;
- 接下来,就是在java代码里面添加 native 函数声明, 并调用这个函数来测试,将MainActivity.java 里面修改如下:
public class MainActivity extends Activity {
native String ndkTest();
static {
System.loadLibrary("NdkTest");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView tView = new TextView(this);
tView.setText( ndkTest() );
setContentView(tView);
}
// others don't have to change
生成 Native 函数接口
调用javah生成头文件,然后手动将这个头文件放到jni目录下面, 实际上,这一步并不是必须的,如果你知道jni中函数的命名规范,自己在NdkTest.cpp 里面添加对应的函数实现就好了,不过,为了避免出错,还是使用 javah 来自动生成C\C++函数接口比较好, 操作步骤的话,很简单
- 切换到 jni目录下面
- 输入
javah -jni -classpath ../bin/classes -o ./ndkTest.h com.example.NdkTest.MainActivity
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_example_ndktest_MainActivity */ #ifndef _Included_com_example_ndktest_MainActivity #define _Included_com_example_ndktest_MainActivity #ifdef __cplusplus extern "C" { #endif /* * Class: com_example_ndktest_MainActivity * Method: ndkTest * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_example_ndktest_MainActivity_ndkTest (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
实现Native函数
在NdkTest.cpp文件里面包含 ndkTest.h, 并实现对应的函数:
#include <jni.h> #include "ndkTest.h" JNIEXPORT jstring JNICALL Java_com_example_ndktest_MainActivity_ndkTest (JNIEnv *env, jobject obj) { return (*env).NewStringUTF("Just a test"); }
注意,虽然 ndkTest.h 里面有 <jni.h> 头文件包含,但是如果 ndkTest.cpp 里面没有的话,Eclipse会无法自动完成函数提示。也可能是我没有配置好。
编译
接下来,Project-> Build Project, 就能够编译得到动态链接库文件了。
TIPS: 不过,如果你的Eclipse设置没怎么修改的话(就像我的一样),可能会有如下错误:
APP_PLATFORM android-14 is larger than android:minSdkVersion 8 in ./AndroidMainfest.xml
可以按照 这里来修改, Window -> Preferences -> C\C++ -> Build -> Settings, 选择右边的 CDT GNU C/C++ Error Parser, 添加一条规则并将它置顶,如下:
上面的设置需要注意前两个冒号后面的空格不能少, 我把空格用下划线替代,应该是这个样子:
(.*?):_Android NDK:_WARNING:(.*)
如果一切顺利的话,就会生成 libNdkTest.so, 并复制到 libs/armeabi 目录下面。 Ctrl + F11 就可以运行程序了。
如果运行失败,确保在MainActivity.java 里面有添加\
static {
System.loadLibrary("NdkTest");
}
这一句话的作用就是加载所调用的动态链接库文件,这里就是 libNdkTest.so。
调试Native code
接下来就是重头戏啦,使用eclipse来调试代码。
生成可调试代码
首先在编译的时候保证代码可调试,Project -> Properties, 选择 C/C++ Build, 去掉 Use default build command 前面的勾, 在编译命令后面加上 NDK_DEBUG=1, 注意等号前后没有空格:
OK 之后, Project -> Build Project, 然后看Console输出窗口,如果看到Gdb字样,说明调试版本编译成功:
调试
不过,离见证奇迹的时刻还差一点, 这里 有说( Known Issues上面的那个 NOTE),native 调试的启动器相对与Android应用的启动有一个延时,也就是很有可能你的 native code都调用完毕了,调试器还木有启动,断点神马的对它来说可能就是个毛线啦。 不过,也有办法来解决。在调试的时候,把 native 函数调用放到一个OnClickLinstener函数里面去,比如,可以这样修改 MainActivity 里面的 OnCreate函数:@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); TextView tView = new TextView(this); tView.setText( "Do you dare to click me???"); tView.setOnClickListener( new OnClickListener() { @Override public void onClick(View v) { ((TextView)v).setText( ndkTest() ); } }); setContentView(tView); }
然后,在 NdkTest.cpp 的 ndkTest 函数里面设置断点,这个,不用我多说了吧!接下来,Run -> DebugAs -> Android Native Application等到程序启动完毕之后,并且Eclipse console窗口有类似如下输出,说明调试器加载完毕(或者是eclipse出现是否进入调试页面,我的设置为默认了,所以不太确定是不是会提示):
最后,见证奇迹的时刻,激动人心的时刻,come come come coming.... 点击一下程序主界面:
嘿嘿嘿,一切尽在不言中。。。。
(这里,为了设断点,我加了两条语句,如果你也修改了 NdkTest.cpp 的话,需要重新 Build Project)