Android Jni 使用篇

1.引言

我们知道,Android系统的底层库由c/c++编写,上层Android应用程序通过Java虚拟机调用底层接口,衔接底层c/c++库与Java应用程序间的接口正是JNI(Java Native Interface)。本文描述了如何在ubuntu下配置Android JNI的开发环境,以及如何编写一个简单的c函数库和JNI接口,并通过编写Java程序调用这些接口,最终运行在模拟器上的过程。

2.环境配置

2.1.安装jdk 1.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 

#JAVA EVIRENMENT

export JAVA_HOME=/usr/lib/java/jdk1.6.0_29

export JRE_HOME=$JAVA_HOME/jre 

export CLASSPATH=$JAVA_HOME/lib:$JRE_HOME/lib:$CLASSPATH

export PATH=$JAVA_HOME/bin:$JRE_HOME/bin:$PATH 

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

(4)验证安装

$ java -version

java version "1.6.0_29"

Java(TM) SE Runtime Environment (build 1.6.0_29-b11)

Java HotSpot(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)AVD

(3)Android SDK 

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

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

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

$ sudo vim /etc/profile 

export PATH=~/software/android/android-sdk-linux/tools:$PATH 

export PATH=~/software/android/android-sdk-linux/platform-tools:$PATH 

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

2.3.安装NDK

NDK是由android提供的编译android本地代码的一个工具。

(1)从android ndk官网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/profile

export PATH=/usr/local/ndk:$PATH

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

(4)验证安装

$ cd /usr/local/ndk/samples/hello-jni/

$ ndk-build 

Gdbserver : [arm-linux-androideabi-4.4.3] libs/armeabi/gdbserver

Gdbsetup : libs/armeabi/gdb.setup 

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文件如下:

<?xml version="1.0" encoding="utf-8"?> 

<resources>

<stringname="hello">Hello World, JNITestActivity!</string>

<string name="app_name">JNITest</string> 

<string name="btn_show">Show</string>

</resources>    

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

<?xmlversion="1.0"encoding="utf-8"?> 

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 

android:layout_width="fill_parent" 

android:layout_height="fill_parent"

android:orientation="vertical" >

<TextView

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="@string/hello" />  

<EditText

android:id="@+id/ed_name"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_gravity="center_horizontal"

android:layout_marginLeft="5dp"

android:layout_marginRight="5dp" />  

<Button 

android:id="@+id/btn_show"

android:layout_width="109dp" 

android:layout_height="wrap_content"

android:layout_gravity="center_horizontal"

android:text="@string/btn_show" /> 

</LinearLayout> 

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

(3)编辑JNITest.java文件

package org.tonny.jni;

import android.app.Activity;

import android.os.Bundle;

import android.view.View;

 import android.widget.EditText;

import android.widget.Button;

 

publicclass JNITest extends Activity {

static{

System.loadLibrary("JNITest");

}

privatenative String GetReply();

private EditText edtName;

private Button btnShow;

String reply;

/** Called when the activity is first created. */

@Override

publicvoid onCreate(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(new Button.OnClickListener() {

publicvoid onClick(View arg0) {

edtName.setText(reply);

}

}); 

}

}

我们看这一段代码:

 

static{ 

System.loadLibrary("JNITest");

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

再看这一段:

privatenative String GetReply();

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

 publicvoid onClick(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 

$ ls

org org_tonny_jni_JNITest.h

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

$ javah -classpath ~/project/Android/JNITest/bin/classes org.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<jni.h>

#include<string.h> 

#include"org_tonny_jni_JNITest.h"

JNIEXPORT jstring JNICALL Java_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 := libJNITest

LOCAL_SRC_FILES := org_tonny_jni_JNITest.c

 

include $(BUILD_SHARED_LIBRARY)

LOCAL_MODULE表示编译的动态库名称

LOCAL_SRC_FILES 表示源代码文件 

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

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

$ cd ~/project/Android/JNITest/

$ ndk-build

Invalid attribute name:

package

Install : 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->AVD Manager下的AVD Name对应,-partition-size表示模拟器的存储设备容量。

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

$ cd ~/project/Android/JNITest/libs/armeabi/ 

$ adb remount 

$ adb push libJNITest.so /system/lib

80 KB/s (10084 bytes in 0.121s)

当在终端上看到有80 KB/s (10084 bytes in 0.121s) 传输速度等信息的时候,说明拷贝成功。

(3)在终端上执行JNITest程序,这个我们可以在Eclipse下,右键点击JNITest工程,Run As->Android Application,即可在模拟器上启动程序,执行效果如下:

在模拟器上点击【Show】按钮,即可看到Hello,JNITest,而这个字符串正是我们在org_tonny_jni_JNITest.c代码文件中所定义的。

OK,现在大功告成!

 4.参考文章

《Android JNI开发入门之一》http://my.unix-center.net/~Simon_fu/?p=833

《Android Jni代码示例讲解》:http://www.2cto.com/kf/201111/111052.html

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值