jni的使用,jni编程指南

http://www.itjxue.com  2023-01-07 05:38  来源:未知  点击次数: 

JNI使用碰到的问题

记录下使用JNI的诡异问题

1.传递String或int出错

正常:static int _open(JNIEnv *env, jclass thisz,? jstring path, int speed)

出问题:static int _open(JNIEnv *env, jstring path, int speed)

出错的原因是 : 把 jclass thisz 去掉了

2. jniRegisterNativeMethods 出错,提示找不到对应java方法

原因:java文件里的native方法未被使用,编译的时候会被优化,这样导致jni找不到注册失败

请问jni技术到底是什么?能不能简单的阐述一下?

jNi就是java调用本地方法的技术,最简单的来说,java运行一个程序需要要和不同的系统平台打交道,在windows里就是和windows平台底层打交道,mac就是要和mac打交道,jvm就是通过大量的jni技术使得java能够在不同平台上运行。而使用了这技术的一个标志就是native,如果一个类里的一个方法被native修饰,那就说明这个方法是jni来实现的,他是通过本地系统api里的方法来实现的。当然这个本地方法可能是c或者C++,当然也可能是别的语言。jni是java跨平台的基础,jvm通过在不同系统上调用不同的本地方法使得jvm可以在不同平台间移植。

当前你自己也可以用jni来写一些程序,这种基本上是你以前使用了其他语言完成了一些功能,但是你有要用java来重复这些功能的时候,就可以使用jni来完成了。不过带来的问题就是,如果你的那个本地方法是依托于本地操作系统的话,那就意味着你的java程序也只能在这一个系统上运行了。所以jni就使得java很容易限定在了一个系统平台上,而jdk的作用在于他提供一个规范,这个规范就是包含了很多native方法,这些方法都是要本地操作系统来实现的,而实现了这些本地方法的操作系统就可以移植java平台了。

android jni怎么使用

Android JNI开发需要so动态库、再把相应的so文件放在对应的文件夹下才可以使用。so文件需要c语言编程。

JAVA 里面如何 使用jni 给个例子 加 解释。谢谢

JAVA以其跨平台的特性深受人们喜爱,而又正由于它的跨平台的目的,使得它和本地机器的各种内部联系变得很少,约束了它的功能。解决JAVA对本地操作的一种方法就是JNI。

JAVA通过JNI调用本地方法,而本地方法是以库文件的形式存放的(在WINDOWS平台上是DLL文件形式,在UNIX机器上是SO文件形式)。通过调用本地的库文件的内部方法,使JAVA可以实现和本地机器的紧密联系,调用系统级的各接口方法。

简单介绍及应用如下:

一、JAVA中所需要做的工作

在JAVA程序中,首先需要在类中声明所调用的库名称,如下:

static {

System.loadLibrary(“goodluck”);

}

在这里,库的扩展名字可以不用写出来,究竟是DLL还是SO,由系统自己判断。

还需要对将要调用的方法做本地声明,关键字为native。并且只需要声明,而不需要具 体实现。如下:

public native static void set(int i);

public native static int get();

然后编译该JAVA程序文件,生成CLASS,再用JAVAH命令,JNI就会生成C/C++的头文件。

例如程序testdll.java,内容为:

public class testdll

{

static

{

System.loadLibrary("goodluck");

}

public native static int get();

public native static void set(int i);

public static void main(String[] args)

{

testdll test = new testdll();

test.set(10);

System.out.println(test.get());

}

}

用javac testdll.java编译它,会生成testdll.class。

再用javah testdll,则会在当前目录下生成testdll.h文件,这个文件需要被C/C++程序调用来生成所需的库文件。

二、C/C++中所需要做的工作

对于已生成的.h头文件,C/C++所需要做的,就是把它的各个方法具体的实现。然后编译连接成库文件即可。再把库文件拷贝到JAVA程序的路径下面,就可以用JAVA调用C/C++所实现的功能了。

接上例子。我们先看一下testdll.h文件的内容:

/* DO NOT EDIT THIS FILE - it is machine generated */

#include jni.h

/* Header for class testdll */

#ifndef _Included_testdll

#define _Included_testdll

#ifdef __cplusplus

extern "C" {

#endif

/*

* Class: testdll

* Method: get

* Signature: ()I

*/

JNIEXPORT jint JNICALL Java_testdll_get (JNIEnv *, jclass);

/*

* Class: testdll

* Method: set

* Signature: (I)V

*/

JNIEXPORT void JNICALL Java_testdll_set (JNIEnv *, jclass, jint);

#ifdef __cplusplus

}

#endif

#endif

在具体实现的时候,我们只关心两个函数原型

JNIEXPORT jint JNICALL Java_testdll_get (JNIEnv *, jclass); 和

JNIEXPORT void JNICALL Java_testdll_set (JNIEnv *, jclass, jint);

这里JNIEXPORT和JNICALL都是JNI的关键字,表示此函数是要被JNI调用的。而jint是以JNI为中介使JAVA的int类型与本地的int沟通的一种类型,我们可以视而不见,就当做int使用。函数的名称是JAVA_再加上java程序的package路径再加函数名组成的。参数中,我们也只需要关心在JAVA程序中存在的参数,至于JNIEnv*和jclass我们一般没有必要去碰它。

好,下面我们用testdll.cpp文件具体实现这两个函数:

#include "testdll.h"

int i = 0;

JNIEXPORT jint JNICALL Java_testdll_get (JNIEnv *, jclass)

{

return i;

}

JNIEXPORT void JNICALL Java_testdll_set (JNIEnv *, jclass, jint j)

{

i = j;

}

编译连接成库文件,本例是在WINDOWS下做的,生成的是DLL文件。并且名称要与JAVA中需要调用的一致,这里就是goodluck.dll 。把goodluck.dll拷贝到testdll.class的目录下,java testdll运行它,就可以观察到结果了。

如何在Android下使用JNI

关于如何在Android使用JNI调用C/C++代码库,网上已经有很多优秀的文章了,这里说一个大概过程吧:

首先需要懂C,其次要明白JNI的开发流程,然后还要知道NDK如何使用

1、在java代码中声明了一个native本地方法

Public native String helloFromc();

2、在项目目录中创建JNI文件夹

3、在JNI文件夹里面创建C文件,按照规范写代码

Jstring

Java_com_cheng_jnitest_MainActivity_helloFromc(JNIEnv* env,jobject obj)

4、用ndk-build指令编译

编译前需要配置Android.mk文件

//指定编译的文件夹,指定当前的目录

LOCAL_PATH := $(call my-dir)

//编译器在编译的时候会产生很多临时变量,中间变量最好在编译前清空所有的临时变量

include $(CLEAR_VARS)

//编译完成后的模块名

LOCAL_MOUDLE := hello

//编译的源文件

LOCAL_SRC_FILES:=hello.c

//编译一个动态库

//动态库.so 只包含运行的函数,不包含依赖,所以体积小,运行的时候回去系统寻找依赖

//静态库.a 包含所有的函数和运行的依赖,所以体积大,包含所有的api

include $(BUILD_SHARED_LIBRARY)

5、生成了一个so动态库,放到了libs里面

6、项目中引入依赖库

Static{

System.loadLibrary("hello");

}

-

如何在JNI中使用线程

如果你想了解JNI在如何在多线程下使用

如果你在子线程使用JNI时遇到findClass不能找到目标Class,而在主线程下却能找到该Class的问题。或是GetEnv返回NULL的问题

如果你想多学点编程技术的话

那么,这篇文章就是为你而写的,

:)

最近工作中遇到这么个问题:c++代码需要调用Android的API来做一个比较耗时的任务,因为有点耗时,希望能有个进度条显示给用户,很自然地,我创建了一个子线程用来执行这个耗时的任务,按照平时写法,结果一运行,GetEnv获取失败了。网上查找一番,官方说明有这么句话:

If

the

current

thread

is

not

attached

to

the

VM,

sets

*env

to

NULL,

and

returns

JNI_EDETACHED.

If

the

specified

version

is

not

supported,

sets

*env

to

NULL,

and

returns

JNI_EVERSION.

Otherwise,

sets

*env

to

the

appropriate

interface,

and

returns

JNI_OK.

调试后找到了原因,the

current

thread

is

not

attached。

原来子线程函数里需要使用AttachCurrentThread()和DetachCurrentThread()这两个函数。没错,你需要gJvm-AttachCurrentThread(env,

NULL);来获取env,这样写之后,以为万事大吉了,结果findClass出错了,没有找到目标类,可是我千真万确地记得在主线程里这么写是没有问题的。env没有问题了,这回又哪里出错了呢?上网google一番,噢,不对,google被墙了,是用bing查找一番后,总算有些眉目了。

首先确保你的class

name写对了,以包名开头,并用反斜杠隔开。如果class

name没有错,那么应该是class

loader的问题了。解决方法是你先在主线程中获取该class,并且将其保存为全局变量,以便其他线程使用。

jclass

tmp

=

env-FindClass("com/example/company/MyClass");

myClass

=

(jclass)env-NewGlobalRef(tmp);

在子线程中,

mid

=

env-GetStaticMethodID(cls,

"fromJNI",

"(I)V");

if

(mid

!=

NULL)

{

env-CallStaticVoidMethod(env,

cls,

mid,

i);

}

当然,也有其他解决方法,至少我使用这种方法成功了。而接下来在java中调用c++的代码就比较顺利了,木有碰到问题了。

总结:

1.在JNI_OnLoad中,保存JavaVM*,这是跨线程的,持久有效的,而JNIEnv*则是当前线程有效的。一旦启动线程,用AttachCurrentThread方法获得env。

2.通过JavaVM*和JNIEnv可以查找到jclass。

3.把jclass转成全局引用,使其跨线程。

4.然后就可以正常地调用你想调用的方法了。

5.用完后,别忘了delete掉创建的全局引用和调用DetachCurrentThread方法。

(责任编辑:IT教学网)

更多

推荐Frontpage教程文章