如何在Android下使用JNI
1.引言
我们知道,Android系统的底层库由c/c++编写,上层Android应用程序通过Java虚拟机调用底层接口,衔接底层c/c++库与Java应用程序间的接口正是JNI(JavaNative Interface)。本文描述了如何在ubuntu下配置AndroidJNI的开发环境,以及如何编写一个简单的c函数库和JNI接口,并通过编写Java程序调用这些接口,最终运行在模拟器上的过程。
2.环境配置
2.1.安装jdk1.6
(1)从jdk官方网站http://www.oracle.com/technetwork/java/javase/downloads/jdk-6u29-download-513648.html下载jdk-6u29-linux-i586.bin文件。
(2)执行jdk安装文件
$chmod a+x jdk-6u29-linux-i586.bin$jdk-6u29-linux-i586.bin
(3) 配置jdk 环境变量
$sudo vim /etc/profile#JAVAEVIRENMENTexportJAVA_HOME=/usr/lib/java/jdk1.6.0_29exportJRE_HOME=$JAVA_HOME/jreexportCLASSPATH=$JAVA_HOME/lib:$JRE_HOME/lib:$CLASSPATHexportPATH=$JAVA_HOME/bin:$JRE_HOME/bin:$PATH
保存后退出编辑,并重启系统。
(4 )验证安装
$java -versionjavaversion "1.6.0_29"Java(TM)SE Runtime Environment (build 1.6.0_29-b11)JavaHotSpot(TM) Server VM (build 20.4-b02, mixed mode)$javah用法:javah[选项]<类>其中[选项]包括:-help输出此帮助消息并退出-classpath<路径>用于装入类的路径-bootclasspath<路径>用于装入引导类的路径-d<目录>输出目录-o<文件>输出文件(只能使用-d或-o中的一个)-jni生成JNI样式的头文件(默认)-version输出版本信息-verbose启用详细输出-force始终写入输出文件使用全限定名称指定<类>(例如,java.lang.Object)。
2.2.安装android应用程序开发环境
ubuntu下安装android应用程序开发环境与windows类似,依次安装好以下软件即可:
(1)Eclipse
(2)ADT
(3)AndroidSDK
与windows下安装唯一不同的一点是,下载这些软件的时候要下载Linux版本的安装包。
安装好以上android应用程序的开发环境后,还可以选择是否需要配置emulator和adb工具的环境变量,以方便在进行JNI开发的时候使用。配置步骤如下:
把emulator所在目录android-sdk-linux/tools以及adb所在目录android-sdk-linux/platform-tools添加到环境变量中,android-sdk-linux指androidsdk安装包android-sdk_rxx-linux的解压目录。
$sudo vim /etc/profileexportPATH=~/software/android/android-sdk-linux/tools:$PATH exportPATH=~/software/android/android-sdk-linux/platform-tools:$PATH
编辑完毕后退出,并重启生效。
2.3.安装NDK
NDK是由android提供的编译android本地代码的一个工具。
(1)从androidndk官网http://developer.android.com/sdk/ndk/index.html下载ndk,目前最新版本为android-ndk-r6b-linux-x86.tar.bz2.
(2)解压ndk到工作目录:
$tar -xvf android-ndk-r6b-linux-x86.tar.bz2$sudo mv android-ndk-r6b /usr/local/ndk
( 3)设置 ndk环境变量
$sudo vim /etc/profileexportPATH=/usr/local/ndk:$PATH
编辑完毕后保存退出,并重启生效
(4)验证安装
$ cd/usr/local/ndk/samples/hello-jni/$ ndk-buildGdbserver : [arm-linux-androideabi-4.4.3] libs/armeabi/gdbserverGdbsetup : libs/armeabi/gdb.setupInstall : libhello-jni.so => libs/armeabi/libhello-jni.so
3.JNI实现
我们需要定义一个符合JNI接口规范的c/c++接口,这个接口不用太复杂,例如输出一个字符串。接下来,则需要把c/c++接口的代码文件编译成共享库(动态库).so文件,并放到模拟器的相关目录下。最后,启动Java应用程序,就可以看到最终效果了。
3.1.编写Java应用程序代码
(1)启动Eclipse,新建android工程
Project:JNITest
Package:org.tonny.jni
Activity:JNITest
(2)编辑资源文件
编辑res/values/strings.xml文件如下:
<?xmlversion="1.0"encoding="utf-8"?>HelloWorld, JNITestActivity!JNITestShow
编辑res/layout/main.xml文件如下:
<?xmlversion="1.0"encoding="utf-8"?>
我们在主界面上添加了一个EditText控件和一个Button控件。
(3)编辑JNITest.java文件
packageorg.tonny.jni;importandroid.app.Activity;importandroid.os.Bundle;importandroid.view.View;importandroid.widget.EditText;importandroid.widget.Button;publicclassJNITestextendsActivity {static{System.loadLibrary("JNITest");}privatenativeString GetReply();privateEditTextedtName;privateButtonbtnShow;Stringreply;/**Called when the activity is first created. */@OverridepublicvoidonCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);reply= GetReply();edtName= (EditText)this.findViewById(R.id.ed_name);btnShow= (Button)this.findViewById(R.id.btn_show);btnShow.setOnClickListener(newButton.OnClickListener() {publicvoidonClick(View arg0) {edtName.setText(reply);}});}}
我们看这一段代码:
static{System.loadLibrary("JNITest");}
static表示在系统第一次加载类的时候,先执行这一段代码,在这里表示加载动态库libJNITest.so文件。
再看这一段:
privatenativeString GetReply();
native表示这个方法由本地代码定义,需要通过jni接口调用本地c/c++代码。
publicvoidonClick(View arg0) {edtName.setText(reply);}
这段代码表示点击按钮后,把native方法的返回的字符串显示到EditText控件。
(4)编译工程,生成.class文件。
3.2.用javah工具生成符合JNI规范的c语言头文件
在终端中,进入android工程所在的bin目录
$cd ~/project/Android/JNITest/bin
我们用ls命令查看,可以看到bin目录下有个classes目录,其目录结构为classes/org/tonny/jni,即classes的子目录结构是android工程的包名org.tonny.jni。请注意,下面我们准备执行javah命令的时候,必须进入到org/tonny/jni的上级目录,即classes目录,否则javah会提示找不到相关的java类。
下面继续:
$cd classes$javah org.tonny.jni.JNITest$lsorg org_tonny_jni_JNITest.h
执行javahorg.tonny.jni.JNITest命令,在classes目录下会生成org_tonny_jni_JNITest.h头文件。如果不进入到classes目录下的话,也可以这样:
$javah -classpath ~/project/Android/JNITest/bin/classesorg.tonny.jni.JNITest
-classpath 参数表示装载类的目录。
3.3.编写c/c++代码
生成org_tonny_jni_JNITest.h头文件后,我们就可以编写相应的函数代码了。下面在android工程目录下新建jni目录,即~/project/Android/JNITest/jni,把org_tonny_jni_JNITest.h头文件拷贝到jni目录下,并在jni目录下新建org_tonny_jni_JNITest.c文件,编辑代码如下:
#include#include#include"org_tonny_jni_JNITest.h"JNIEXPORTjstring JNICALLJava_org_tonny_jni_JNITest_GetReply(JNIEnv *env, jobject obj){return(*env)->NewStringUTF(env,(char*)"Hello,JNITest");}
我们可以看到,该函数的实现相当简单,返回一个字符串为:"Hello,JNITest"
3.4.编写Android.mk文件
在~/project/Android/JNITest/jni目录下新建Android.mk文件,android可以根据这个文件的编译参数编译模块。编辑Android.mk文件如下:
LOCAL_PATH:= $(call my-dir)include$(CLEAR_VARS)LOCAL_MODULE := libJNITestLOCAL_SRC_FILES:= org_tonny_jni_JNITest.cinclude$(BUILD_SHARED_LIBRARY)
LOCAL_MODULE表示编译的动态库名称
LOCAL_SRC_FILES 表示源代码文件
3.5.用ndk工具编译并生成.so文件
进入到JNITest的工程目录,执行ndk-build命令即可生成libJNITest.so文件。
$cd ~/project/Android/JNITest/$ndk-buildInvalidattribute name:packageInstall : libJNITest.so => libs/armeabi/libJNITest.so
可以看到,在工程目录的libs/armeabi目录下生成了libJNITest.so文件。
3.6.在模拟器上运行
(1)首先,我们把android模拟器启动起来。进入到emulator所在目录,执行emulator命令:
$cd ~/software/android/android-sdk-linux/tools$./emulator @AVD-2.3.3-V10 -partition-size 512
AVD-2.3.3-V10表示你的模拟器名称,与在Eclipse->AVDManager下的AVDName对应,-partition-size表示模拟器的存储设备容量。
(2)接下来,我们需要把libJNITest.so文件拷贝到模拟器的/system/lib目录下,执行以下命令:
$cd ~/project/Android/JNITest/libs/armeabi/$adb remount$adb push libJNITest.so /system/lib80 KB/s (10084 bytes in 0.121s)
当在终端上看到有80 KB/s (10084 bytes in 0.121s)传输速度等信息的时候,说明拷贝成功。
(3)在终端上执行JNITest程序,这个我们可以在Eclipse下,右键点击JNITest工程,RunAs->Android Application,即可在模拟器上启动程序,执行效果如下:
在模拟器上点击【Show】按钮,即可看到Hello,JNITest,而这个字符串正是我们在org_tonny_jni_JNITest.c代码文件中所定义的。
OK,现在大功告成!
4.参考文章
AndroidJNI开发入门之一
AndroidJni代码示例讲解
更多相关文章
- Android中的一个简单的List应用
- android 滚动条颜色设置(android Progressbar color)
- Mac下Android配置及unity3d的导出Android
- Android(安卓)MVP设计模式实例详解
- android framework层 学习笔记(一)
- Android修改主题,去掉ActionBar、TitleBar
- Android学习总结 :自定义 View(一)
- Hack4-自定义PreferenceActivity界面
- Android(安卓)Studio编译jar架包必看