当编写一个混合有本地C代码和Java的应用程序时,需要使用Java本地接口(JNI)作为连接桥梁。JNI作为一个软件层和API,允许使用本地代码调用Java对象的方法,同时也允许在Java方法中调用本地函数。

在Java端,开发者所需要做的仅仅是在连接本地函数的方法之前加上native关键字。这样VM就会去寻找这个本地函数。

1.从Java调用本地函数

从Java调用本地函数时,需要在类中定义一个带有native关键字的特有方法,作为连接本地代码的桥梁。通过这个定义,尝试调用本地方法时JVM会找到一个名字带有包名,类名和方法名的本地函数。

package com.example.liyuanjing.jniproject;
import android.util.Log;
public class NativeSorting { static {
System.loadLibrary("sorting_jni");
} public NativeSorting() { } public void sortIntegers(int[] ints) {
nativeSort(ints);
for (int i = ; i < ints.length-; i++) {
System.out.print(String.valueOf(ints[i]));
Log.i("liyuanjinglyj",String.valueOf(ints[i]));
}
} private native void nativeSort(int[] ints);
}

上面是一个简化的示例,包括一个对int数组进行排序的方法。除构造函数之外还有两个方法。第一个是sortIntegers(),它是一个常规的Java方法,可以在其他Java类中调用它。第二个是nativeSort(),这个方法指向本地代码中的函数。虽然可以把本地方法定义为公共的,但更好的做法是把它们作为私有方法包装在一个Java方法中,以便进行一些错误处理。

可以从头开始写本地代码,但也可以借助javah工具来生成部分代码,该工具在Java SDK中。它会生成一个C语言头文件,包括本地方法对应的函数定义。首先要编译Java程序代码,然后在当前项目的src/main目录运行如下命令:

javah -classpath ../../build/intermediates/classes/debug/ -d jni/ com.example.liyuanjing.jniproject.NativeSorting

上面命令展示了如何为之前示例代码中的NativeSorting生成一个头文件。-classpath参数指定了编译好的类文件位置,注意不是DEX文件。-d参数指定了生成头文件的输出目录。运行完命令后,会在jni目录生成com_example_liyuanjing_jniproject_NativeSorting.h文件,它包含了本地函数的定义。

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_liyuanjing_jniproject_NativeSorting */ #ifndef _Included_com_example_liyuanjing_jniproject_NativeSorting
#define _Included_com_example_liyuanjing_jniproject_NativeSorting
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_liyuanjing_jniproject_NativeSorting
* Method: nativeSort
* Signature: ([I)V
*/
JNIEXPORT void JNICALL Java_com_example_liyuanjing_jniproject_NativeSorting_nativeSort
(JNIEnv *, jobject, jintArray); #ifdef __cplusplus
}
#endif
#endif

这段代码即为生成的头文件。正如第一行注释所说,不要修改这个文件。开发者所要做的就是把函数定义复制到实现该函数的.c文件中。

下面的代码展示了头文件com_example_liyuanjing_jniproject_NativeSorting.h中的JNI函数实现,本例没有在JNI_OnLoad函数做太多的操作,只是返回了代表当前JNI版本为1.6的常量,这是Dalvik VM支持的一个版本,下面是array.c代码:

#include <jni.h>
#include <android/log.h>
#include "com_example_liyuanjing_jniproject_NativeSorting.h"
void quicksort(int *arr, int start, int end); JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
return JNI_VERSION_1_6;
} JNIEXPORT void JNICALL Java_com_example_liyuanjing_jniproject_NativeSorting_nativeSort
(JNIEnv *env, jobject obj, jintArray data) {
jint* array = (*env)->GetIntArrayElements(env, data, );
jint length = (*env)->GetArrayLength(env, data);
quicksort(array, , length);
(*env)->ReleaseIntArrayElements(env, data, array, );
} void quicksort(int *arr, int start, int end)
{
int i, j, temp;
for (i = ; i < end-1; i++)
{
for (j = ; j < end - i-1; j++)
{
if (*(arr+j) < *(arr+j+))
{
temp = *(arr+j);
*(arr+j) = *(arr+j+);
*(arr+j+) = temp;
}
}
}
}

这个示例中,函数GetIntArrayElements,GetArrayLength和ReleaseIntArrayElements都是特定的JNI代码。第一个函数得到一个本地数据指针,以便把数据传给普通的C函数;第二个函数返回数据的大小;第三个函数告诉JVM本地端的工作已经完成,需要把数组复制回原地。这些函数都是必须的,因为从Java到JNI传送复杂的数据类型时必须通过JNIEnv对象来完成。

注意:调用GetIntArrayElements返回一个jint指针,指向函数中jintArray里的数据,接下来就可以把jint指针作为普通int类型指针来使用。

2.Android实现JNI

要想Android能运行起来,必须到NDK目录android-ndk-r10d\samples\native-activity\jni目录下拷贝Android.mk,到刚才放置com_example_liyuanjing_jniproject_NativeSorting.h和array.c同一目录下,当然还要更改Android.mk的几个值。

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE    := sorting_jni
LOCAL_SRC_FILES := array.c
include $(BUILD_SHARED_LIBRARY)

$(call import-module,android/native_app_glue)

一.LOCAL_PATH := $(call my-dir)

一个Android.mk
file首先必须定义好LOCAL_PATH变量。它用于在开发树中查找源文件。在这个例子中,宏函数’my-dir’, 由编译系统提供,用于返回当前路径(即包含Android.mk
file文件的目录)。

二.include $(CLEAR_VARS)

CLEAR_VARS由编译系统提供,指定让GNU
MAKEFILE为你清除许多LOCAL_XXX变量(例如 LOCAL_MODULE,
LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES, 等等...),

除LOCAL_PATH 。这是必要的,因为所有的编译控制文件都在同一个GNU MAKE执行环境中,所有的变量都是全局的。

三.LOCAL_MODULE    := sorting_jni

LOCAL_MODULE变量必须定义,以标识你在Android.mk文件中描述的每个模块。名称必须是唯一的,而且不包含任何空格。注意编译系统会自动产生合适的前缀和后缀,换句话说,一个被命名为'sorting_jni'的共享库模块,将会生成'libsorting_jni'文件。

重要注意事项

如果你把库命名为‘libhelloworld’,编译系统将不会添加任何的lib前缀,也会生成libhelloworld.so,这是为了支持来源于Android平台的源代码的Android.mk文件,如果你确实需要这么做的话。

四.LOCAL_SRC_FILES := array.c

LOCAL_SRC_FILES变量必须包含将要编译打包进模块中的C或C++源代码文件。注意,你不用在这里列出头文件和包含文件,因为编译系统将会自动为你找出依赖型的文件;仅仅列出直接传递给编译器的源代码文件就好。【注意,默认的C++源码文件的扩展名是’.cpp’. 指定一个不同的扩展名也是可能的,只要定义LOCAL_DEFAULT_CPP_EXTENSION变量,不要忘记开始的小圆点(也就是定义为‘.cxx’,而不是‘cxx’)(当然这一步我们一般不会去改它)】

五.include $(BUILD_SHARED_LIBRARY)

BUILD_SHARED_LIBRARY是编译系统提供的变量,指向一个GNU
Makefile脚本(应该就是在build/core目录下的shared_library.mk),负责收集自从上次调用'include
$(CLEAR_VARS)'以来,定义在LOCAL_XXX变量中的所有信息,并且决定编译什么,如何正确地去做。并根据其规则生成静态库。同理对于静态库。

当配置完上面所说的一个C头文件,一个.c文件,一个Android.mk文件后,进入CMD到当前目录中。输入ndk-build命令:

[armeabi] Compile thumb  : sorting_jni <= array.c
[armeabi] SharedLibrary  : libsorting_jni.so
[armeabi] Install        : libsorting_jni.so => libs/armeabi/libsorting_jni.so

如果没有意外会显示上述正确结果。

然后在Android Studio项目的app/src/main/目录下建立jinLibs目录将生成的libs目录中的文件拷贝到JinLibs目录中。如下图所示:

然后调用此方法,就可以实现Android使用JNI的功能了。

05-25 20:02:46.720  32349-32349/com.example.liyuanjing.jniproject I/liyuanjinglyj﹕ 9
05-25 20:02:46.720  32349-32349/com.example.liyuanjing.jniproject I/liyuanjinglyj﹕ 8
05-25 20:02:46.720  32349-32349/com.example.liyuanjing.jniproject I/liyuanjinglyj﹕ 7
05-25 20:02:46.720  32349-32349/com.example.liyuanjing.jniproject I/liyuanjinglyj﹕ 6
05-25 20:02:46.720  32349-32349/com.example.liyuanjing.jniproject I/liyuanjinglyj﹕ 5
05-25 20:02:46.720  32349-32349/com.example.liyuanjing.jniproject I/liyuanjinglyj﹕ 4
05-25 20:02:46.720  32349-32349/com.example.liyuanjing.jniproject I/liyuanjinglyj﹕ 3
05-25 20:02:46.720  32349-32349/com.example.liyuanjing.jniproject I/liyuanjinglyj﹕ 2
05-25 20:02:46.720  32349-32349/com.example.liyuanjing.jniproject I/liyuanjinglyj﹕ 1

之前的JNI例子只是演示用的,开发者应该使用Arrays.sort()或Collections.sort()来进行排序。通常不需要在本地进行排序,因为Java实现已经够快了。

版权声明:本文为博主原创文章,未经博主允许不得转载。

Android使用JNI(从java调用本地函数)的更多相关文章

  1. Android与JNI(二) ---- Java调用C++ 动态调用

    目录: 1. 简介 2. JNI 组件的入口函数 3. 使用 registerNativeMethods 方法 4. 测试 5. JNI 帮助方法 6. 参考资料 1. 简介 Android与JNI( ...

  2. Android与JNI(一) ---- Java调用C 静态调用

    第一.通过eclipse新建一个工程名为HelloJni的android工程,并编译. 第二.右键工程-->Android Tools --> Add Native Support,出现如 ...

  3. Android JNI之JAVA调用C/C++层

    转载请声明:原文转自:http://www.cnblogs.com/xiezie/p/5929996.html 一.java调用本地函数的开发步骤: 1.编写本地方法的类(可以说是用来叙述本地方法的类 ...

  4. java调用本地方法的时候报错 could not find the main class:xx.program will exit

    如图所示,当在java调用本地方法的时候报错 我的解决办法是把dll文件放到System.out.println(System.getProperty("java.library.path& ...

  5. [转] Android自动化测试之使用java调用monkeyrunner(五)

    Android自动化测试之使用java调用monkeyrunner 众所周知,一般情况下我们使用android中的monkeyrunner进行自动化测试时,使用的是python语言来写测试脚本.不过, ...

  6. [转载]java调用本地dos命令

    在社区看到java调用本地dos命令的代码,特贴出来 String command = "ipconfig"; Runtime run = Runtime.getRuntime() ...

  7. Android 通过 JNI 访问 Java 字段和方法调用

    在前面的两篇文章中,介绍了 Android 通过 JNI 进行基础类型.字符串和数组的相关操作,并描述了 Java 和 Native 在类型和签名之间的转换关系. 有了之前那些基础,就可以实现 Jav ...

  8. Android与JNI(三) ---- c++调用java(转载)

    源码下载:JniDemo JNI就是Java Native Interface, 即可以实现Java调用本地库, 也可以实现C/C++调用Java代码, 从而实现了两种语言的互通, 可以让我们更加灵活 ...

  9. Linux平台下Java调用C函数

    JNI是Java native interface的简写,可以译作Java原生接口.Java可以通过JNI调用C/C++的库,这对于那些对性能要求比较高的Java程序无疑是一个 福音. 使用JNI也是 ...

随机推荐

  1. TMsgThread, TCommThread -- 在delphi线程中实现消息循环

    http://delphi.cjcsoft.net//viewthread.php?tid=635 在delphi线程中实现消息循环 在delphi线程中实现消息循环 Delphi的TThread类使 ...

  2. CHM文件无法查看内容解决办法

    http://www.cnblogs.com/skey_chen/archive/2011/05/24/2055538.html 从网上下载了一份CHM格式的文档,打开时左侧菜单出来了,但右边显示如下 ...

  3. Qt技巧:QProcess与外部程序的调用

    项目做到一定阶段,常常须要在原来的project上调用外部程序. Qt为此提供了QProcess类,QProcess可用于完毕启动外部程序,并与之交互通信. 一.启动外部程序的两种方式: (1)一体式 ...

  4. SMARTFORM报表程序设计(2)

    在创建并设置好STYLE程序之后,在SMARTFORM页面选择单选框FORM输入报表程序名称(ZS_SFLIGHT),点击CREATE按钮即可进入SMARTFORM BUILDER图形设置界面,SMA ...

  5. SAP MRP的计算步骤

          SAP MRP的计算步骤,物料需求计划(简称为MRP)与主生产计划一样属于ERP计划管理体系,它主要解决企业生产中的物料需求与供给之间的关系,即无论是对独立需求的物料,还是相关需求的物料, ...

  6. 【Animation】 使用handler和Runnable实现某一个控件的抖动效果

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

  7. 在ascx中调用另一个ascx的写法

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...

  8. 管理和维护RHCS集群

    导读 管理和维护RHCS集群是一个非常复杂和繁琐的工作,要维护好一个RHCS集群,必须熟悉RHCS的基本运行原理,在集群管理方面,RHCS提供了两种方式:即Luci图形界面方式和命令行方式,这儿重点讲 ...

  9. Spring Mvc返回html页面404错误解决记录--转载

    原文地址:http://53873039oycg.iteye.com/blog/2061992 以前使用Spring Mvc时候都是返回jsp页面或者ftl页面,昨天想返回html页面,spring- ...

  10. setsockopt

    1.closesocket(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket:BOOL bReuseaddr=TRUE;setsockopt(s,SOL_SOCKET ,SO ...