Android手机游戏开发设计实践活动之NDK与JNI开发设

摘要: 拥有前边2~3篇NDK与JNI开发设计有关基本做铺垫,再说根据编码表明下这些方面实际的实际操作及其一些关键的关键点。那麼,就再次NDK与JNI的学习培训小结。 JavaVM和JNIEnv 在jni.h头文档...

拥有前边2~3篇NDK与JNI开发设计有关基本做铺垫,再说根据编码表明下这些方面实际的实际操作及其一些关键的关键点。那麼,就再次NDK与JNI的学习培训小结。

JavaVM和JNIEnv

在jni.h头文档中界定了二种关键的数据信息构造JavaVM和JNIEnv,而且在C和C++中他们的完成不是同的(根据#if defined(__cplusplus)宏界定完成)。实质全是偏向封裝了JNI涵数目录的指针。

JavaVM

是java虚似机在jni层的表明。在Android中一个JVM只容许有一个JavaVM目标。能够线上程间共享资源一个JavaVM目标。

JavaVM申明

在jni中对于C語言自然环境和C++語言自然环境的JavaVM完成有一定的不一样。

C版的JavaVM申明为:

C++编码 typedef const struct JNIInvokeInterface* JavaVM;    struct JNIInvokeInterface {   void* reserved0;   void* reserved1;   void* reserved2;     jint (*DestroyJavaVM)(JavaVM*);   jint (*AttachCurrentThread)(JavaVM*, JNIEnv**, void*);   jint (*DetachCurrentThread)(JavaVM*);   jint (*GetEnv)(JavaVM*, void**, jint);   jint (*AttachCurrentThreadAsDaemon)(JavaVM*, JNIEnv**, void*);  }; 

C++版的JavaVM申明为:

Java编码 typedef _JavaVM JavaVM;    struct _JavaVM {   const struct JNIInvokeInterface* functions;    #if defined(__cplusplus)   jint DestroyJavaVM()   { return functions- DestroyJavaVM(this); }   jint AttachCurrentThread(JNIEnv** p_env, void* thr_args)   { return functions- AttachCurrentThread(this, p_env, thr_args); }   jint DetachCurrentThread()   { return functions- DetachCurrentThread(this); }   jint GetEnv(void** env, jint version)   { return functions- GetEnv(this, env, version); }   jint AttachCurrentThreadAsDaemon(JNIEnv** p_env, void* thr_args)   { return functions- AttachCurrentThreadAsDaemon(this, p_env, thr_args); }  #endif /*__cplusplus*/  }; 

JavaVM获得方法

(1)jni动态性申请注册的方法。在载入动态性连接库的情况下,JVM会启用JNI_OnLoad(JavaVM* vm, void* reserved),并传到JavaVM指针:

C++编码 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {    } 

(2)在当地编码中通快递过启用jint JNI_CreateJavaVM(JavaVM**, JNIEnv**, void*)来建立。

JNIEnv

简易来讲,便是JNIEnv出示了全部JNI涵数启用的插口。不可以线上程间共享资源同一个JNIEnv自变量,仅在建立它的进程合理,假如要在其他进程浏览JVM,必须启用AttachCurrentThread或AttachCurrentThreadAsDaemon将当今进程与JVM关联。再根据JavaVM目标的GetEnv来获得JNIEnv。

JNIEnv申明

与JavaVM相近,JNIEnv在C和C++語言中的申明也是有所不一样。

C版的JavaVM申明为:

C++编码 typedef const struct JNINativeInterface* JNIEnv;    struct JNINativeInterface {   jint (*GetVersion)(JNIEnv *);      } 

C++版的JavaVM申明为:

C++编码 typedef _JNIEnv JNIEnv;    struct _JNIEnv {   /* do not rename this; it does not seem to be entirely opaque */   const struct JNINativeInterface* functions;    #if defined(__cplusplus)     jint GetVersion()   { return functions- GetVersion(this); }     ...  } 

jobject、jclass、jmethodID和jfieldID

jobject:

是JNI对初始java.lang.Object的投射。能够根据启用NewObject来得到一个jobject目标。比如:

env- NewObject(jclass clazz, jmethodID methodID, ...)

jclass:

是JNI对初始java.lang.Class的投射。能够根据启用FindClass来得到jclass目标。比如:

jclass intArrayClass = env- FindClass( [I

jmethodID:

获得相匹配类组员方式的方式id。能够根据启用GetMethodID来获得。比如:

jmethodID myMethodId = env- (jclass clazz, const char *name, const char *sig);

jfieldID:

获得相匹配类组员自变量的字段名id。能够根据启用GetFieldID来得到。比如:

jfieldID nameFieldId = env- GetFieldID(jclass clazz, const char *name, const char *sig)

当地库启用

JNI的载入当地库文件的编码,流程概述以下(同时,也是Android强烈推荐的作法):

(1)在java类的静态数据块中启用System.loadLibrary来载入动态性库,若动态性库的姓名为libcocos2dx.so,那麼,启用为:

Java编码 static {   System.loadLibrary( cocos2dx );  } 

(2)在当地编码中完成JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved);方式。

(3)在该JNI_OnLoad方式中,启用env- RegisterNatives(jclass clazz, const JNINativeMethod *methods, jint nMethods)申请注册全部当地的完成方式。强烈推荐将方式申明为静态数据的,那样不容易占有机器设备上的标记表的室内空间。

JNI通讯

JNI的通讯全过程,实际上便是原生态Java与最底层C/C++数据信息传送的全过程。这儿简易梳理下,数据信息传送分成下列这几类:

传送基本数据信息种类(比如:int,float等)

 传送目标(比如:String,Object,自定类MyObject等)

 传送数字能量数组(比如:int[], String[]等)

 传送结合目标(比如:ArrayList,HashMap等)

而启用方法有能够分成:

(1)java启用native方式

(2)native启用java静态数据方式,非静态数据方式(组员方式),及其获得java类的组员自变量。

下边依照完成方法的不一样融合之上关键点,根据一个案子编码来讲明下实际是怎样完成的。

(1)静态数据申请注册的方法

工程项目构造以下:(这儿只例举出关键表明的项)

XML/HTML编码 JNISample1   │── build.gradle   │── CMakeLists.txt   └── app   ├── build.gradle   ├── CMakeLists.txt   └── src   ├── cpp   │ ├── JNIUtils.h   │ └── JNIUtils.cpp   └──&.alphagl.main   ├── JNIUtils.java   ├── MainActivity.Java   └── Person.java 

编码以下:(这儿干了下简单化,除掉些注解及其模块检测一部分的编码)

MainActivity.java:

Java编码 .alphagl.main;    import android.app.Activity;  import android.os.Bundle;  import android.util.Log;    public class MainActivity extends Activity {     static {   System.loadLibrary( native-lib );   }     protected void onCreate(Bundle savedInstanceState) {   super.onCreate(savedInstanceState);   setContentView(R.layout.activity_main);     Log.i( MainActivity ,  getStringFromJNI =============   + JNIUtils.getStringFromJNI());   Log.i( MainActivity ,  getIntArrayFromJNI =============   + JNIUtils.getIntArrayFromJNI()[0] +  ,  + JNIUtils.getIntArrayFromJNI()[1]);   JNIUtils.setPersonToJNI(new Person(18,  jobs ));   Log.i( MainActivity ,  getPersonFromJNI =============   + JNIUtils.getPersonFromJNI().getAge()+  ,  + JNIUtils.getPersonFromJNI().getName());   }  } 

Person.java:(封裝的自定目标)

Java编码 .alphagl.main;    import android.util.Log;    public class Person {   private int age;   private String name;     public Person(int age, String name) {   this.age = age;   this.name = name;   }     public void setAge(int age) {   this.age = age;   }     public int getAge() {   return age;   }     public void setName(String name) {   this.name = name;   }     public String getName() {   return name;   }     public void printPerson() {   Log.d( MainActivity ,  age ========   + age +  ,  +  name ========   + name);   }  } 

JNIUtils.java:

Java编码 .alphagl.main;    public class JNIUtils {   public static native String getStringFromJNI();   public static native int[] getIntArrayFromJNI();   public static native void setPersonToJNI(Person person);   public static native Person getPersonFromJNI();  } 

JNIUtils.h:

C++编码 #include  jni.h   #include  stdio.h     #ifndef&_alphagl_main_JNIUtils  #define&_alphagl_main_JNIUtils  #ifdef __cplusplus  extern  C  {  #endif    JNIEXPORT jstring JNICALL&_alphagl_main_JNIUtils_getStringFromJNI   (JNIEnv *, jclass);      JNIEXPORT jintArray JNICALL&_alphagl_main_JNIUtils_getIntArrayFromJNI   (JNIEnv *, jclass);      JNIEXPORT void JNICALL&_alphagl_main_JNIUtils_setPersonToJNI   (JNIEnv *, jclass, jobject);      JNIEXPORT jobject JNICALL&_alphagl_main_JNIUtils_getPersonFromJNI   (JNIEnv *, jclass);    #ifdef __cplusplus  }  #endif  #endif 

JNIUtils.cpp

C++编码 #include  JNIUtils.h   #include  android/log.h     #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,  MainActivity , __VA_ARGS__)  #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,  MainActivity , __VA_ARGS__)  #define LOGE(...) __android_log_print(ANDROID_LOG_ERROE,  MainActivity , __VA_ARGS__)      JNIEXPORT jstring JNICALL&_alphagl_main_JNIUtils_getStringFromJNI (JNIEnv *env, jclass jcls) {   LOGD(  ====================== getStringFromJNI );   // 结构一个String标识符串   return env- NewStringUTF( Hello from jni );  }      JNIEXPORT jintArray JNICALL&_alphagl_main_JNIUtils_getIntArrayFromJNI (JNIEnv *env, jclass jcls) {   LOGD(  ====================== getIntArrayFromJNI );   // 结构一个int[]数字能量数组   jintArray intArray = env- NewIntArray(2);   int size[]={640, 960};   // 给int[]数字能量数组取值   env- SetIntArrayRegion(intArray, 0, 2, size);     return intArray;  }      JNIEXPORT void JNICALL&_alphagl_main_JNIUtils_setPersonToJNI (JNIEnv *env, jclass jcls, jobject jobj) {   LOGD(  ====================== setPersonToJNI );   jclass jperson = env- GetObjectClass(jobj);   if (jperson != NULL) {   // 获得Person目标的age字段名id   jfieldID ageFieldId = env- GetFieldID(jperson,  age ,  I );   // 获得Person目标的name字段名id   jfieldID nameFieldId = env- GetFieldID(jperson,  name ,  Ljava/lang/String; );     // 获得Person的age组员自变量   jint age = env- GetIntField(jobj, ageFieldId);   // 获得Person的name组员自变量   jstring name = (jstring)env- GetObjectField(jobj, nameFieldId);     const char *c_name = env- GetStringUTFChars(name, NULL);     // 复印从Java传送回来的Person目标的age和name自变量   LOGD( age ===== %d, name ===== %s , age, c_name);   }     // 下列是以JNI结构Java目标,并启用Java类中的组员方式,仅作为演试   // 获得Person目标的class   jclass jstu = env- FindClass( com/alphagl/main/Person );   // 获得Person目标的结构方式的方式id   jmethodID personMethodId = env- GetMethodID(jperson,  init ,  (ILjava/lang/String;)V );   // 结构一个String标识符串   jstring name = env- NewStringUTF( bill );     // 结构一个Person目标   jobject jPersonObj = env- NewObject(jstu, personMethodId, 30, name);   // 获得Person目标的printPerson组员方式的方式id   jmethodID jid = env- GetMethodID(jstu,  printPerson ,  ()V );   // 启用java的printPerson方式   env- CallVoidMethod(jPersonObj, jid);  }      JNIEXPORT jobject JNICALL&_alphagl_main_JNIUtils_getPersonFromJNI(JNIEnv *env, jclass jcls) {   LOGD(  ====================== getPersonFromJNI );   // 获得Person目标的class   jclass jstudent = env- FindClass( com/alphagl/main/Person );   // 获得Person目标的结构方式的方式id   jmethodID studentMethodId = env- GetMethodID(jstudent,  init ,  (ILjava/lang/String;)V );   // 结构一个String标识符串   jstring name = env- NewStringUTF( john );   // 结构一个Person目标   jobject jstudentObj = env- NewObject(jstudent, studentMethodId, 20, name);     return jstudentObj;  } 

这儿再提一下,如上`JNIUtils.java`类中界定好啦native方式,怎样依据目标的方式签字转化成相匹配的C/C++方式的申明。这一部份内容在Android手机游戏开发设计实践活动(1)之NDK与JNI开发设计01 早已提及过,大家能够依靠javah来依据编译程序后的.class转化成针对的头文档。

一般作法是:

Android游戏开发实践之NDK与JNI开发04

在AndroidStudio中能够:

Tools- External Tools - 加上

Android游戏开发实践之NDK与JNI开发04

(1)javah所属的相对路径

(2)指令行主要参数

(3)头文档转化成的相对路径

Android游戏开发实践之NDK与JNI开发04

在申明了native方式的类,鼠标右键实行javah就可以。

(2)动态性申请注册的方法

工程项目构造以下:(这儿只例举出关键表明的项)

XML/HTML编码 JNISample2   │── build.gradle   │── CMakeLists.txt   └── app   ├── build.gradle   ├── CMakeLists.txt   └── src   ├── cpp   │ └── JNIUtils.cpp   │   └──&.alphagl.main   ├── JNIUtils.java   ├── MainActivity.Java   └── Person.java 

这儿关键看看不一样的编码一部分,即JNIUtils.cpp。

JNIUtils.cpp:

C++编码 #include  jni.h   #include  string   #include  android/log.h     #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,  MainActivity , __VA_ARGS__)  #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,  MainActivity , __VA_ARGS__)  #define LOGE(...) __android_log_print(ANDROID_LOG_ERROE,  MainActivity , __VA_ARGS__)    #define CLASSNAME  com/alphagl/main/JNIUtils     static jstring getStringFromJNI_native(JNIEnv *env, jclass jcls) {   LOGD(  ====================== getStringFromJNI );   // 结构一个String标识符串   return env- NewStringUTF( Hello from jni );  }    static jarray getIntArrayFromJNI_native(JNIEnv *env, jclass jcls) {   LOGD(  ====================== getIntArrayFromJNI );   // 结构一个int[]数字能量数组   jintArray intArray = env- NewIntArray(2);   int size[]={640, 960};   // 给int[]数字能量数组取值   env- SetIntArrayRegion(intArray, 0, 2, size);     return intArray;  }    static void setJniPerson_native(JNIEnv *env, jclass jcls, jobject jobj) {   LOGD(  ====================== setPersonToJNI );   jclass jperson = env- GetObjectClass(jobj);   if (jperson != NULL) {   // 获得Person目标的age字段名id   jfieldID ageFieldId = env- GetFieldID(jperson,  age ,  I );   // 获得Person目标的name字段名id   jfieldID nameFieldId = env- GetFieldID(jperson,  name ,  Ljava/lang/String; );     // 获得Person的age组员自变量   jint age = env- GetIntField(jobj, ageFieldId);   // 获得Person的name组员自变量   jstring name = (jstring)env- GetObjectField(jobj, nameFieldId);     const char *c_name = env- GetStringUTFChars(name, NULL);     // 复印从Java传送回来的Person目标的age和name自变量   LOGD( age ===== %d, name ===== %s , age, c_name);   }     // 下列是以JNI结构Java目标,并启用Java类中的组员方式,仅作为演试   // 获得Person目标的class   jclass jstu = env- FindClass( com/alphagl/main/Person );   // 获得Person目标的结构方式的方式id   jmethodID personMethodId = env- GetMethodID(jperson,  init ,  (ILjava/lang/String;)V );   // 结构一个String标识符串   jstring name = env- NewStringUTF( bill );     // 结构一个Person目标   jobject jPersonObj = env- NewObject(jstu, personMethodId, 30, name);   // 获得Person目标的printPerson组员方式的方式id   jmethodID jid = env- GetMethodID(jstu,  printPerson ,  ()V );   // 启用java的printPerson方式   env- CallVoidMethod(jPersonObj, jid);  }    static jobject getJniPerson_native(JNIEnv *env, jclass jcls) {   LOGD(  ====================== getPersonFromJNI );   // 获得Person目标的class   jclass jstudent = env- FindClass( com/alphagl/main/Person );   // 获得Person目标的结构方式的方式id   jmethodID studentMethodId = env- GetMethodID(jstudent,  init ,  (ILjava/lang/String;)V );   // 结构一个String标识符串   jstring name = env- NewStringUTF( john );   // 结构一个Person目标   jobject jstudentObj = env- NewObject(jstudent, studentMethodId, 20, name);     return jstudentObj;  }    static JNINativeMethod gMethods[] = {   { getStringFromJNI ,  ()Ljava/lang/String; , (void*)getStringFromJNI_native},   { getIntArrayFromJNI ,  ()[I , (void*)getIntArrayFromJNI_native},   { setPersonToJNI ,  (Lcom/alphagl/main/Person;)V , (void*)setJniPerson_native},   { getPersonFromJNI ,  ()Lcom/alphagl/main/Person; , (void*)getJniPerson_native}  };    static jint registerNativeMethods(JNIEnv *env, const char* className, JNINativeMethod *gMethods, int numMethods) {   jclass jcls;   jcls = env- FindClass(className);   if (jcls == NULL) {   return JNI_FALSE;   }     if (env- RegisterNatives(jcls, gMethods, numMethods)   0) {   return JNI_FALSE;   }     return JNI_TRUE;  }    static jint registerNative(JNIEnv *env) {   return registerNativeMethods(env, CLASSNAME, gMethods, sizeof(gMethods) / sizeof(gMethods[0]));  }    JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {   JNIEnv *env = NULL;   if ((vm- GetEnv((void**) env, JNI_VERSION_1_6)) != JNI_OK) {   return JNI_ERR;   }     if (!registerNative(env)) {   return JNI_ERR;   }     return JNI_VERSION_1_6;  } 

最终的实行結果为:

Android游戏开发实践之NDK与JNI开发04

二种完成方法较为:

(1)动态性申请注册中,能够无需申明形如Java_packageName_className_methodName文件格式的方式。

(2)动态性申请注册中,要重新写过JNI_OnLoad方式,手动式启用RegisterNatives来申请注册当地方式,及其申明在JNINativeMethod中。

(3)动态性申请注册,显著这类方法更灵便,但对编码规定高些,强烈推荐应用这类方法。

之上实例编码早已提交Github,有必须的能够自主查询。

cnsuperx/android-jni-example

JNI调节

假如安裝了LLVM自然环境得话,立即将Jni Debuggable选择项开启就可以。自然环境构建能够参照Android手机游戏开发设计实践活动(1)之NDK与JNI开发设计03。

Android游戏开发实践之NDK与JNI开发04

然后立即在C或C++编码中设定断点就可以。

Android手机游戏开发设计实践活动之NDK与JNI开发设计03 (:55)

Android手机游戏开发设计实践活动之NDK与JNI开发设计02 (:31)

Android手机游戏开发设计实践活动之NDK与JNI开发设计01 (:39)

Android Studio下NDK开发设计该怎样配备 (:42)

Android提升21篇之十六:应用NDK把彩图变换为灰度值图 (:50)

指令行下应用Android NDK交叉式编译程序专用工具的方式 (:18)

cocos2d-x Android ndk-gdb真机调节自然环境构建 (:11)

《Android运用特性提升》 (:5)

Android开发设计自然环境中的定义和专用工具详细介绍 (:25)

Windows系统软件下配备Android NDK开发设计自然环境 (:55)


网页链接(选填)

认证(*)

文章正文(*)(留言板留言最多篇幅:1000)

记牢我,下一次回应时无需再次键入本人信息内容



联系我们

全国服务热线:4000-399-000 公司邮箱:343111187@qq.com

  工作日 9:00-18:00

关注我们

官网公众号

官网公众号

Copyright?2020 广州凡科互联网科技股份有限公司 版权所有 粤ICP备10235580号 客服热线 18720358503

技术支持:手机网页模板