一个简单但完整的NDK demo

来源:互联网 发布:剑网3萝莉脸型数据 编辑:程序博客网 时间:2024/04/26 18:02

这是我模仿视频教程而写的一个简单的NDKdemo,麻雀虽小五脏俱全,里面也有不少注意事项,在此记下详细步骤。

因为最早是看的《Android应用开发解密》和土豆网上的一个视频,前者ndk版本太老,一些诸如class文件生成、jni文件夹的新建、Android.mk、Application.mk的生成等操作需要我们手工进行,而我用的NDK r9则方便得多,除了Application.mk根本不需要生成之外,class文件、jni文件夹、Android.mk都是系统自动生成,连so文件也可以通过软件生成,不用借助Cygwin;后者缺少so的生成的步骤说明。下面开始整个流程,目的是在MainActivity的一个Button上显示C代码返回的String,如下图。环境:Eclipse 3.8,NDK r9。


流程严格分为四步:

一.JNI接口设计;二.在C/C++中实现本地方法;三.生成动态链接库so;四.在工程中调用so。

一.JNI接口设计

  1. 新建一个Android工程,名为SecondNDK,在布局xml里定义一个Button,id为result。

  2.在工程文件夹上  右键--Android Tools--Add Native Support,核查一下各参数,标配如下图,点击‘完成’。


注:如果你的NDK位置处是空白的话,可以到 菜单栏的‘Windows--Preference’窗口左侧的‘ Android--NDK’中设置你NDK的路径,设置好后这里就会有所显示。

点击‘完成’后,发现工程的根目录下自动多了一个‘jni文件夹’(这在NDK r1里是要我们手动新建的),里面自动生成Android.mk和一个.cpp文件,甚好,如下图。


3.在同一个package里新建一个.java文件,里面是一些由java文件定义、在c/c++文件里实现的一些函数,注意这些函数要加关键字“native”。


接下来要为饱含接口函数的java文件生成C的头文件,这步需要在cmd中进行。命令如下图:


说明:参考BetweenFunc.class的位置为:E:\工作空间1\工作空间2\SecondNDK\bin\classes\com\example\secondndk,或许就能猜到这个格式的路径的含义,其中的

javah -d ../../jni是为了指定生成的c头文件的位置,-classpath . 是因为系统总提示我“找不到**类文件”。

此步成功后,便在jni文件夹中生成了c的头文件,刷新jni文件夹后便可看到如图结果:


打开这个.h文件后会发现有一些错误,那是因为系统未成功解析‘’#include‘’命令,解决方法是:工程右键---属性---C/C++常规---路径和符号---(框体中央的)包含---GNUC---添加,然后索引至下图中椭圆圈出的路径(NDK的位置因人而异,但最终须定位至对应API级别的文件夹),然后‘应用---确定’,发现错误消失。(注,后来的2013.11.21那天,我又遇到过这个问题,通过依次尝试得出的结论是:这种error只需要通过让GNU C++ 的包含目录只含对应android的include文件即可,GNU C是可以不包含任何东西的。如下下图,之前的说法不准确,在这里更正。)



不妨再看看头文件(下图)的一些语句的意思:

(1)注释部分:标出了 类名 + 方法名,Signature后是()+字段 的形式,括号里是形参的类型,本例中函数无形参,括号里就是空(注意,JNIEnv *, jobject除外,此二者是所有的jni函数里都必须含有的形参,第一者表示jni环境,后者表示对应的java对象本身,但细节我也不清楚);字段表明函数的返回值的类型,I表示返回int型,Ljava/lang/String表示返回的是字符串,连包含关系也显示出来了。

(2)JNIEXPORT和JNICALL是jni的宏,可以去掉,留下也没错;函数名,虽长但很有规律:Java_包名_类名_函数名。


至此,第一大步的JNI接口的设计已完成,接下来要在.cpp文件中实现java文件中定义的方法。

二.用C/C++实现本地方法

 打开jni文件夹下的SecondNDK.cpp文件,首先把上一步生成的c头文件include进来,然后复制.h中的个方法头到本.cpp文件中,并予以实现,如下图:


注:这里的方法名的尾部(get_1Num和get_1Name)和我真实的函数名(get_Num和get_Name)不一致,我曾试图将其中的‘1’删掉,但一旦删掉,程序便强制退出,在此暂且保留。

至此第二大步完成,该生成动态连接库so文件了。《Android应用开发揭秘》上由于NDK版本过低,so的生成是通过在Cygwin中输入命令符实现的,我一开始在输入命令时并不顺利,总是各种错误,而调试方法网上各执一词;再者,土豆网上的《Android NDK开发》点击打开链接应该是缺了一步:so的生成,导致完全模仿该视频是没办法运行该工程的。r9版本的NDK已有界面化的操作来生成so,步骤来源于这篇博客(点击打开链接)。

三.生成so文件

在此我拷贝并部分修改上面提到的第二个博客里的关键部分的说明:

3.1 新建并配置一个新的Builder

    1) 点击Project->Properties->Builders->New,新建立一个Builder。在弹出的对话框上面点击Program,点击OK;

    2) 在弹出的对话框【Edit Configuration】中,配置选项卡【Main】:

               Location中需要填入nkd-build.cmd的路径(NDK安装目录下)。

               WorkingDiretcoty中需要填入SecondSDK的工程根目录。


    3) 在【EditConfiguration】中,配置选项卡【Refresh】:

              勾选“Refresh resources upon completion”,

              勾选“The entire workspace”,

              勾选“Recuresively include sub-folders”。


    4)在【EditConfiguration】中,配置选项卡【Build Options】:

             勾选“After a “Clean””,

             勾选“During manual builds”,

             勾选“During auto builds”,

             勾选“Specify working set of relevant resources”。

             点击“Specify Resources…”勾选TestNDK工程的“jni“目录,Finish!

      保存设置,点击OK。


3.2 生成so文件

     由于我们勾选了“During auto builds”,所以在工程有所改变的时候,so文件便会自动编译,控制台会有输出,如下图。正确生成以后就能在工程目录下发现多了两个文件夹,文件夹libs\armeabi目录下生成了一个叫libhello-jni.so的文件,如下下图。至此,使用NDK生成so文件的工作就完成了。




至此,第三大步(so的生成) 已经完成,接下来就是调用该库了。

四.调用so库。

这步比较简单,首先在定义各函数的java文件中添加一个static块,内容是调用so库,实参为Android.mk里的LOCAL_MODULE的value值,在本工程里就是SecondSDK。


至此,第四大步完成。


现在可以在MainActivity里进行findViewById()及.setText()之类的操作了,如下图



在虚拟机上跑一下了,结果无误,可以正常显示。


另一篇博客写的是直接在Linux下进行NDK编程的一个简单demo,里面有一些更详细的字段说明(包括.h和.mk文件的语句说明)比较详尽,可待日后查看。

由于自己也是刚学,而且网上NDK的资料比较少,所以步骤写得拙劣了些,但绝对易懂,希望和大家一起交流,一起进步!

2013.11.21 加:如果在最终运行程序之前,因为某种原因而手动修改了.java里的某个native方法名,那必须注意:对应的C/C++的头文件和源文件,都需要更新方法名(.cpp和.h文件可以手动去改,注意别改错了方法名);同时,函数链接库so文件也需要修改更新,但so文件就没法手动去改了(打开全是乱码),只好将其所在的libs和obj文件夹删除,然后重新在Cygwin中进入工程的文件夹,再执行 $NDK/ndk-build指令去重新生成这两个文件夹(执行后刷新即可看到恢复的两个文件夹)。所以,在一开始写代码的时候,该好好考虑到维护时的代价,否则会‘牵一发而动全身’。

原创粉丝点击