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实现的。在游戏开发中,这种需求更是必不可少。

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环境下的交叉编译及调用过程。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容