Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android游戲 >> Android游戲開發 >> Android游戲開發實踐之NDK與JNI開發01

Android游戲開發實踐之NDK與JNI開發01

編輯:Android游戲開發

  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函數的簽名格式:

Java代碼
  1. // 原型為:  
  2. boolean  isLoading();  
  3. // 簽名格式為:  
  4. ()Z  
Java代碼
  1. // 原型為:  
  2. void  setLevel(int level);  
  3. // 簽名格式為:  
  4. (I)V  
Java代碼
  1. // 原型為:  
  2. char  getCharFunc(int index, String str, int[] value);  
  3. // 簽名格式為:  
  4. (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舉例了)

Java代碼
  1. package com.hellojni.test;  
  2.   
  3. public class HelloJni {  
  4.   
  5.     public native void printJni();  
  6.       
  7.     public static void main(String[] args) {  
  8.   
  9.     }  
  10. }  

  (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的頭文件。

C++代碼
  1. /* DO NOT EDIT THIS FILE - it is machine generated */  
  2. #include <jni.h>  
  3. /* Header for class com_hellojni_test_HelloJni */  
  4.   
  5. #ifndef _Included_com_hellojni_test_HelloJni  
  6. #define _Included_com_hellojni_test_HelloJni  
  7. #ifdef __cplusplus  
  8. extern "C" {  
  9. #endif  
  10. /* 
  11. * Class:     com_hellojni_test_HelloJni 
  12. * Method:    printJni 
  13. * Signature: ()V 
  14. */  
  15. JNIEXPORT void JNICALL Java_com_hellojni_test_HelloJni_printJni(JNIEnv *, jobject);  
  16.   
  17. #ifdef __cplusplus  
  18. }  
  19. #endif  
  20. #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 修改為#include "jni.h",在當前目錄下找jni.h頭文件。

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

C++代碼
  1. #include "stdafx.h"  
  2. #include <iostream>  
  3. #include "com_hellojni_test_HelloJni.h"  
  4.   
  5. using namespace std;  
  6.   
  7. JNIEXPORT void JNICALL Java_com_hellojni_test_HelloJni_printJni(JNIEnv *env, jobject obj)  
  8. {  
  9.     cout<<"Hello JNI"<<endl;  
  10. }  

  (5)再將工程重新生成下,成功的話,會在工程的Debug目錄下生成一個HelloJni.dll的動態庫。將HelloJni.dll所在的路徑添加到環境變量,這樣每次重新生成,在任意目錄都能訪問。

  (6)在java工程中引用剛生成的HelloJni.dll。並加入如下代碼:

Java代碼
  1. package com.hellojni.test;  
  2.   
  3. public class HelloJni {  
  4.   
  5.     public native void printJni();  
  6.       
  7.     public static void main(String[] args) {  
  8.         System.loadLibrary("HelloJni");  
  9.           
  10.         HelloJni hello = new HelloJni();  
  11.         hello.printJni();  
  12.     }  
  13. }  

  調用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環境下的交叉編譯及調用過程。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved