有了前面几篇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++代码
  1. typedef?const?struct?JNIInvokeInterface*?JavaVM;??
  2. ??
  3. struct?JNIInvokeInterface?{??
  4. ????void*???????reserved0;??
  5. ????void*???????reserved1;??
  6. ????void*???????reserved2;??
  7. ??
  8. ????jint????????(*DestroyJavaVM)(JavaVM*);??
  9. ????jint????????(*AttachCurrentThread)(JavaVM*,?JNIEnv**,?void*);??
  10. ????jint????????(*DetachCurrentThread)(JavaVM*);??
  11. ????jint????????(*GetEnv)(JavaVM*,?void**,?jint);??
  12. ????jint????????(*AttachCurrentThreadAsDaemon)(JavaVM*,?JNIEnv**,?void*);??
  13. };??

  C++版的JavaVM声明为:

Java代码
  1. typedef?_JavaVM?JavaVM;??
  2. ??
  3. struct?_JavaVM?{??
  4. ????const?struct?JNIInvokeInterface*?functions;??
  5. ??
  6. #if?defined(__cplusplus)??
  7. ????jint?DestroyJavaVM()??
  8. ????{?return?functions->DestroyJavaVM(this);?}??
  9. ????jint?AttachCurrentThread(JNIEnv**?p_env,?void*?thr_args)??
  10. ????{?return?functions->AttachCurrentThread(this,?p_env,?thr_args);?}??
  11. ????jint?DetachCurrentThread()??
  12. ????{?return?functions->DetachCurrentThread(this);?}??
  13. ????jint?GetEnv(void**?env,?jint?version)??
  14. ????{?return?functions->GetEnv(this,?env,?version);?}??
  15. ????jint?AttachCurrentThreadAsDaemon(JNIEnv**?p_env,?void*?thr_args)??
  16. ????{?return?functions->AttachCurrentThreadAsDaemon(this,?p_env,?thr_args);?}??
  17. #endif?/*__cplusplus*/??
  18. };??

  JavaVM获取方式

  (1)jni动态注册的方式。在加载动态链接库的时候,JVM会调用JNI_OnLoad(JavaVM* vm, void* reserved),并传入JavaVM指针:

C++代码
  1. JNIEXPORT?jint?JNICALL?JNI_OnLoad(JavaVM*?vm,?void*?reserved)?{??
  2. ??
  3. }??

  (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++代码
  1. typedef?const?struct?JNINativeInterface*?JNIEnv;??
  2. ??
  3. struct?JNINativeInterface?{??
  4. ????????jint????????(*GetVersion)(JNIEnv?*);??
  5. ????????···??
  6. }??

  C++版的JavaVM声明为:

C++代码
  1. typedef?_JNIEnv?JNIEnv;??
  2. ??
  3. struct?_JNIEnv?{??
  4. ????/*?do?not?rename?this;?it?does?not?seem?to?be?entirely?opaque?*/??
  5. ????const?struct?JNINativeInterface*?functions;??
  6. ??
  7. #if?defined(__cplusplus)??
  8. ??
  9. ????jint?GetVersion()??
  10. ????{?return?functions->GetVersion(this);?}??
  11. ??
  12. ????...??
  13. }??

  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代码
  1. static?{??
  2. ????System.loadLibrary("cocos2dx");??
  3. }??

  (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代码
  1. JNISample1????
  2. ??│──?build.gradle??
  3. ??│──?CMakeLists.txt???
  4. ??└──?app???
  5. ??????├──?build.gradle??
  6. ??????├──?CMakeLists.txt??
  7. ??????└──?src???
  8. ??????????├──?cpp??
  9. ??????????│????├──?JNIUtils.h??
  10. ??????????│????└──?JNIUtils.cpp??
  11. ??????????└──?com.alphagl.main??
  12. ????????????????????├──?JNIUtils.java??
  13. ????????????????????├──?MainActivity.Java??
  14. ????????????????????└──?Person.java??

  代码如下:(这里做了下简化,去掉些注释以及单元测试部分的代码)

  MainActivity.java:

Java代码
  1. package?com.alphagl.main;??
  2. ??
  3. import?android.app.Activity;??
  4. import?android.os.Bundle;??
  5. import?android.util.Log;??
  6. ??
  7. public?class?MainActivity?extends?Activity?{??
  8. ??
  9. ????static?{??
  10. ????????System.loadLibrary("native-lib");??
  11. ????}??
  12. ??
  13. ????protected?void?onCreate(Bundle?savedInstanceState)?{??
  14. ????????super.onCreate(savedInstanceState);??
  15. ????????setContentView(R.layout.activity_main);??
  16. ??
  17. ????????Log.i("MainActivity",?"getStringFromJNI?=============?"?+?JNIUtils.getStringFromJNI());??
  18. ????????Log.i("MainActivity",?"getIntArrayFromJNI?=============?"?+?JNIUtils.getIntArrayFromJNI()[0]?+?","?+?JNIUtils.getIntArrayFromJNI()[1]);??
  19. ????????JNIUtils.setPersonToJNI(new?Person(18,?"jobs"));??
  20. ????????Log.i("MainActivity",?"getPersonFromJNI?=============?"?+?JNIUtils.getPersonFromJNI().getAge()+?","?+?JNIUtils.getPersonFromJNI().getName());??
  21. ????}??
  22. }??

  Person.java:(封装的自定义对象)

Java代码
  1. package?com.alphagl.main;??
  2. ??
  3. import?android.util.Log;??
  4. ??
  5. public?class?Person?{??
  6. ????private?int?age;??
  7. ????private?String?name;??
  8. ??
  9. ????public?Person(int?age,?String?name)?{??
  10. ????????this.age?=?age;??
  11. ????????this.name?=?name;??
  12. ????}??
  13. ??
  14. ????public?void?setAge(int?age)?{??
  15. ????????this.age?=?age;??
  16. ????}??
  17. ??
  18. ????public?int?getAge()?{??
  19. ????????return?age;??
  20. ????}??
  21. ??
  22. ????public?void?setName(String?name)?{??
  23. ????????this.name?=?name;??
  24. ????}??
  25. ??
  26. ????public?String?getName()?{??
  27. ????????return?name;??
  28. ????}??
  29. ??
  30. ????public?void?printPerson()?{??
  31. ????????Log.d("MainActivity",?"age?========?"?+?age?+?","?+?"name?========?"?+?name);??
  32. ????}??
  33. }??

  JNIUtils.java:

Java代码
  1. package?com.alphagl.main;??
  2. ??
  3. public?class?JNIUtils?{??
  4. ????public?static?native?String?getStringFromJNI();??
  5. ????public?static?native?int[]?getIntArrayFromJNI();??
  6. ????public?static?native?void?setPersonToJNI(Person?person);??
  7. ????public?static?native?Person?getPersonFromJNI();??
  8. }??

  JNIUtils.h:

C++代码
  1. #include???
  2. #include???
  3. ??
  4. #ifndef?_Included_com_alphagl_main_JNIUtils??
  5. #define?_Included_com_alphagl_main_JNIUtils??
  6. #ifdef?__cplusplus??
  7. extern?"C"?{??
  8. #endif??
  9. ??
  10. JNIEXPORT?jstring?JNICALL?Java_com_alphagl_main_JNIUtils_getStringFromJNI??
  11. ??(JNIEnv?*,?jclass);??
  12. ??
  13. ??
  14. JNIEXPORT?jintArray?JNICALL?Java_com_alphagl_main_JNIUtils_getIntArrayFromJNI??
  15. ??(JNIEnv?*,?jclass);??
  16. ??
  17. ??
  18. JNIEXPORT?void?JNICALL?Java_com_alphagl_main_JNIUtils_setPersonToJNI??
  19. ??(JNIEnv?*,?jclass,?jobject);??
  20. ??
  21. ??
  22. JNIEXPORT?jobject?JNICALL?Java_com_alphagl_main_JNIUtils_getPersonFromJNI??
  23. ??(JNIEnv?*,?jclass);??
  24. ??
  25. #ifdef?__cplusplus??
  26. }??
  27. #endif??
  28. #endif??

  JNIUtils.cpp

C++代码
  1. #include?"JNIUtils.h"??
  2. #include???
  3. ??
  4. #define?LOGI(...)?__android_log_print(ANDROID_LOG_INFO,?"MainActivity",?__VA_ARGS__)??
  5. #define?LOGD(...)?__android_log_print(ANDROID_LOG_DEBUG,?"MainActivity",?__VA_ARGS__)??
  6. #define?LOGE(...)?__android_log_print(ANDROID_LOG_ERROE,?"MainActivity",?__VA_ARGS__)??
  7. ??
  8. ??
  9. JNIEXPORT?jstring?JNICALL?Java_com_alphagl_main_JNIUtils_getStringFromJNI?(JNIEnv?*env,?jclass?jcls)?{??
  10. ????LOGD("?======================?getStringFromJNI");??
  11. ????//?构造一个String字符串??
  12. ????return?env->NewStringUTF("Hello?from?jni");??
  13. }??
  14. ??
  15. ??
  16. JNIEXPORT?jintArray?JNICALL?Java_com_alphagl_main_JNIUtils_getIntArrayFromJNI?(JNIEnv?*env,?jclass?jcls)?{??
  17. ????LOGD("?======================?getIntArrayFromJNI");??
  18. ????//?构造一个int[]数组??
  19. ????jintArray?intArray?=?env->NewIntArray(2);??
  20. ????int?size[]={640,?960};??
  21. ????//?给int[]数组赋值??
  22. ????env->SetIntArrayRegion(intArray,?0,?2,?size);??
  23. ??
  24. ????return?intArray;??
  25. }??
  26. ??
  27. ??
  28. JNIEXPORT?void?JNICALL?Java_com_alphagl_main_JNIUtils_setPersonToJNI?(JNIEnv?*env,?jclass?jcls,?jobject?jobj)?{??
  29. ????LOGD("?======================?setPersonToJNI");??
  30. ????jclass?jperson?=?env->GetObjectClass(jobj);??
  31. ????if?(jperson?!=?NULL)?{??
  32. ????????//?获取Person对象的age字段id??
  33. ????????jfieldID?ageFieldId?=?env->GetFieldID(jperson,?"age",?"I");??
  34. ????????//?获取Person对象的name字段id??
  35. ????????jfieldID?nameFieldId?=?env->GetFieldID(jperson,?"name",?"Ljava/lang/String;");??
  36. ??
  37. ????????//?获取Person的age成员变量??
  38. ????????jint?age?=?env->GetIntField(jobj,?ageFieldId);??
  39. ????????//?获取Person的name成员变量??
  40. ????????jstring?name?=?(jstring)env->GetObjectField(jobj,?nameFieldId);??
  41. ??
  42. ????????const?char?*c_name?=?env->GetStringUTFChars(name,?NULL);??
  43. ??
  44. ????????//?打印从Java传递过来的Person对象的age和name变量??
  45. ????????LOGD("age?=====?%d,?name?=====?%s",?age,?c_name);??
  46. ????}??
  47. ??
  48. ????//?以下是从JNI构造Java对象,并调用Java类中的成员方法,仅用作演示??
  49. ????//?获取Person对象的class??
  50. ????jclass?jstu?=?env->FindClass("com/alphagl/main/Person");??
  51. ????//?获取Person对象的构造方法的方法id??
  52. ????jmethodID?personMethodId?=?env->GetMethodID(jperson,?"",?"(ILjava/lang/String;)V");??
  53. ????//?构造一个String字符串??
  54. ????jstring?name?=?env->NewStringUTF("bill");??
  55. ??
  56. ????//?构造一个Person对象??
  57. ????jobject??jPersonObj?=?env->NewObject(jstu,?personMethodId,?30,?name);??
  58. ????//?获取Person对象的printPerson成员方法的方法id??
  59. ????jmethodID?jid?=?env->GetMethodID(jstu,?"printPerson",?"()V");??
  60. ????//?调用java的printPerson方法??
  61. ????env->CallVoidMethod(jPersonObj,?jid);??
  62. }??
  63. ??
  64. ??
  65. JNIEXPORT?jobject?JNICALL?Java_com_alphagl_main_JNIUtils_getPersonFromJNI(JNIEnv?*env,?jclass?jcls)?{??
  66. ????LOGD("?======================?getPersonFromJNI");??
  67. ????//?获取Person对象的class??
  68. ????jclass?jstudent?=?env->FindClass("com/alphagl/main/Person");??
  69. ????//?获取Person对象的构造方法的方法id??
  70. ????jmethodID?studentMethodId?=?env->GetMethodID(jstudent,?"",?"(ILjava/lang/String;)V");??
  71. ????//?构造一个String字符串??
  72. ????jstring?name?=?env->NewStringUTF("john");??
  73. ????//?构造一个Person对象??
  74. ????jobject??jstudentObj?=?env->NewObject(jstudent,?studentMethodId,?20,?name);??
  75. ??
  76. ????return?jstudentObj;??
  77. }??

  这里再提一下,如上`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代码
  1. JNISample2????
  2. ??│──?build.gradle??
  3. ??│──?CMakeLists.txt???
  4. ??└──?app???
  5. ??????├──?build.gradle??
  6. ??????├──?CMakeLists.txt??
  7. ??????└──?src???
  8. ??????????├──?cpp??
  9. ??????????│???└──?JNIUtils.cpp??
  10. ??????????│??????
  11. ??????????└──?com.alphagl.main??
  12. ????????????????????├──?JNIUtils.java??
  13. ????????????????????├──?MainActivity.Java??
  14. ????????????????????└──?Person.java??

  这里主要看下不同的代码部分,即JNIUtils.cpp。

  JNIUtils.cpp:

C++代码
  1. #include???
  2. #include???
  3. #include???
  4. ??
  5. #define?LOGI(...)?__android_log_print(ANDROID_LOG_INFO,?"MainActivity",?__VA_ARGS__)??
  6. #define?LOGD(...)?__android_log_print(ANDROID_LOG_DEBUG,?"MainActivity",?__VA_ARGS__)??
  7. #define?LOGE(...)?__android_log_print(ANDROID_LOG_ERROE,?"MainActivity",?__VA_ARGS__)??
  8. ??
  9. #define?CLASSNAME?"com/alphagl/main/JNIUtils"??
  10. ??
  11. static?jstring?getStringFromJNI_native(JNIEnv?*env,?jclass?jcls)?{??
  12. ????LOGD("?======================?getStringFromJNI");??
  13. ????//?构造一个String字符串??
  14. ????return?env->NewStringUTF("Hello?from?jni");??
  15. }??
  16. ??
  17. static?jarray?getIntArrayFromJNI_native(JNIEnv?*env,?jclass?jcls)?{??
  18. ????LOGD("?======================?getIntArrayFromJNI");??
  19. ????//?构造一个int[]数组??
  20. ????jintArray?intArray?=?env->NewIntArray(2);??
  21. ????int?size[]={640,?960};??
  22. ????//?给int[]数组赋值??
  23. ????env->SetIntArrayRegion(intArray,?0,?2,?size);??
  24. ??
  25. ????return?intArray;??
  26. }??
  27. ??
  28. static?void?setJniPerson_native(JNIEnv?*env,?jclass?jcls,?jobject?jobj)?{??
  29. ????LOGD("?======================?setPersonToJNI");??
  30. ????jclass?jperson?=?env->GetObjectClass(jobj);??
  31. ????if?(jperson?!=?NULL)?{??
  32. ????????//?获取Person对象的age字段id??
  33. ????????jfieldID?ageFieldId?=?env->GetFieldID(jperson,?"age",?"I");??
  34. ????????//?获取Person对象的name字段id??
  35. ????????jfieldID?nameFieldId?=?env->GetFieldID(jperson,?"name",?"Ljava/lang/String;");??
  36. ??
  37. ????????//?获取Person的age成员变量??
  38. ????????jint?age?=?env->GetIntField(jobj,?ageFieldId);??
  39. ????????//?获取Person的name成员变量??
  40. ????????jstring?name?=?(jstring)env->GetObjectField(jobj,?nameFieldId);??
  41. ??
  42. ????????const?char?*c_name?=?env->GetStringUTFChars(name,?NULL);??
  43. ??
  44. ????????//?打印从Java传递过来的Person对象的age和name变量??
  45. ????????LOGD("age?=====?%d,?name?=====?%s",?age,?c_name);??
  46. ????}??
  47. ??
  48. ????//?以下是从JNI构造Java对象,并调用Java类中的成员方法,仅用作演示??
  49. ????//?获取Person对象的class??
  50. ????jclass?jstu?=?env->FindClass("com/alphagl/main/Person");??
  51. ????//?获取Person对象的构造方法的方法id??
  52. ????jmethodID?personMethodId?=?env->GetMethodID(jperson,?"",?"(ILjava/lang/String;)V");??
  53. ????//?构造一个String字符串??
  54. ????jstring?name?=?env->NewStringUTF("bill");??
  55. ??
  56. ????//?构造一个Person对象??
  57. ????jobject??jPersonObj?=?env->NewObject(jstu,?personMethodId,?30,?name);??
  58. ????//?获取Person对象的printPerson成员方法的方法id??
  59. ????jmethodID?jid?=?env->GetMethodID(jstu,?"printPerson",?"()V");??
  60. ????//?调用java的printPerson方法??
  61. ????env->CallVoidMethod(jPersonObj,?jid);??
  62. }??
  63. ??
  64. static?jobject?getJniPerson_native(JNIEnv?*env,?jclass?jcls)?{??
  65. ????LOGD("?======================?getPersonFromJNI");??
  66. ????//?获取Person对象的class??
  67. ????jclass?jstudent?=?env->FindClass("com/alphagl/main/Person");??
  68. ????//?获取Person对象的构造方法的方法id??
  69. ????jmethodID?studentMethodId?=?env->GetMethodID(jstudent,?"",?"(ILjava/lang/String;)V");??
  70. ????//?构造一个String字符串??
  71. ????jstring?name?=?env->NewStringUTF("john");??
  72. ????//?构造一个Person对象??
  73. ????jobject??jstudentObj?=?env->NewObject(jstudent,?studentMethodId,?20,?name);??
  74. ??
  75. ????return?jstudentObj;??
  76. }??
  77. ??
  78. static?JNINativeMethod?gMethods[]?=?{??
  79. ????????{"getStringFromJNI",?"()Ljava/lang/String;",?(void*)getStringFromJNI_native},??
  80. ????????{"getIntArrayFromJNI",?"()[I",?(void*)getIntArrayFromJNI_native},??
  81. ????????{"setPersonToJNI",?"(Lcom/alphagl/main/Person;)V",?(void*)setJniPerson_native},??
  82. ????????{"getPersonFromJNI",?"()Lcom/alphagl/main/Person;",?(void*)getJniPerson_native}??
  83. };??
  84. ??
  85. static?jint?registerNativeMethods(JNIEnv?*env,?const?char*?className,?JNINativeMethod?*gMethods,?int?numMethods)?{??
  86. ????jclass?jcls;??
  87. ????jcls?=?env->FindClass(className);??
  88. ????if?(jcls?==?NULL)?{??
  89. ????????return?JNI_FALSE;??
  90. ????}??
  91. ??
  92. ????if?(env->RegisterNatives(jcls,?gMethods,?numMethods)?
  93. ????????return?JNI_FALSE;??
  94. ????}??
  95. ??
  96. ????return?JNI_TRUE;??
  97. }??
  98. ??
  99. static?jint?registerNative(JNIEnv?*env)?{??
  100. ????return?registerNativeMethods(env,?CLASSNAME,?gMethods,?sizeof(gMethods)?/?sizeof(gMethods[0]));??
  101. }??
  102. ??
  103. JNIEXPORT?jint?JNICALL?JNI_OnLoad(JavaVM*?vm,?void*?reserved)?{??
  104. ????JNIEnv?*env?=?NULL;??
  105. ????if?((vm->GetEnv((void**)&env,?JNI_VERSION_1_6))?!=?JNI_OK)?{??
  106. ????????return?JNI_ERR;??
  107. ????}??
  108. ??
  109. ????if?(!registerNative(env))?{??
  110. ????????return?JNI_ERR;??
  111. ????}??
  112. ??
  113. ????return?JNI_VERSION_1_6;??
  114. }??

  最后的执行结果为:

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

  两种实现方式比较:

  (1)动态注册中,可以不用声明形如Java_packageName_className_methodName格式的方法。

  (2)动态注册中,要重写JNI_OnLoad方法,手动调用RegisterNatives来注册本地方法,以及声明在JNINativeMethod中。

  (3)动态注册,明显这种方式更灵活,但对代码要求更高,推荐使用这种方式。

  以上示例代码都已上传Github,有需要的可以自行查看。

  https://github.com/cnsuperx/android-jni-example

  JNI调试

  如果安装了LLVM环境的话,直接将Jni Debuggable选项打开即可。环境搭建可以参考Android游戏开发实践(1)之NDK与JNI开发03。

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

  接着直接在C或C++代码中设置断点即可。

本文发布:Android开发网
本文地址:http://www.jizhuomi.com/android/game/713.html
2017年9月13日
发布:鸡皇冠现金官网hg6388|首页 分类:Android游戏开发 浏览: 评论:0