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閱讀 228,786評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,656評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,697評論 0 379
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,098評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,855評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,254評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,322評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,473評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,014評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,833評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,016評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,568評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,273評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,680評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,946評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,730評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,006評論 2 374

推薦閱讀更多精彩內容