`
mmdev
  • 浏览: 12847339 次
  • 性别: Icon_minigender_1
  • 来自: 大连
文章分类
社区版块
存档分类
最新评论

技术转载:Jni学习一:了解Jni

 
阅读更多

转自:http://www.iteye.com/topic/295776

通过本博文,可以大体了解Jni是一个什么东西,其中包含了一些对java基本数据类型的操作实例,对于Jni中如何使用Java更复杂的数据类型,将在下一章节总结。

JNI一直以来都很少去关注,但却是我心中的一个结,最近这几天刚好手头有点时间,因此抽空看了一下这方面的东西,整理了一份文档,JNI技术的出现主要是基于三个方面的应用需求:

1. 解决性能问题
Java具有平台无关性,这使人们在开发企业级应用的时候总是把它作为主要候选方案之一,但是性能方面的因素又大大削弱了它的竞争力。为此,提高Java的性能就显得十分重要。Sun公司及Java的支持者们为提高Java的运行速度已经做出了许多努力,其中大多数集中在程序设计的方法和模式选择方面。由于算法和设计模式的优化是通用的,对Java有效的优化算法和设计模式,对其他编译语言也基本同样适用,因此不能从根本上改变Java程序与编译型语言在执行效率方面的差异。由此,于是人们开始引入JIT(Just In Time,及时编译)的概念。它的基本原理是:首先通过Java编译器把Java源代码编译成平台无关的二进制字节码。然后在Java程序真正执行之前,系统通过JIT编译器把Java的字节码编译为本地化机器码。最后,系统执行本地化机器码,节省了对字节码进行解释的时间。这样做的优点是大大提高了Java程序的性能,缩短了加载程序的时间;同时,由于编译的结果并不在程序运行间保存,因此也节约了存储空间。缺点是由于JIT编译器对所有的代码都想优化,因此同样也占用了很多时间。

动态优化技术是提高Java性能的另一个尝试。该技术试图通过把Java源程序直接编译成机器码,以充分利用Java动态编译和静态编译技术来提高Java的性能。该方法把输入的Java源码或字节码转换为经过高度优化的可执行代码和动态库 (Windows中的. dll文件或Unix中的. so文件)。该技术能大大提高程序的性能,但却破坏了Java的可移植性。

JNI(Java Native Interface, Java本地化方法)技术由此闪亮登场。因为采用JNI技术只是针对一些严重影响Java性能的代码段,该部分可能只占源程序的极少部分,所以几乎可以不考虑该部分代码在主流平台之间移植的工作量。同时,也不必过分担心类型匹配问题,我们完全可以控制代码不出现这种错误。此外,也不必担心安全控制问题,因为Java安全模型已扩展为允许非系统类加载和调用本地方法。根据Java规范,从JDK 1. 2开始,FindClass将设法找到与当前的本地方法关联的类加载器。如果平台相关代码属于一个系统类,则无需涉及任何类加载器; 否则,将调用适当的类加载器来加载和链接已命名的类。换句话说,如果在Java程序中直接调用C/C++语言产生的机器码,该部分代码的安全性就由Java虚拟机控制。

2. 解决本机平台接口调用问题
JAVA以其跨平台的特性深受人们喜爱,而又正由于它的跨平台的目的,使得它和本地机器的各种内部联系变得很少,约束了它的功能。解决JAVA对本地操作的一种方法就是JNI。JAVA通过JNI调用本地方法,而本地方法是以库文件的形式存放的(在WINDOWS平台上是DLL文件形式,在UNIX机器上是SO文件形式)。通过调用本地的库文件的内部方法,使JAVA可以实现和本地机器的紧密联系,调用系统级的各接口方法。

3. 嵌入式开发应用
“一次编程,到处使用”的Java软件概念原本就是针对网上嵌入式小设备提出的,几经周折,目前SUN公司已推出了J2ME(Java 2 P1atform Micro Edition)针对信息家电的Java版本,其技术日趋成熟,开始投入使用。SUN公司Java虚拟机(JVM)技术的有序开放,使得Java软件真正实现跨平台运行,即Java应用小程序能够在带有JVM的任何硬软件系统上执行。加上Java语言本身所具有的安全性、可靠性和可移植性等特点,对实现瘦身上网的信息家电等网络设备十分有利,同时对嵌入式设备特别是上网设备软件编程技术产生了很大的影响。也正是由于JNI解决了本机平台接口调用问题,于是JNI在嵌入式开发领域也是如火如荼。

不失直观性,我们首先写一个JNI小例子:

在class文件生成的相应目录执行命令如下:
----------------------------------------------------
E:\projects\jni\target\classes>javah HelloJni
----------------------------------------------------

得到C++文件HelloJni.h

JNI函数名称分为三部分:首先是Java关键字,供Java虚拟机识别;然后是调用者类名称(全限定的类名,其中用下划线代替名称分隔符);最后是对应的方法名称,各段名称之间用下划线分割。


JNI函数的参数也由三部分组成:首先是JNIEnv *,是一个指向JNI运行环境的指针;第二个参数随本地方法是静态还是非静态而有所不同一一非静态本地方法的第二个参数是对对象的引用,而静态本地方法的第二个参数是对其Java类的引用;其余的参数对应通常Java方法的参数,参数类型需要根据一定规则进行映射。

编写C++文件HelloJni.h的实现类,我是比较常用VC6.0来生成dll文件(helloJni.dll)的

其实此时,我们的工程目前还暂时不能生成我们想要的 helloJni.dll 文件,问题就出在了“#include <jni.h>”。由于VC6.0里没有我们需要的“jni.h”文件,因此就需要手动加入到VC6.0的环境中去。在JAVA_HOME路径下我们可以找到include文件夹,其中就可以找到我们需要的“jni.h”文件。为了避免以后麻烦起见,将所有的C++文件全部拿出来,放在“%CPP_HOME%\VC98\Include”路径下。然后将工程进行打包就可以得到我们需要的“helloJni.dll”文件了。

将helloJni.dll文件放置于工程classes目录,执行命令如下:
-----------------------------------------------
E:\projects\jni\target\classes>java HelloJni
-----------------------------------------------

运行结果如下:
-----------------------------------------------------------------
Hello Dynamic Link Library has been calling!
Java_HelloJni_displayHelloJni method has been executed!
-----------------------------------------------------------------


但是要想在eclipse中运行helloJni.dll文件,就需要将文件拷贝到工程的根目录,或者将其放在诸如C:\WINDOWS\system32;C:\WINDOWS;等目录下。因为,eclipse在运行helloJni.dll文件时首先会去在当前根目录找,如果找不到则在path上去找,因此你还可以为了方便管理生成的dll文件,将所有工程中的dll文件都放到一个特定的目录,然后将该目录加入到你的本地path环境变量中去,这样每次只需要将生成的dll文件放入path目录下就可以访问了。注,如果需要加环境变量最好在加好以后重新启动一下eclipse,确保eclipse能够加载到最新的path环境。

接下来,对小例子进行重构:
1. 新增一个基础类

2. 定义新类继承基础类

3. 编写调用类

此次,将dll文件定义为:helloJniTest.dll。

执行结果:
------------------------------------------------------------------------------------
Java_org_danlley_jni_test_HelloJniTest_displayHelloJni has been called!
------------------------------------------------------------------------------------

例子相当简单,没有传入参数,也没有返回值,那么是不是可以让本地方法返回一些参数,同时又可以传入数据进行处理,并把处理结果返回给方法的调用者呢,先拿基本类型开刀。接下来对 HelloJniTest 继续进行改造:新增两个本地方法,如下:

重新生成org_danlley_jni_test_HelloJniTest.h文件,并改写其实现类org_danlley_jni_test_HelloJniTest.cpp如下:

修改 RunMain 类:

运行RunMain:

-----------------------------------------------------------------------
tester.getDynamicIntDataNoParam()=65535
tester.getDynamicIntData(100)=10000
Java_org_danlley_jni_test_HelloJniTest_displayHelloJni has been called!
-----------------------------------------------------------------------

OK,一切正常。

还是不过瘾,简单对象可以处理了,如果是一个java对象,还可以处理吗,答案是当然可以,接下来我们来继续对 helloJniTest 类进行改造。新增一个方法如下:

重新生成org_danlley_jni_test_HelloJniTest.h文件:

改写org_danlley_jni_test_HelloJniTest.cpp文件:

重新对C++工程打包成dll文件,运行结果:
---------------------------------------------------------------------------
tester.getDynamicIntDataNoParam()=65535
tester.getDynamicIntData(100)=10000
tester.getDynamicStringData=My first String test
Java_org_danlley_jni_test_HelloJniTest_displayHelloJni has been called!
My first String test
---------------------------------------------------------------------------

我们不仅把Java的一个String对象成功的传给了dll,而且还将处理后的结果返回了出来。

但是总觉得还是不够,那我们就再来个比较复杂的对象把,我们这次将一个整形数组通过java传给dll,看看是不是也可以处理,继续还是对 helloJniTest 类进行改造,新增一个方法:

重新生成org_danlley_jni_test_HelloJniTest.h文件

改写org_danlley_jni_test_HelloJniTest.cpp文件:

改写RunMain:

运行结果:
--------------------------------------------------------------------------------
tester.getDynamicIntDataNoParam()=65535
tester.getDynamicIntData(100)=10000
tester.getDynamicStringData=My first String test
0
1
4
9
16
25
36
49
64
81
Java_org_danlley_jni_test_HelloJniTest_displayHelloJni has been called!
My first String test
--------------------------------------------------------------------------------

另附加一个自定义的对象的操作方法:

代码:

package com.boao;

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

public class JniString3Activity extends Activity {
/** Called when the activity is first created. */
public static final String libName = "JniString3";
TextView textView;
Date date;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
textView = (TextView) findViewById(R.id.text);
date = new Date();
date.string = "boao";
getString(date);
textView.setText(date.string);
}

public native void getString(Date date);

static {
System.loadLibrary(libName);
}
}

package com.boao;

public class Date {

public String string;
}


LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog


LOCAL_MODULE := JniString3
LOCAL_SRC_FILES := \
JavaCalu.c \


include $(BUILD_SHARED_LIBRARY)

#include <string.h>
#include <android/log.h>
#include <jni.h>

/**
* 对传递进来的字符串先输出,然后在对其进行修改,最后设置属性
*/
void Java_com_boao_JniString3Activity_getString(JNIEnv* env,jobject thiz,jobject date)
{
jclass date_calss;
jfieldID date_string_f;
jstring jstr;
const char *str;

//得到Date的class类型
date_calss = (*env)->GetObjectClass(env,date);
//得到Date对象的string属性的属性ID
date_string_f = (*env)->GetFieldID(env,date_calss,"string","Ljava/lang/String;");
//得到Date对象的string属性的属性值
jstr = (*env)->GetObjectField(env,date,date_string_f);
//从jstring类型取得c语言环境下的char*类型
str = (*env)->GetStringUTFChars(env,jstr,0);
//释放jni分配的内存
(*env)->ReleaseStringUTFChars(env,jstr,str);
jstr = (*env)->NewStringUTF(env,"hao");
//给Date对象的string属性重新赋值
(*env)->SetObjectField(env,date,date_string_f,jstr);
//return;
}

参考资料:

http://en.wikipedia.org/wiki/Java_Native_Interface
http://www.yesky.com/20011004/199789.shtml
http://www.21ic.com/news/html/63/show13260.htm
http://java.ccidnet.com/art/297/20060228/439729_1.html
http://www.blogjava.net/soft/archive/2006/11/13/posoft.html
http://www.blogjava.net/soft/archive/2006/11/13/80788.html
http://www.blogjava.net/soft/archive/2006/11/13/80789.html

分享到:
评论

相关推荐

    JNI完全技术手册 带完整书签

    Chap9:如何编写jni方法(转载)... 55 1、实例一:在jni中调用标准c中自带的函数printf(): 57 2、实例二、调用c 语言用户定义的函数... 58 3、实例三、在jni函数中访问java类中的对象实例域... 58 4、实例四:在...

    JNI调用大全

    Chap1:JNI完全手册... 3 Chap2:JNI-百度百科... 11 Chap 3:javah命令帮助信息......Chap 4:用javah产生一个...Chap5:使用JNI技术实现java程序调用第三方dll(c/c++)文件的功能... 47 Chap9:如何编写jni方法(转载)... 55

    JNI学习示例代码,含java代码工程和win32 dll工程

    JNI,全称为Java Native Interface,即Java本地接口,JNI是Java调用Native 语言的一种特性。通过JNI可以使得Java与C/C++机型交互。即可以在Java代码中调用C/C++等语言的代码或者在C/C++代码中调用Java代码。由于JNI...

    JNI文档资料源码_2020_01_22

    【Android NDK 开发】Visual Studio 2019 使用 CMake 开发 JNI 动态库 ( 动态库编译配置 | JNI 头文件导入 | JNI 方法命名规范 ) 博客地址 : https://hanshuliang.blog.csdn.net/article/details/104068609 博客...

    使用JNI调用本地接口(解决中文字符传递,源码+说明)

    通过JNI调用本地DLL,并传递中文字符串, Visual C++ 6.0 开发DLL 原创作品,随意转载,提前请说明!

    JNA—JNI终结者(转载)

    NULL 博文链接:https://8366.iteye.com/blog/1100396

    使用JNI调用本地接口(解决中文问题)

    使用JNI与本地接口,并传递中文字符串。 绝对原创,转载请说明!

    android jni开发实例

    转载几篇介绍android jni开发方法的文章,有介绍基础的,有介绍方法的,也有实例可参考。

    JNI文档资料源码_2020_02_04.zip

    【Android NDK 开发】JNI 方法解析 ( C/C++ 调用 Java 方法 | 函数签名 | 调用对象方法 | 调用静态方法 ) I . 调用 Java 方法流程 II . 获取 jclass 对象 ( GetObjectClass ) III . 获取 jclass 对象 ( Find...

    jna-3.5.2jar

    经典的JNI的基础之上的一个框架。 JNA项目地址:https://jna.dev.java.net/ JNA使Java调用原生函数就像.NET上的P/Invoke一样方便、快捷。 JNA的功能和P/Invoke类似,但编写方法与P/Invoke截然不同。JNA没有使用...

    尼斯知识体系:不积跬步无以至千里,每天进步一点点,激情,自我调节,爱与分享

    知识体系总结 如果你感到委屈,证明你还有底线;如果你感到迷茫,证明你还有追求;如果你感到痛苦,证明你还有力气;...NDK JNI学习 文章标题 状态 原创,转载 已发布 原创 已发布 原创 自定义视图 文章标题 状态 原创

    JAVA调用C语言写的SO文件

    因为工作需要写一份SO文件,作为手机硬件IC读卡和APK交互的桥梁,也是中间件,看了网上有说到JNI接口技术实现,这里转载了一个实例 1 // 用JNI实现 2 // 实例: 3 4 // 创建HelloWorld.java 5 class HelloWorld...

    jdbc连接数据库的方式2

    如果利用Oracle的面向对象的技术,可以通过创建一个新的数据库对象类型在数据库中模仿其数据和操作,然后使用JPublisher等工具生成自己的Java bean类。如果使用这种方式,不但Java应用程序可以使用应用软件的对象...

Global site tag (gtag.js) - Google Analytics