JNI – Java Native Interface: Java平台中一个用于和本地C代码进行互操作的API,称为Java本地接口。也就是用来实现java程序中调用C函数。实质是将本地C代码编译到一个动态装载库中(如libHello.so/Hello.dll)。
Android JNI: 在Android工程中调用C的过程,称为Native Development。
本文档通过在Android APP中调用C函数执行Liniux 的Shell命令来简单介绍一下Native Development。
一、 准备
1、OS:Windows
2、Eclipse:略。
3、NDK(Native Development Kit):用来将C/C++生成的原生代码库嵌入到.apk文件中。下载地址:android-ndk-1.6_r1 ,解压即可。此外,NDK中还包含了从C / C++生成原生代码库所需要的工具和build files。要执行这些build,需要借助下面的Cygwin工具。
4、 Cygwin:是一个在windows平台上运行的unix模拟环境,使用gnu工具集在windows上进行嵌入式系统开发的优秀工具。下面着重介绍其安装方法:
首先下载安装工具:http://www.cygwin.com/setup.exe
点击执行它,下面是几个关键步骤的截图:
点击下一步 –> 选择Install from Internet –> 下一步 –>选择安装目录 –> 下一步 –> 选择下载文件存放的目录 –> 下一步 –> 选择上网代理:Use Internet Explorer Proxy Settings –> 下一步出现下图:
选择一个下载网站,通过测试,从http://mirrors.163.com下载速度较快,如果列表中没有,可以Add进去。
然后点击下一步,就进入了一个漫长的阶段Downloading,完成后会出现下图:
将箭头指向的Devel@Default点成install然后下一步, 又进入一个漫长的阶段,包括解包,Checking,Running…
好几个小时后,该阶段结束,直接下一步点击完成安装。桌面出现图标:
点击测试,如果出现下图,则表示安装成功:
接下来用Cygwin执行NDK/build下的host-setup.sh完成NDK的安装。方法:在窗口中通过cd命令进入NDK目录,然后执行build/host-setup.sh.
user@lizq /cygdrive/e/eclipse/android-ndk-1.6_r1
$build/host-setup.sh
出现以上信息表示 Host setup complete。可以先编译一下NDK/apps文件夹中的工程实例,检查NDK是否正常工作.
选择apps/hello-jni做实验, 在NDK根目录下执行:make APP=hello-jni -B,其中-B参数是始终执行build, 不管C/C++代码是否有修改。如下图:
从执行结果中可以看到:
SharedLibrary : libhello-jni.so
Install : libhello-jni.so => apps/hello-jni/project/libs/armeabi
生成了一个动态装载库libhello-jni.so,并且在libs/目录下NDK新建了一个armeabi文件夹,然后把libhello-jni.so放进去。
二、工程实践 – MyJni
1. 在NDK/apps/目录下新建一个文件夹:MyJni
2. 在MyJni下新建一个文件夹:project,用来存放实际工程
3. 在MyJni下新建一个文件:Application.mk, 内容如下:
APP_PROJECT_PATH := $(call my-dir)/project
APP_MODULES := Hello #最终.so文件的名字,如(Hello对应libHello.so)
4.在Eclipse中新建一个Android工程,Location选择上面的../apps/Myjni/project
5. 代码:
HelloJni.java
package com.jni.hello;
public class HelloJni {
public native String cmd(String c);
}
执行javac HelloJni.java -> javah HelloJni,生成HelloJni.h文件, 内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloJni */
#ifndef _Included_ HelloJni
#define _Included_HelloJni
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloJni
* Method: cmd
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_ HelloJni_cmd
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
注意几个地方jstring和Ljava/lang/String;
在C和Java之间传递数据时,应该知道彼此之间的对应类型。如jstring 对应Ljava/lang/String;
6. 在project/目录下新建一个文件夹:jni,用来存放C/C++文件,把上面的HelloJni.h放进去。
7. 实现HelloJni.c
#include “HelloJni.h”
JNIEXPORT jstring JNICALL Java_HelloJni_cmd(JNIEnv * env, jobject obj, jstring cmd)
{
//略,参考附件源代码
}
8. 在jni目录下新建一个Android.mk, 内容如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := Hello #最终.so文件的名字,如(Hello对应libHello.so)
LOCAL_SRC_FILES := HelloJni.c
LOCAL_LDLIBS += -llog
include $(BUILD_SHARED_LIBRARY)
9. 用Cygwin编译生成动态装载库
打开Cygwin,cd进入NDK目录,如/cygdrive/e/eclipse/android-ndk-1.6_r1
执行make APP=MyJin,如图:
生成了libHello.so,并被放到../libs/armeabi目录下。
10. 在Android工程中调用JNI,如下Hello.java 代码片段:
public class Hello extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//…
System.loadLibrary("Hello"); //对应libHello.so
HelloJni hjni = new HelloJni();
String cmd = “cat /proc/meminfo”; //shell 命令,查看内存信息
_tv.setText("excute " + cmd + "\n" + hjni.cmd(cmd)); //在界面上显示执行结果
setContentView(_tv);
}
}
11. 模拟器运行结果:
12.源代码及工具包
参考附件。
一、
二、
三、 总结/问题
本文档是Android工程使用JNI之实践的初级篇,许多地方都是按部就班,比较死板。像
下面的一些问题都没有做深入的研究。
1. 为什么一定要把工程放在NDK/apps/下?
2. 如果工程的目录结构变化了,该如何写Application.mk和Android.mk?
能否将Cygwin和NDK都“集成”到Eclipse中,实现一键编译?