关闭

JNI编程

173人阅读 评论(0) 收藏 举报
分类:

1、JNI简介    

        JNI是Java调用Native机制,是Java语言自己的特性全称为Java Native Interface,JNI是java语言提供的Java和C/C++相互沟通的机制,Java可以通过JNI调用本地的C/C++代码,本地的C/C++的代码也可以调用java代码。JNI 是本地编程接口,Java和C/C++互相通过的接口。Java通过C/C++使用本地的代码的一个关键性原因在于C/C++代码的高效性。

2、Java通过JNI机制和C/C++沟通的具体步骤

       1)、编写包含native本地方法的java类
       2)、通过javah工具生成C/C++语言的头文件
       3)、使用C/C++语言实现头文件
       4)、使用交叉编译工具对C/C++本地代码进行编译,最后通过链接生成*.so可执行的C/C++库
       5)、实际执行Java代码去和本地的C/C++代码互相沟通
3、Android的NDK

       Android的NDK(Native Development Kit)开发工具集是Google公司推出的帮助Android开发者通过C/C++本地语言编写应用的开发包,包含了C/C++的头文件、库文件、说明文档和示例代码。通过Android的NDK,Android软件开发者可以很方便地实现Java和本地C/C++代码的相互调用,充分发挥本地硬件特性和C/C++高效性。

4、开发第一个Android NDK程序

        1)、编写包含native本地方法的java类

         创建一个工厂名称为HelloAdroidNDK的Android工程,在此工程中创建一个NativeClass.java文件,在此文件中加载本地的C库文件,同时声明本地的C方法。

package com.igood.ndk.hello;

public class NativeClass {
	//静态代码块在类加载时会执行,这个时候就会加载本地的C库文件
	static{
		//加载本地的C库文件
		System.loadLibrary("helloAndroidNDK");
	}
	//声明加载的C库中的本地方法
	public native String sayHelloToNDK();
}
        保存文件后,Eclipse自动帮我们完成该类的编译工作,此时会在本工程下的bin\classes\com\igood\ndk\hello路径下生成NativeClass.class字节码文件。

        2)、通过javah工具生成C/C++语言的头文件

        在HelloAdroidNDK工程目录下建立一个名为jni的文件夹,打开Windows的命令窗口,进入到HelloAdroidNDK工程目录下,使用javah工具产生NativeClass.class的头文件具体命令如下图:


        此时我们成功地执行了javah命令并生成了头文件,打开jni文件夹,可以看到自动生成的头文件com_igood_ndk_hello_NativeClass.h,具体内容如下:

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

#ifndef _Included_com_igood_ndk_hello_NativeClass
#define _Included_com_igood_ndk_hello_NativeClass
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_igood_ndk_hello_NativeClass
 * Method:    sayHelloToNDK
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_igood_ndk_hello_NativeClass_sayHelloToNDK
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif
       3)、使用C/C++语言实现头文件
        此时我们可以新建一个com_igood_ndk_hello_NativeClass.c文件,实现相应的函数功能

#include "com_igood_ndk_hello_NativeClass.h"
JNIEXPORT jstring JNICALL Java_com_igood_ndk_hello_NativeClass_sayHelloToNDK
  (JNIEnv *env , jobject thiz)
  {
	  return (*env)->NewStringUTF(env,"Hello Android NDK!!");
  }
        4)、使用交叉编译工具对C/C++本地代码进行编译,最后通过链接生成*.so可执行的C/C++库
        编写Android.mk编译脚本文件,主要指定生成的so模块名称,所要编译的c文件和所要链接的其他C库,内容如下图:


         打开Windows的命令窗口,进入到HelloAdroidNDK工程目录下,执行ndk-build命令就可以编译,,在obj\local\armeabi\目录下生成libhelloAndroidNDK.so库文件。

         5)、实际执行Java代码去和本地的C/C++代码互相沟通

         修改MainActivity.java的内容,调用NativeClass类中声明的本地函数,修改后内容如下:

package com.igood.ndk.hello;

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

public class MainActivity extends Activity {
	private TextView tvMsg;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		NativeClass nc = new NativeClass();
		tvMsg = (TextView) findViewById(R.id.tv_msg);
		tvMsg.setText(nc.sayHelloToNDK());
	}

}

       运行的界面如下图所示:


       文本内容显示了从本地C代码中获得字符串的内容,说明成功调用本地的C语言代码。

5、JNI中的JavaVM与JNIEnv对象

         在标准的Java平台下,每个Process里可以产生很多JavaVM对象,每个JavaVM对象都有一个与之对应的JavaVM对象,但是在Android平台上,每个Process只能产生一个DalvikVM对象,也就是说在一个Android的进程中是通过有且只有一个虚拟器对象来服务所有Java和C++代码的。
        1、JNIEnv *内部包含一个Pointer,Pointer指向Dalvik的JavaVM对象的Fanction Table,JNIEnv *关于程序执行环境的众多函数正是来源于Dalvik虚拟机
        2、Android中每当一个Java线程第一次要调用本地C/C++代码时,Dalvik虚拟机实例会为该Java线程产生一个JNIEnv *指针
        3、Java每条线程在和C/C++互相调用时,JNIEnv*是相互独立的,互不干扰
        4、每本地的C/C++代码想获得当前线程所要使用的JNIEnv时,可以使用Dalvik VM对象的Java VM* jvm->getEnv()方法,该方法即会返回当前线程所在的JNIEnv*。

6、在Android的NDK中,Java、C/C++、Dalvik VM关系如下:
        1、java的dex字节码和C/C++的*.so同时运行DalvikVM之内,共同使用一个进程空间。每次使用jni调用c/c++开辟一个线程去处理
        2、java和C/C++可以相互调用,调用的关键是DalvikVM
        3、一般而言,比较经典的模式是Java通过JNI的C组建和C++相互沟通,一般业务处理放在C/C++中
        4、C++代码处于核心控制地位更具价值
        当java需要C/C++代码时,在DalvikVM虚拟机中加载动态链接库时,会先调用JNI_Onload()函数,此时就会把javaVM对象的指针存储于C层JNI组建的全局环境中,在JAVA层调用C层的本地库函数时,调用C本地函数线程必然通过Dalvik VM来调用C本地函数,测试Dalvik虚拟机会为本地的C组建实例化一个JNIEnv指针,该指针指向Dalvik虚拟机的具体函数列表,当JNI的C组件调用java层方法和属性时,需要通过JNIEnv指针来进行调用。
        当C++组件主动调用Java层方法时,需要通过JNI的C组件把JNIEnv指针传递给C++组件,此后,c++组件即可通过JNIEnv指针来掌控Java层代码。


0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:13015次
    • 积分:487
    • 等级:
    • 排名:千里之外
    • 原创:35篇
    • 转载:18篇
    • 译文:0篇
    • 评论:2条
    文章分类
    最新评论