Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 253|回复: 0

[默认分类] 如何在Android下使用JNI

[复制链接]
  • TA的每日心情
    开心
    2021-12-13 21:45
  • 签到天数: 15 天

    [LV.4]偶尔看看III

    发表于 2018-5-22 12:42:01 | 显示全部楼层 |阅读模式




    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安装文件

      
    1. $chmod a+x jdk-6u29-linux-i586.bin
    2. $jdk-6u29-linux-i586.bin
    复制代码
      

      (3)
      配置jdk
      环境变量
      

      

      

      
    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
      )验证安装
      

      

      

      
    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类似,依次安装好以下软件即可:
    (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的解压目录。

      
    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到工作目录:

      
    1. $tar -xvf android-ndk-r6b-linux-x86.tar.bz2
    2. $sudo mv android-ndk-r6b /usr/local/ndk
    复制代码
      

      3)设置
      ndk环境变量
      

      

      

    1. $sudo vim /etc/profile
    2. exportPATH=/usr/local/ndk:$PATH
    复制代码
      

      

      

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

      

      
    (4)验证安装

    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工程
    Project:JNITest
    Package:org.tonny.jni
    Activity:JNITest
    (2)编辑资源文件


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

    1. <?xmlversion="1.0"encoding="utf-8"?>
    2. <resources>
    3. <stringname="hello">HelloWorld, JNITestActivity!</string>
    4. <stringname="app_name">JNITest</string>
    5. <stringname="btn_show">Show</string>
    6. </resources>
    复制代码


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



    1. <?xmlversion="1.0"encoding="utf-8"?>
    2. <LinearLayoutxmlns: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文件

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




    我们看这一段代码:

    1. static{
    2. System.loadLibrary("JNITest");
    3. }
    复制代码


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


    再看这一段:

    1. privatenativeString GetReply();
    复制代码

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

    1. publicvoidonClick(View arg0) {
    2. edtName.setText(reply);
    3. }
    复制代码


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


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


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

      

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

    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类。
    下面继续:

    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目录下的话,也可以这样:

    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文件,编辑代码如下:

    1. #include<jni.h>
    2. #include<string.h>
    3. #include"org_tonny_jni_JNITest.h"
    4. JNIEXPORTjstring JNICALLJava_org_tonny_jni_JNITest_GetReply
    5. (JNIEnv *env, jobject obj){
    6. return(*env)->NewStringUTF(env,(char*)"Hello,JNITest");
    7. }
    复制代码


    我们可以看到,该函数的实现相当简单,返回一个字符串为:"Hello,JNITest"
    3.4.编写Android.mk文件
    在~/project/Android/JNITest/jni目录下新建Android.mk文件,android可以根据这个文件的编译参数编译模块。编辑Android.mk文件如下:

    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文件。

    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命令:

    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目录下,执行以下命令:

    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代码示例讲解
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-5-16 18:35 , Processed in 0.392723 second(s), 46 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表