您的位置:首页 > 移动开发 > Android开发

Android JNI操作指南

2012-07-13 15:17 525 查看
近日在研究Android JNI方面的东西,Android JNI又名:NDK(Native Development Kit),它是开发JNI的一个工具,JNI是jave本地接口,它是通过java程序调用本地的C/C++程序,使程序的执行效率在一定程度上有了很大的提高。

一、首先,介绍NDK环境的搭建过程及遇到的一些问题。

1、下载并安装Cygwin工具。

问:为什么要安装这个工具,这工具有什么用呢?

答:安装Cygwin工具是为了之后编译我们自己写的C/C++代码的,因为C/C++代码的编译需要在linux环境下进行,而我们是在window下进行开发的,因此Cygwin就是一个在Window下的linux系统工具,我们只要安装它就可以使用linux系统来编译JNI本地代码了。

下载地址:http://cygwin.com/setup.exe



执行setup.exe,因为安装软件很容易,所以我只介绍几个关键的步骤其他的按默认“下一步”就可以了。





这里《选择安装方式》。



这里《选择安装Cygwin的路径》。



这里《选择下载包的安装路径》,这个下载包可以保留的,因为在《选择安装方式》有个install from Local Directory就是从本地安装,这样就避免下次安装时又要从网上下,这样很慢的。



这里《选择下载包网址》,没有可以自己Add一个,不过这么多还不够你选的么

不过有些站点的连接速度很慢甚至有的会断,看你运气了,我选择的是上面那个。



这里《选择要安装的包》点击圈

就会变成

这个Devel里面有我们JNI需要的编译C/C++的make和gcc等工具。



这里就是《在线安装》了,在这里我等待了一个上午的时间才等到出现下一步然后点击完成的。这还算是好的,运气不好的可以要一天吧




桌面出现上快捷图标表明基本安装成功,为什么是基本呢因为还没检查make和gcc是否安装成功了,点进去看看吧。



这样才表明真正的Cygwin安装成功了。

2、下载并解压NDK工具。

NDK是开发JNI的工具,NDK提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应用一起打包成apk,NDK的发布,使“Java+C”的开发方式终于转正,成为官方支持的开发方式。

下载地址:http://dl.google.com/android/ndk/android-ndk-r8-windows.zip


解压到D:\Android\下。

接下来就是配置NDK了,在这里我遇到一个问题就是按网上的许多文章和书籍都是说点击进入Cygwin通过命令:cd /cygwin/d/Android/android-ndk-r8/下执行build/host-setup.sh如下图:



这样就表明安装成功了,ndk可以用了,如果出现的是“Please run this script from the top-level NDK directory as in: cd $NDKROOT build/host-setup.sh"信息表明需要配置NDK路径,需要修改Cygwin目录下的home/username/下的

,在文件尾加下下面两句:

NDKROOT=/cygdrive/d/androidsdktools/android-ndk-1.5_r1 //NDK路径

export NDKROOT

保存就可以了。

可是我的结果是如下:



可是没有host-setup.sh这个文件,我到android-ndk-r8下的build目录查看真是没有的。那怎么办呢,急死我了

后来在网上找到了一篇好文章解决了我的问题,他说:ndk_r1~ndk_r4版本是网上的许多文章和书籍说的那样,因为他它们的ndk/build/下有host-setup.sh这个文件,但ndk_r5之后的就没有了,而且执行build/host-setup.sh只是一个检测ndk安装是否成功的方法不是必须的,真正的是配置ndk路径才有用而且编译方法也不一样,r5之前是在ndk根目录下用make
APP = hello-jni来编译,而r5之后不是这样的,之后会讲到,所以我这就到Cygwin安装目录C:\cygwin\home\Administrator\下修改

也是在尾部加入如下两行:

NDKROOT=/cygdrive/d/Android/android-ndk-r8/ //我的NDK路径

export NDKROOT

保存就好了,这样NDK配置工作就这样完成了。

还有个问题是我之前把NDK放在了D:\Program Files\Android\android-ndk-r8\这是不可以的因为Pogram Files这间有空格,所以以后最好不要把ndk放在有空格的路径下。

验证NDK是否安装成功我们就编译下android-ndk-r8下的例子吧这里介绍了jni的编译方法是不是和网上还有书上说的不一样啊??

如下图:



表明NDK安装成功。这里hello-jni是个工程java工程,jni是工程下的一个目录存放所有的C/C++代码和.mk文件,上面执行后会在工程下自动生成libs目录,我们的共享库


会在它下面。这个文件就是我们的JNI我们用C/C++实现的代码供java调用的库。

二、JNI编程讲解

JNI编程技术一般是用来实现一些接口类的,因为那些接口可能是要通过C/C++代码来实现的,所以就这用到JNI编程技术了。

我们这里用一个TestJni的例子为例简单介绍JNI的实现步骤:先看看我的JNI工程的最终目录结构吧:



这个目录结构是不是很像Android工程啊,就是一个Android工程只不过是多个jni、libs、obj等三个目录。

第一步:建一个Android工程工程名TestJni。

工程保存路径很重要路径也不能有空格不然编译是不能通过的,我的工程路径:D:\Android\workspace\目录下。



第二步:在包【com.pve.testjni】下建立一个接口类文件取名【JniInterfaces.java】

注:【】表示可以自定义。文件内容如下:

package com.pve.testjni;

public class JniInterfaces {
static{
System.loadLibrary("interfaces-jni");  //加载C/C++编译生的库名
}
public native int getCInt();           //接口方法,得到一个整形数据

public native String getCString();     //接口方法,得到一串字符串数据

}


第三步:生成C/C++的接口类的头文件。

进入window控制台检查javah是否安装成功,javah是之前在搭建android开发环境时首先要装的jdk中的一个工具,作用:就java语言 C 头文件和 stub 文件生成器:



进入工程目录执行javah com.pve.testjni.JniInterfaces在classes下生了接口类的头文件

,在工程目录下建了一个jni目录把它拷到jni目录下,之后刷新下自己的Android工程就会多出之前看到jni文件夹了。



com_pve_testjni_JniInterfaces.h代码如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_pve_testjni_JniInterfaces */

#ifndef _Included_com_pve_testjni_JniInterfaces
#define _Included_com_pve_testjni_JniInterfaces
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class:     com_pve_testjni_JniInterfaces
* Method:    getCInt
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_pve_testjni_JniInterfaces_getCInt                 //接口方法,得到一个整形数的C语言函数声明
(JNIEnv *, jobject);

/*
* Class:     com_pve_testjni_JniInterfaces
* Method:    getCString
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_pve_testjni_JniInterfaces_getCString           //接口方法,得到一个字符串的C语言函数声明
(JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif


第四步:在jni目录下编写C/C++文件实现接口类中的接口。

有两种方式来编写C/C++代码,一种是新建一个com_pve_testjni_JniInterfaces.c文件,在里面实现上面两接口方法并加入头文件#include "com_pve_testjni_JniInterfaces.h"

第二种:直接把com_pve_testjni_JniInterfaces.h文件改成com_pve_testjni_JniInterfaces.c文件并在里面实现。

这里我选择的是第二种方法,com_pve_testjni_JniInterfaces.c的代码如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <stdio.h>
#include <stdlib.h>
#include <jni.h>
/* Header for class com_pve_testjni_JniInterfaces */

#ifndef _Included_com_pve_testjni_JniInterfaces
#define _Included_com_pve_testjni_JniInterfaces
#ifdef __cplusplus
extern "C" {
#endif

int sum()
{
int x,y;
x = 1000;
y = 2399;
x += y;
return x;
}
/*
* Class:     com_pve_testjni_JniInterfaces
* Method:    getCInt
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_pve_testjni_JniInterfaces_getCInt                //接口方法,得到一个整形数的C语言函数实现
(JNIEnv *env, jobject thiz)
{
return sum();
}

/*
* Class:     com_pve_testjni_JniInterfaces
* Method:    getCString
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_pve_testjni_JniInterfaces_getCString          //接口方法,得到一个字符串的C语言函数实现
(JNIEnv *env, jobject thiz)
{
(*env)->NewStringUTF(env, " HelloNDK--------->>> ");
}

#ifdef __cplusplus
}
#endif
#endif
其实这个C文件我总结了一下:如果你看明白了这个C的函数命名规则和java数据类型与C/C++数据类型对照表,就可以直接写C文件省去上面生成C/C++的接口类的头文件的步骤了,这只是建议jni编程高手是这样做的,新手还是一步步来吧



函数名生成规则为:Java[ _包名]_类名_方法名[ _函数签名](其中[ ]是可选项),均以字符下划线( _ )分割,其他都一样。

java数据类型与C/C++数据类型对照表:

java语言C/C++语言bit位数
booleanjboolean8 unsigned
bytejbyte8
charjchar16 unsigned
shortjshort16
intjint32
longjlong64
floatjfloat32
doublejdouble64
voidvoid0
到这里我们已经完成了C/C++代码的编写。

第五步:新建Android.mk文件。

Android.mk文件是编译刚才写好的C文件的一个重要文件,这个文件可以从之前编译NDK中的例子中拷过来修改,Android.mk文件如下:

# Copyright (C) 2009 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0 #
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := interfaces-jni                       //要生成的.so库的文件名
LOCAL_SRC_FILES := com_pve_testjni_JniInterfaces.c      //要编译的源文件 复杂一点的可能还有依赖文件之类的,linux高手应该知道的。

include $(BUILD_SHARED_LIBRARY)
存放目录如下:



第六步:编译生成我们需要的SO文件。

打开Cygwin,进入到工程目录下的jni目录下执行命令如下图:



此时刷新TestJni工程就会看到之前另两个文件目录libs、obj 。

到这里接口类的本地方法已经 实现并生成了.so文件供JAVA程序调用,接下来就是使用接口了。

第七步:在android工程的布局中增加两textview来显示结果。

main.xml代码如下:

<?xml version="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:id="@+id/showgetdatafromJNI1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/showgetdatafromJNI2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</LinearLayout>


第八步:在java文件中调用接口。

TestJniActivity.java代码如下:


package com.pve.testjni;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class TestJniActivity extends Activity {
private TextView showgetdatafromJNI1 = null;
private TextView showgetdatafromJNI2 = null;

static
{
System.loadLibrary("interfaces-jni");
}
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
JniInterfaces interfaces = new JniInterfaces();
showgetdatafromJNI1 = (TextView) findViewById(R.id.showgetdatafromJNI1);
showgetdatafromJNI2 = (TextView) findViewById(R.id.showgetdatafromJNI2);

showgetdatafromJNI1.setText(interfaces.getCInt() + "");   //在此调用了获得整形数的接口函数
showgetdatafromJNI2.setText(interfaces.getCString());     //在此调用了获得字符串的接口函数
}
}


第九步:运行Android程序

运行结果:



到这里此篇《Android JNI操作指南》算是结束了,希望对于初学者有帮助,这里面讲的只是冰山一角,以后的JNI编程之路还得靠大家自己去琢磨。

版权所有:zhengbin1987512l

日期:2012-07-13
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: