Android JNI调用(三)

来源:互联网 发布:战争是必然的 知乎 编辑:程序博客网 时间:2024/05/16 09:48
  最后需要说的就是,在偶传上来的代码中,可能会发现有一个叫做libcutils.a的编译好的静态库,这个东西就“说来话长”了,主要原因是偶在做实验的时候,还没有ndk发布出来,android手机里面也没个gdbserver之类的工具,调试起来十分痛苦。偶认为再怎么弱,也要输出点东西到logcat吧?!因此,从android-platform的平台源代码中提取了cutils的头文件,直接把android平台编译出来的二进制.a文件拷贝出来,链接到偶自己的“土法”生成的so库里面,这样就可以调用libcutils.a中定义的log函数,就可以直接通过联机的logcat查看jni中的log日志输出,很爽!ndk的文档中承诺,在未来的android ndk开发包中会提供在线调试的功能。

    到此为止,“土法”编译和编写jni的方法已经基本记录和讲解完毕。一定能够对ndk的本质有了新的认识。而不是那里面readme和howto文档中的几行字,修改android。mk之类云云。。。
  当然有了上面的这些底层编译的探索,加上ndk里面提供的。h和若干运行时库,甚至android平台源代码里面编译出来的静态二进制包,jni几乎可以实现任何功能。
  还是那句话,“潘多拉”的盒子一旦打开,能否控制得住,就不是google这样的公司能够左右的了。
  等有时间再来写写关于使用google的ndk来编写和调试jni模块的方法。。。
  其实主要是关于ndk的一些编译选项的研究和翻译(其实人家google的文档已经说的很清楚了)。偶选用的测试环境是slackware 12。0 + android 1。5 r1 for linux + jdk 1。6。0_12,ndk选用的是android 1。5 ndk r1这个版本的(直接解压就行,免安装的)。
  1、从ndk安装说起
  ndk安装的时候需要运行一个~/android-ndk-1。5_r1/build/目录下面的一个叫做host-setup。sh的脚本。大略读了一下这个脚本,发现这个主要是用来生成out/host/host/config。mk文件的。主要用于指定用户操作系统的判断以及支持的编译器类型(设置makefile中的cc,ar,ld之类的变量)
  ndk的目录介绍。
  2、ndk的目录结构分析
  进入android-ndk-1。5_r1目录,看到如下目录结构:
  GNUmakefile: 标准的makefile格式的文件,用于引用build/core/main。mk的编译脚本。
  README。TXT:基本的说明,没啥大用,真正有用的文档都在docs目录下面。
  apps/:存放带有jni接口的android工程目录(工程里面有利用native关键字定义的java函数)
  build/:存放着几乎所有的ndk编译相关的脚本以及必要的静态链接库。
  docs/:存放这ndk的所有“官方”文档,每一篇文档对于jni编写者来说这里面的任何一点点资料都是无价的。
  out/:存放一些中间的临时文件,例如jni的。c/。cpp文件编译过程中产生的。o文件等。
  sources/:存放jni文件的。c/。cpp的源代码文件。
  3、基本的使用方法
  (1)创建一个android工程
  进入apps目录,运行如下命令:
  android create project --target 2 --package com。TWM --activity NDKTest --path 。/NDKTest/project
  通过命令行创建一个叫做NDKTest的activity,注意这里的--path需要设置为。/XXXXX/project这个目录,这个XXXXX目录主要是为了ndk的make区分不同项目和工程使用的。编写Application。mk文件的时候,一定要把Application。mk写到这个XXXXX目录下面。
  $NDK/apps/<myapp>/Application。mk
  另外,编译jni库的时候使用的命令也是如此:
  make APP=<your app name>
  这里的<your app name>实际上也是这个XXXXX目录。
  (2)为工程添加一个jni的java调用接口
  进入app/NDKTest/project/src/com/TWM/NdkTest目录,建立一个新的java文件(例如:NDKJni。java),然后把代码写成类似下面这个样子:

java代码:
  1. package com.TWM.NdkTest ; 
  2. public class NDKJni {
  3. public native int MyFunc(int a, int b) ;
  4. static {
  5. System.loadLibrary("NDKjni") ; 
  6. }


复制代码

       这里的MyFunc由于是使用native修饰,因此,这个MyFunc函数是一个调用jni的函数。
  (3)为java工程编写Application。mk文件
  该文件主要放在app/NDKTest目录下,用于告知ndk的编译脚本,当前的程序需要哪个jni模块。
  看上去应该是这个样子的:
  APP_PROJECT_PATH := $(call my-dir)/project   ---> 当前目录下的project目录包含了jni模块的java接口
  APP_MODULES      := NDKTest                               --->当前模块的名字叫做NDKTest
  (4)弄清楚java程序的包层次
  以当前的这个project为例,就是上面代码中的package com。TWM。NdkTest,定义的类名为NDKJni。因此,根据这个包的层次,可以根据jni文件的函数命名规则定义函数:
  JNIEXPORT jint JNICALL Java_com_TWM_NdkTest_NDKJni_MyFunc(JNIEnv * env, jobject thiz, jint a, jint b) ;
  当然,手工根据包层次定义jni函数还是很痛苦的,可以借助于javah工具:
  mkdir -p apps/NDKTest/project/jni
  cd apps/NDKTest/project/jni
  javah -classpath "/bin/classes" com。TWM。NdkTest。NDKJni
  然后就会自动生成一个叫做com_TWM_NdkTest_NDKJni。h的文件,里面的内容基本上跟手工生成的差不多:

java代码:
  1. /* DO NOT EDIT THIS FILE - it is machine generated */

  2. #include <jni.h>

  3. /* Header for class com_TWM_NdkTest_NDKJni */
  4. #ifndef _Included_com_TWM_NdkTest_NDKJni
  5. #define _Included_com_TWM_NdkTest_NDKJni
  6. #ifdef __cplusplus
  7. extern "C" {

  8. #endif

  9. /*
  10. * Class: com_TWM_NdkTest_NDKJni
  11. * Method: MyFunc
  12. * Signature: (II)I
  13. */
  14. JNIEXPORT jint JNICALL Java_com_TWM_NdkTest_NDKJni_MyFunc
  15. (JNIEnv *, jobject, jint, jint);

  16. #ifdef __cplusplus
  17. }
  18. #endif


复制代码
0 0