Quantcast
Viewing all articles
Browse latest Browse all 5930

一个简单的例子带你了解jni流程



1、前言

jni是java调用原生语言来进行开发的一座桥梁,原生语言一般是指c,c++语言,即jni机制可以让java语言调用c,c++语言,也可以让c,c++语言调用java语言。这样的相互调用,互相结合,主要是出现在对性能要求较高的应用上。在android中,由于它的开发语言也是java,所以也可以利用原生语言进行开发,对jni的了解和使用有助于我们在做应用的时候,对于时间性能要求较高的代码段,可以用原生语言来开发,然后java通过jni机制来调用它,就可以达到很好的效果。

总的来说,jni是一种机制,一种可以然java和原生语言互相调用的机制。

学习jni开发需要有c++或者c基础。


2、基本环境要求

利用jni机制开发,对于开发环境是有特别要求的,因为c,c++语言的开发也需要环境的支持。

单纯的android开发:jdk,sdk,eclipse,adt。

如果要在eclipse上进行jni开发,那么需要:ndk,cygwin,ant。

本文接下来的例子都是使用eclipse的方法进行开发,使用android studio的朋友,由于编译器自身的集成,利用jni机制进行开发会更加方便,但是为了更深入的了解jni机制,本文使用的是eclipse。


对于android的基本开发环境的安装就不提了,jni需要的开发环境这里就提一下,方便完全没基础的朋友安装环境。

2.1 ant的安装

ant是一个命令行构建工具,它主要是驱动目标进行任务的进行。在jni中一般是用来驱动命令。它的安装可以去ant下载官网下载。一般下载zip压缩包即可。然后解压安装到我们指定的目录(自由的目录位置)。最关键的一步是添加环境变量。

假如你的ant根目录是E:\apache-ant-1.9.7。那么你只需要复制这个文件夹下的bin路径,添加到高级环境变量的path变量中。注意与path变量本身存在的值必须添加分号进行分割。

比如:

Image may be NSFW.
Clik here to view.


随后我们用命令行工具输入ant -version命令。如果安装成功就会输出ant的版本号。



2.2cygwin的安装


由于android原生语言开发环境包(NDK)是基于类unix系统运行的,它包含了许多shell脚本,而它们又是不可以直接在window系统运行的,因此需要安装cygwin模拟类unix系统的环境。cygwin的下载地址

由于笔者的安装环境以及完成,所以并不能图文并茂德截图讲解。为了方便大家的安装,笔者决定直接使用参考资料的截图。

Image may be NSFW.
Clik here to view.



Image may be NSFW.
Clik here to view.



Image may be NSFW.
Clik here to view.



Image may be NSFW.
Clik here to view.



Image may be NSFW.
Clik here to view.


Image may be NSFW.
Clik here to view.



读者只需要按照笔者的截图顺序去做就可以安装成功了,cygwin一定要安装成功,否则会导致ndk运行的失败,这也是为什么笔者花这么多图的原因。


2.3 NDK的安装

NDK即android原生语言开发环境包。下载地址:NDK的下载

我们下载好压缩包解压到我们指定的目录,然后需要添加环境变量,比如笔者的NDK安装在:E:\android-ndk-r11b。那么只需要在path变量里面添加此路径即可,记得用分号分隔。

Image may be NSFW.
Clik here to view.



随后我们在命令行窗口输入ndk-build命令。如果有以下输出说明安装成功。

Image may be NSFW.
Clik here to view.




2.4 在eclipse里面配置jni开发环境


需要指定NDK的目录路径。笔者的如下:

Image may be NSFW.
Clik here to view.



3、实践过程进行总结

实践才是检验真理的唯一标准,通过上面的环境配置,我们就可以搭建一个可以开发jni的android环境了,接下来就一步一步引导大家去开发一个jni实例。


首先新建一个空白的android项目。

我们新建一个类,专门处理native函数。笔者命名为CppUtils。内容如下:

public class CppUtils {

	static {
		System.loadLibrary("cppUtils");
	}

	/**
	 * 从CPP获取字符串
	 * 
	 * @return
	 */
	public native String getStringFromCPP();

	

}

这里是简单的从c++原生代码中获取字符串。


3.1 System.loadLibrary

java在java.lang.System包提供了两个静态方法用于加载共享库(一种含原生语言实现的可供android程序调用的库),分别是load,loadLibrary两个方法,由于我们在程序启动的时候就需要加载共享库,因此放在静态代码块中加载。这两个方法的参数是共享库名称。注意共享库为了跨平台使用,它的文件名称会包含一些前缀,而共享库文件的后缀是so。比如我们加载cppUtils共享库,其实他的全名是:libcppUtils.so。


3.2 native标签

native标签用于告诉java编辑器,它的方法是由原生语言实现的,因此不需要去实现它。native方法用分号结束。即如果你希望一个方法用原生语言实现,那么你就给它声明为native方法。


3.3 生成原生语言头文件

由于原生语言头文件需要根据字节码文件来进行分析,所以,在生成头文件之前,我们必须对项目进行build。之后打开我们项目的bin/classes的文件夹,笔者的文件夹如下图:

Image may be NSFW.
Clik here to view.


接着我们就要针对CppUtils.class进行分析生成头文件,在我们对编写原生语言头文件的时候,最好借助工具生成,而不是手写,这样出错的概率才会更低,否则很容易发生jni桥无法将java函数与原生方法联系起来的错误。生成头文件的方法,就是使用命令行工具。比如笔者这里就是,先进入自己项目要分析的java文件的目录下,然后生成头文件。生成头文件的命令如下:
javah -classpath bin/classes com.example.jnibolg.CppUtils

完整的操作过程看下图:

Image may be NSFW.
Clik here to view.



然后回到eclipse中,刷新下我们的项目,我们会发现多了一个以h结尾的文件,这个就是机器生成的头文件。

Image may be NSFW.
Clik here to view.



关于原声函数的实现以及祥光头文件,我们需要放在jni文件夹中,因此,我们接下来需要在项目中建立jni文件夹,并将相关文件放进去。

Image may be NSFW.
Clik here to view.



这样我们的原生文件就生成了。


3.4 分析头文件

接下来我们需要分析头文件的内容,有助于帮助我们了解整个实现过程。首先我们看头文件的代码:

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

#ifndef _Included_com_example_jnibolg_CppUtils
#define _Included_com_example_jnibolg_CppUtils
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_jnibolg_CppUtils
 * Method:    getStringFromCPP
 * Signature: ()Ljava/lang/String;
 */
<pre name="code" class="cpp">JNIEXPORT jstring JNICALL Java_com_example_jnibolg_CppUtils_getStringFromCPP
  (JNIEnv *, jobject);

#ifdef __cplusplus}#endif#endif

首先我们可以看到jni.h头文件被包含了,这个头文件包含了jni机制为了实现从java对象到原生语言的映射的规则。因此,我们一切java调用原生函数,或者原声函数调用java,都必须通过它来实现。
其次我们关注这个函数声明:
JNIEXPORT jstring JNICALL Java_com_example_jnibolg_CppUtils_getStringFromCPP
  (JNIEnv *, jobject);

这个函数声明说明了,它实现的是jnibolg包下的CppUtils类的getStringFromCPP方法,返回的是jstring类型,这是一个jni类型,映射到java的string类型。这里不详细解析jni类型映射,以免变得复杂,主要以实现一个例子了解整个流程为主。

JNIEnv 是一个指针,指向jni对象。通过它,就可以调用jni.h头文件包含的所有函数。即,它就是一个指向jni对象的指针。

jobject表示当前函数所实现方法所属的java对象,这里指的是CppUtils的一个实例。


有了头文件,那么接下来,我们需要用到NDK了,这就需要获取NDK的支持。


3.5 获取NDK的支持

右击我们的项目,找到android tools,点击add native support,这样就可以获取NDK开发包的支持了。点击之后,会需要你填写一个名称,这个名称将会用作共享库的名称,同时这个名字也是我们CppUtils中System.loadLibrary所包含的文件名称。

Image may be NSFW.
Clik here to view.


确定之后,我们看jni文件夹,会生成NDK支持的文件。

Image may be NSFW.
Clik here to view.



其中cppUtils.cpp是我们要进行实现的C++文件。Android.mk是NDK的makefile文件,通过他,可以将原生语言的实现生成为共享库。我们之前安装的cygwin目的就是支持NDK的系统构建。我们打开我们NDK的目录,笔者的如下:

Image may be NSFW.
Clik here to view.

可以发现NDK有很多makefile文件,这些文件都是用于帮助构成共享库的。


3.6 分析mk文件的内容


我们打开Android.mk文件,内容如下:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := cppUtils
LOCAL_SRC_FILES := cppUtils.cpp

include $(BUILD_SHARED_LIBRARY)


LOCAL_PATH,这是一个用于定位源文件的宏,在Android.mk文件中,它必须是第一个变量。
include $(CLEAR_VARS),作用是清除命名冲突,因为Android构建系统在单次执行中会构建多个文件和模块,为了避免LOCAL_<NAME>模式的变量名冲突,必须包含这条指令。
LOCAL_MODULE,指的是生成的共享库的名称,为了适应不同的架构,生成的共享库会含有lib前缀。
LOCAL_SRC_FILES,指的是生成共享库的源文件,多个源文件之间用空格隔开。
include $(BUILD_SHARED_LIBRARY)指令表示生成一个共享库。

关于共享库的生成,可以有更复杂的组织,比如多个共享库依赖某个静态库.....这里不详细讲解,只为了让大家理解整个流程。我们要知道的就是,基本的生成流程都是按照上面这几条指令顺序来的。


3.7实现原生代码 


我们打开cppUtils.cpp文件,会发现是空的。如果包含了jni.h头文件指令,我们把他删掉,因为我们即将要实现的头文件已经包含了jni,h头文件,所以我们的源文件无需再次包含。

接着我们将头文件需要实现的函数声明复制过来,为了避免出错,强烈建议复制过来。然后修改成下面这个样子。

#include "com_example_jniblog_CppUtils.h"

JNIEXPORT jstring JNICALL Java_com_example_jniblog_CppUtils_getStringFromCpp
  (JNIEnv * env, jobject jthis)
{
   return env->NewStringUTF("来自C++");
}


3.8调用native函数

做完了上面这些,就可以函数调用了。调用和正常的java调用没有差别的。比如这里就是:

private TextView text;
	CppUtils cppUtils = new CppUtils();

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		text = (TextView) findViewById(R.id.text);
		text.setText("从C++获取字符串:" + cppUtils.getStringFromCpp());
	}




3.9 总结


从整个流程下来,我们可以清晰的知道每一步要怎么做为什么这么做这么做的意义,虽然并没有深入讲解,但是对于整个jni流程来说,是一个很好的了解。



---------文章写自:HyHarden---------

--------博客地址:http://blog.csdn.net/qq_25722767-----------


作者:qq_25722767 发表于2016/9/15 21:41:33 原文链接
阅读:80 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles