zoukankan      html  css  js  c++  java
  • Android游戏开发实践(1)之NDK与JNI开发01

    Android游戏开发实践(1)之NDK与JNI开发01

    NDK是Native Developement Kit的缩写,顾名思义,NDK是Google提供的一套原生Java代码与本地C/C++代码“交互”的开发工具集。而Android是运行在Dalvik虚拟机之上,支持通过JNI的方式调用本地C/C++动态链接库。C/C++有着较高的性能和移植性,通过这种调用机制就可以实现多平台开发、多语言混编的Android应用了。当然,这些都是基于JNI实现的。在游戏开发中,这种需求更是必不可少。

    作者:AlphaGL。版权所有,欢迎保留原文链接进行转载

    1、认识JNI

    JNI是Java Native Interface的缩写,也称为Java本地接口。是JVM规范中的一部分,因此,我们可以将任何实现了JVM规范的JNI程序在Java虚拟机中运行。这里的本地接口,主要指的是C/C++所现实的接口。因此,也使得我们可以通过这种方式重用C/C++开发的代码或模块。
    具体关于JNI的详细介绍,可以参见JNI的官方文档。

    Java Native Interface Specification:
    http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/jniTOC.html

    2、JNI的类型和数据结构

    实现原生Java代码与本地C/C++代码,一个重要的环节是将原生Java的类型和数据结构映射成本地C/C++支持的相应的类型和数据结构。

    (1)Java基本数据类型与原生C/C++类型对应关系如下:

    Java类型 本地类型 说明
    boolean jboolean 无符号,8位
    byte jbyte 无符号,8位
    char jchar 无符号,16位
    short jshort 有符号,16位
    int jint 有符号,32位
    long jlong 有符号,64位
    float jfloat 32位
    double jdouble 64位
    void void N/A

    (2)Java引用数据类型与原生C/C++类型对应关系如下:

    Java类型 本地类型
    Object jobject
    Class jclass
    String jstring
    Object[] jobjectArray
    boolean[] jbooleanArray
    byte[] jbyteArray
    char[] jcharArray
    short[] jshortArray
    int[] jintArray
    long[] jlongArray
    float[] jfloatArray
    double[] jdoubleArray

    通过上面的对应关系可以发现,本地类型的命名基本上是在Java原生类型明明的前面加上了个j,组成j-type格式的新类型命名,还是很直观的。

    (3)JNI引用类型的类关系图,如下:

    (上图源自:Java Native Interface Specification文档)

    3、JNI函数的签名

    在函数的声明中,由函数的参数,返回值类型共同构成了函数的签名。因此,将Java函数映射到本地C/C++中的对应也要遵循相应的规则。

    (1)函数数据类型的签名关系如下:

    Java类型 类型签名
    boolean Z
    byte B
    char C
    short S
    int I
    long J
    float F
    double D
    void V
    full-qualified-class(全限定的类) L
    [] [
    boolean[] [Z
    byte[] [B
    char[] [C
    short[] [S
    int[] [I
    long[] [J
    float[] [F
    double[] [D

    注意:

    1. full-qualified-class(全限定的类):指的是引用类型,用L加全类名表示。
    2. 数组类型的签名,只取中括号左半边。

    (2)JNI函数签名格式比较
    Java函数原型:

       return-value fun(params1, params2, params3)
    

    return-value:表示返回值
    params:表示参数

    对应函数签名格式为:

       (params1params2params3)return-value
    

    注意:

    1. JNI函数签名中间都没逗号,没有空格
    2. 返回值在()后面
    3. 如果参数是引用类型,那么参数应该写为:L加全类名加分号。例如:Ljava/lang/String;

    根据这种规则,知道Java函数原型就能判断出对应的JNI函数的签名格式:

       // 原型为:
       boolean  isLoading();
       // 签名格式为:
       ()Z
    
       // 原型为:
       void  setLevel(int level);
       // 签名格式为:
       (I)V
    
       // 原型为:
       char  getCharFunc(int index, String str, int[] value);
       // 签名格式为:
       (ILjava/lang/String;[I)C
    

    4、JNI开发流程

    1.简要开发步骤

    JNI的具体开发流程总结起来分为这么几步:
    (1)在原生java类中声明native方法。native表明该方法为一个本地方法,由C/C++实现。
    (2)使用javac命令将带有声明native方法的类,编译成class字节码。javac是jdk自带的一个命令,一般在javapath/bin(javapath为java安装目录)路径下。
    (3)使用javah命令将编译好的class生成本地C/C++代码的.h头文件。同样,javah也是jdk自带的一个命令。
    (4)实现.h头文件中的方法。
    (5)将本地代码编译成动态库。注意,不同平台的动态库是不一样的。
    (6)在java工程中引用编译好的动态库。

    2.开发实例

    按照上面的开发步骤作为指导,来一步步实现个简单的JNI的例子。
    (1)新建名为HelloJNI的java工程,并新建一个声明了native方法的类。(这里就以Eclipse作为开发IDE举例了)

        package com.hellojni.test;
    
        public class HelloJni {
    
            public native void printJni();
            
            public static void main(String[] args) {
    
            }
        }
    

    (2)使用javac编译该HelloJni的类编译为.class文件。当然,这步也可以由Eclipse来完成即可。

    (3)使用javah命令生成头文件。在命令行终端下输入如下命令:

        javah -classpath E:\workplace\java\HelloJNI\src com.hellojni.test.HelloJni
    

    classpath:是指定加载类的路径
    com.hellojni.test.HelloJni:为完整类名。注意,不需要带java
    具体javah的使用参数介绍,可以输入javah -help

    如果,执行成功,会在当前目录下生成com_hellojni_test_HelloJni.h的头文件。

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

    可以看到javah自动为我们生成了一个Java_com_hellojni_test_HelloJni_printJni的方法。格式是:Java_Packagename_Classname_Methodname。
    首先,这里引入了jni.h的头文件。这个是jdk自带的一个头文件,一般在javapath/include(javapath为java安装目录)。

    (4)打开VS新建一个Win32控制台应用程序,应用程序类型选择DLL(Win平台动态库为.dll)。并将生成的Java_com_hellojni_test_HelloJni_printJni.h头文件拷贝到该工程目录下。
    然后,再将该头文件添加到工程中。如图:

    编译生成一下。会提示找不到jni.h。因此,把jni.h拷贝到工程目录下,并加入到项目中。jni.h一般在javapath/include(javapath为java安装目录)路径下。

    重新编译生成下,会提示找不到jni_md.h。这个文件在,javapath/include/win32路径下。拷贝该文件再加入工程。并修改Java_com_hellojni_test_HelloJni_printJni.h头文件。
    将#include <jni.h>修改为#include "jni.h",在当前目录下找jni.h头文件。

    新建一个hellojni.cpp的源文件。如下:

        #include "stdafx.h"
        #include <iostream>
        #include "com_hellojni_test_HelloJni.h"
    
        using namespace std;
    
        JNIEXPORT void JNICALL Java_com_hellojni_test_HelloJni_printJni(JNIEnv *env, jobject obj)
        {
            cout<<"Hello JNI"<<endl;
        }
    

    (5)再将工程重新生成下,成功的话,会在工程的Debug目录下生成一个HelloJni.dll的动态库。将HelloJni.dll所在的路径添加到环境变量,这样每次重新生成,在任意目录都能访问。

    (6)在java工程中引用刚生成的HelloJni.dll。并加入如下代码:

        package com.hellojni.test;
    
        public class HelloJni {
    
            public native void printJni();
            
            public static void main(String[] args) {
                System.loadLibrary("HelloJni");
                
                HelloJni hello = new HelloJni();
                hello.printJni();
            }
        }
    

    调用System.loadLibrary来加载动态库。注意,动态库的名字不需要加.dll

    运行java工程,这时候会提示Exception in thread “main” java.lang.UnsatisfiedLinkError: no HelloJni in java.library.path。这时候,需要重启下Eclipse。因为,刚配置的环境变量。重启下,Eclipse才能识别。

    重启完毕,运行java工程。控制台会输入:
    Hello JNI
    表明整个JNI调用成功。

    第一篇就介绍这么多,大体明白了JNI的整个开发流程及基本规则。下一篇将介绍下在Android NDK环境下的交叉编译及调用过程。

    技术交流QQ群:528655025
    作者:AlphaGL
    出处:http://www.cnblogs.com/alphagl/
    版权所有,欢迎保留原文链接进行转载

  • 相关阅读:
    HDU 5486 Difference of Clustering 图论
    HDU 5481 Desiderium 动态规划
    hdu 5480 Conturbatio 线段树 单点更新,区间查询最小值
    HDU 5478 Can you find it 随机化 数学
    HDU 5477 A Sweet Journey 水题
    HDU 5476 Explore Track of Point 数学平几
    HDU 5475 An easy problem 线段树
    ZOJ 3829 Known Notation 贪心
    ZOJ 3827 Information Entropy 水题
    zoj 3823 Excavator Contest 构造
  • 原文地址:https://www.cnblogs.com/alphagl/p/6064013.html
Copyright © 2011-2022 走看看