如何在Android下使用JNI

简介:

1.引言

我们知道,Android系统的底层库由c/c++编写,上层Android应用程序通过Java虚拟机调用底层接口,衔接底层c/c++库与Java应用程序间的接口正是JNIJavaNative 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安装文件

[html]  view plain copy
  1. $chmod a+x jdk-6u29-linux-i586.bin  
  2. $jdk-6u29-linux-i586.bin  

(3) 配置jdk 环境变量

[html]  view plain copy
  1. $sudo vim /etc/profile  
  2. #JAVAEVIRENMENT  
  3. exportJAVA_HOME=/usr/lib/java/jdk1.6.0_29  
  4. exportJRE_HOME=$JAVA_HOME/jre  
  5. exportCLASSPATH=$JAVA_HOME/lib:$JRE_HOME/lib:$CLASSPATH  
  6. exportPATH=$JAVA_HOME/bin:$JRE_HOME/bin:$PATH  

保存后退出编辑,并重启系统。

4 )验证安装

[html]  view plain copy
  1. $java -version  
  2. javaversion "1.6.0_29"  
  3. Java(TM)SE Runtime Environment (build 1.6.0_29-b11)  
  4. JavaHotSpot(TM) Server VM (build 20.4-b02, mixed mode)  
  5. $javah  
  6. 用法:javah[选项]<>  
  7. 其中[选项]包括:  
  8. -help输出此帮助消息并退出  
  9. -classpath<路径>用于装入类的路径  
  10. -bootclasspath<路径>用于装入引导类的路径  
  11. -d<目录>输出目录  
  12. -o<文件>输出文件(只能使用-d或-o中的一个)  
  13. -jni生成JNI样式的头文件(默认)  
  14. -version输出版本信息  
  15. -verbose启用详细输出  
  16. -force始终写入输出文件  
  17. 使用全限定名称指定<>(例  
  18. 如,java.lang.Object)。  

2.2.安装android应用程序开发环境

ubuntu下安装android应用程序开发环境与windows类似,依次安装好以下软件即可:

1Eclipse

2ADT

3AndroidSDK

windows下安装唯一不同的一点是,下载这些软件的时候要下载Linux版本的安装包。

安装好以上android应用程序的开发环境后,还可以选择是否需要配置emulatoradb工具的环境变量,以方便在进行JNI开发的时候使用。配置步骤如下:

emulator所在目录android-sdk-linux/tools以及adb所在目录android-sdk-linux/platform-tools添加到环境变量中,android-sdk-linuxandroidsdk安装包android-sdk_rxx-linux的解压目录。

[plain]  view plain copy
  1. $sudo vim /etc/profile  
  2. exportPATH=~/software/android/android-sdk-linux/tools:$PATH  
  3.  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到工作目录:

[plain]  view plain copy
  1. $tar -xvf android-ndk-r6b-linux-x86.tar.bz2  
  2. $sudo mv android-ndk-r6b /usr/local/ndk  

3)设置 ndk环境变量

[plain]  view plain copy
  1. $sudo vim /etc/profile  
  2. exportPATH=/usr/local/ndk:$PATH  

编辑完毕后保存退出,并重启生效

4)验证安装

[plain]  view plain copy
  1. $ cd/usr/local/ndk/samples/hello-jni/  
  2. $ ndk-build  
  3. Gdbserver : [arm-linux-androideabi-4.4.3] libs/armeabi/gdbserver  
  4. Gdbsetup : libs/armeabi/gdb.setup  
  5. Install : libhello-jni.so => libs/armeabi/libhello-jni.so  

3.JNI实现

我们需要定义一个符合JNI接口规范的c/c++接口,这个接口不用太复杂,例如输出一个字符串。接下来,则需要把c/c++接口的代码文件编译成共享库(动态库).so文件,并放到模拟器的相关目录下。最后,启动Java应用程序,就可以看到最终效果了。

3.1.编写Java应用程序代码

1)启动Eclipse,新建android工程

ProjectJNITest

Packageorg.tonny.jni

ActivityJNITest

2)编辑资源文件


编辑res/values/strings.xml文件如下:

[html]  view plain copy
  1. <?xmlversionxmlversion="1.0"encoding="utf-8"?>  
  2. <resources>  
  3. <stringnamestringname="hello">HelloWorld, JNITestActivity!</string>  
  4. <stringnamestringname="app_name">JNITest</string>  
  5. <stringnamestringname="btn_show">Show</string>  
  6. </resources>  

编辑res/layout/main.xml文件如下:


[html]  view plain copy
  1. <?xmlversionxmlversion="1.0"encoding="utf-8"?>  
  2. <LinearLayoutxmlns:androidLinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"  
  3. android:layout_width="fill_parent"  
  4. android:layout_height="fill_parent"  
  5. android:orientation="vertical">  
  6. <TextView  
  7. android:layout_width="fill_parent"  
  8. android:layout_height="wrap_content"  
  9. android:text="@string/hello"/>  
  10. <EditText  
  11. android:id="@+id/ed_name"  
  12. android:layout_width="match_parent"  
  13. android:layout_height="wrap_content"  
  14. android:layout_gravity="center_horizontal"  
  15. android:layout_marginLeft="5dp"  
  16. android:layout_marginRight="5dp"/>  
  17. <Button  
  18. android:id="@+id/btn_show"  
  19. android:layout_width="109dp"  
  20. android:layout_height="wrap_content"  
  21. android:layout_gravity="center_horizontal"  
  22. android:text="@string/btn_show"/>  
  23. </LinearLayout>  

我们在主界面上添加了一个EditText控件和一个Button控件。


3)编辑JNITest.java文件

[java]  view plain copy
  1. packageorg.tonny.jni;  
  2.   
  3. importandroid.app.Activity;  
  4. importandroid.os.Bundle;  
  5. importandroid.view.View;  
  6. importandroid.widget.EditText;  
  7. importandroid.widget.Button;  
  8.   
  9.   
  10. publicclassJNITestextendsActivity {  
  11. static{  
  12. System.loadLibrary("JNITest");  
  13. }  
  14. privatenativeString GetReply();  
  15. privateEditTextedtName;  
  16. privateButtonbtnShow;  
  17. Stringreply;  
  18. /**Called when the activity is first created. */  
  19. @Override  
  20. publicvoidonCreate(Bundle savedInstanceState) {  
  21. super.onCreate(savedInstanceState);  
  22. setContentView(R.layout.main);  
  23. reply= GetReply();  
  24. edtName= (EditText)this.findViewById(R.id.ed_name);  
  25. btnShow= (Button)this.findViewById(R.id.btn_show);  
  26. btnShow.setOnClickListener(newButton.OnClickListener() {  
  27. publicvoidonClick(View arg0) {  
  28. edtName.setText(reply);  
  29. }  
  30. });  
  31. }  
  32. }  


我们看这一段代码:

[java]  view plain copy
  1. static{  
  2. System.loadLibrary("JNITest");  
  3. }  

static表示在系统第一次加载类的时候,先执行这一段代码,在这里表示加载动态库libJNITest.so文件。


再看这一段:

[java]  view plain copy
  1. privatenativeString GetReply();  

native表示这个方法由本地代码定义,需要通过jni接口调用本地c/c++代码。

[java]  view plain copy
  1. publicvoidonClick(View arg0) {  
  2. edtName.setText(reply);  
  3. }  

这段代码表示点击按钮后,把native方法的返回的字符串显示到EditText控件。


4)编译工程,生成.class文件。


3.2.用javah工具生成符合JNI规范的c语言头文件


在终端中,进入android工程所在的bin目录

[plain]  view plain copy
  1. $cd ~/project/Android/JNITest/bin  

我们用ls命令查看,可以看到bin目录下有个classes目录,其目录结构为classes/org/tonny/jni,即classes的子目录结构是android工程的包名org.tonny.jni。请注意,下面我们准备执行javah命令的时候,必须进入到org/tonny/jni的上级目录,即classes目录,否则javah会提示找不到相关的java类。

下面继续:

[plain]  view plain copy
  1. $cd classes  
  2. $javah org.tonny.jni.JNITest  
  3. $ls  
  4. org org_tonny_jni_JNITest.h  

执行javahorg.tonny.jni.JNITest命令,在classes目录下会生成org_tonny_jni_JNITest.h头文件。如果不进入到classes目录下的话,也可以这样:

[plain]  view plain copy
  1. $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文件,编辑代码如下:

[cpp]  view plain copy
  1. #include<jni.h>  
  2. #include<string.h>  
  3. #include"org_tonny_jni_JNITest.h"  
  4.   
  5.   
  6. JNIEXPORTjstring JNICALLJava_org_tonny_jni_JNITest_GetReply  
  7. (JNIEnv *env, jobject obj){  
  8. return(*env)->NewStringUTF(env,(char*)"Hello,JNITest");  
  9. }  

我们可以看到,该函数的实现相当简单,返回一个字符串为:"Hello,JNITest"

3.4.编写Android.mk文件

~/project/Android/JNITest/jni目录下新建Android.mk文件,android可以根据这个文件的编译参数编译模块。编辑Android.mk文件如下:

[plain]  view plain copy
  1. LOCAL_PATH:= $(call my-dir)  
  2. include$(CLEAR_VARS)  
  3. LOCAL_MODULE := libJNITest  
  4. LOCAL_SRC_FILES:= org_tonny_jni_JNITest.c  
  5. include$(BUILD_SHARED_LIBRARY)  

LOCAL_MODULE表示编译的动态库名称

LOCAL_SRC_FILES 表示源代码文件

3.5.用ndk工具编译并生成.so文件

进入到JNITest的工程目录,执行ndk-build命令即可生成libJNITest.so文件。

[plain]  view plain copy
  1. $cd ~/project/Android/JNITest/  
  2. $ndk-build  
  3. Invalidattribute name:  
  4. package  
  5. Install : libJNITest.so => libs/armeabi/libJNITest.so  

可以看到,在工程目录的libs/armeabi目录下生成了libJNITest.so文件。

3.6.在模拟器上运行

1)首先,我们把android模拟器启动起来。进入到emulator所在目录,执行emulator命令:

[plain]  view plain copy
  1. $cd ~/software/android/android-sdk-linux/tools  
  2. $./emulator @AVD-2.3.3-V10 -partition-size 512  

AVD-2.3.3-V10表示你的模拟器名称,与在Eclipse->AVDManager下的AVDName对应,-partition-size表示模拟器的存储设备容量。


2)接下来,我们需要把libJNITest.so文件拷贝到模拟器的/system/lib目录下,执行以下命令:

[plain]  view plain copy
  1. $cd ~/project/Android/JNITest/libs/armeabi/  
  2. $adb remount  
  3. $adb push libJNITest.so /system/lib  
  4. 80 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代码示例讲解

相关实践学习
使用CLup和iSCSI共享盘快速体验PolarDB for PostgtreSQL
在Clup云管控平台中快速体验创建与管理在iSCSI共享盘上的PolarDB for PostgtreSQL。
AnalyticDB PostgreSQL 企业智能数据中台:一站式管理数据服务资产
企业在数据仓库之上可构建丰富的数据服务用以支持数据应用及业务场景;ADB PG推出全新企业智能数据平台,用以帮助用户一站式的管理企业数据服务资产,包括创建, 管理,探索, 监控等; 助力企业在现有平台之上快速构建起数据服务资产体系
相关文章
|
21天前
|
Android开发
Android JNI与CAN通信遇到的问题总结
Android JNI与CAN通信遇到的问题总结
37 1
|
22天前
|
Android开发
Android JNI 报错(signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr )
Android JNI 报错(signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr )
65 1
|
22天前
|
传感器 Java 开发工具
[NDK/JNI系列03] Android Studio集成NDK开发环境
[NDK/JNI系列03] Android Studio集成NDK开发环境
22 0
|
5月前
|
Java 开发工具 Android开发
OpenCV(一):Android studio jni配置OpenCV(亲测有效,保姆级)
OpenCV(一):Android studio jni配置OpenCV(亲测有效,保姆级)
180 0
|
2月前
|
Android开发
[Android jni] Bitmap与Mat对象的相互转换
[Android jni] Bitmap与Mat对象的相互转换
64 0
|
8月前
|
Java Android开发 C++
Android中的JNI开发,你了解多少?
Android中的JNI开发,你了解多少?
69 0
|
9月前
|
Java Android开发
Android JNI开发从0到1,java调C,C调Java,保姆级教程详解
Android JNI开发从0到1,java调C,C调Java,保姆级教程详解
65 1
|
5月前
|
Java 开发工具 Android开发
[Android]JNI的基础知识
[Android]JNI的基础知识
52 0
[Android]JNI的基础知识
|
5月前
|
算法 Java 数据安全/隐私保护
Android App开发之利用JNI实现加密和解密操作实战(附源码 简单易懂)
Android App开发之利用JNI实现加密和解密操作实战(附源码 简单易懂)
79 0
|
5月前
|
XML Java API
Android App开发之创建JNI接口获取CPU指令集讲解及实战(附源码 简单易懂)
Android App开发之创建JNI接口获取CPU指令集讲解及实战(附源码 简单易懂)
46 0