我不明白为什么有些人认为这是无用的。事实上,当你涉足Android游戏编程时,Java->C->Java的情况非常普遍:
- Android应用程序从使用SDK编写的一些样板代码开始(使用Java)
- 启动一个新线程来运行游戏的主要逻辑,该逻辑使用C/C++编写以提高效率(并更容易访问OpenGL ES)
- C/C++代码调用一些Java方法以获取所需的功能
使用
SDK(Java) 的原因是大多数 Android APIs 只提供 Java!
NDK(C/C++) 仅提供精简的 Linux 环境(将其视为“标准 C 库”+“标准 C++ 库”+“Linux 系统库的削减版”)。例如,如果您想将应用与
AdMob(Google 广告) 或
In-app Billing(Google 支付) 集成,或者想要访问设备的内置相机,则必须从游戏逻辑(编写为 C/C ++!)调用 Java 方法。
Java -> C 部分
编写一个 Java 类。声明一个方法 native
:
$ ls -F1
classes/
jni/
src/
$ nano src/com/example/jcj/Test.java
Test.java:
package com.example.jcj;
public class Test {
public static void main(String[] args) {
doSomethingInC();
}
public static native void doSomethingInC();
public static void doSomethingInJava() {
System.out.println("Done something in Java!");
}
static {
System.loadLibrary("hello_jcj");
}
}
调用“Java编译器”(
javac
),然后是“C头文件和存根文件生成器”(
javah
):
$ javac -sourcepath src -d classes src/com/example/jcj/Test.java
$ javah -classpath classes -d jni com.example.jcj.Test
$ ls -F1 jni/
com_example_jcj_Test.h
实现 C 部分:
$ nano jni/com_example_jcj_Test.c
com_example_jcj_Test.c:
#include "com_example_jcj_Test.h"
#include <stdio.h>
JNIEXPORT void JNICALL
Java_com_example_jcj_Test_doSomethingInC(JNIEnv* env, jclass clazz)
{
printf("Done something in C!\n");
}
编译C源代码(Windows):
> REM TODO: Add this!
编译C源代码(Linux):
$ gcc -I{JAVA_HOME}/include
-shared \
-o libhello_jcj.so \
jni/com_example_jcj_Test.c
$ ls -F1
classes/
jni/
libhello_jcj.so*
src/
编译C源代码(Mac OS X):
$ gcc -I${JAVA_HOME}/include \
-shared \
-o libhello_jcj.jnilib \
jni/com_example_jcj_Test.c
$ ls -F1
classes/
jni/
libhello_jcj.jnilib*
src/
运行Java应用程序:
$ java -classpath classes com.example.jcj.Test
Done something in C!
C -> Java 部分
由于您在 Java 中启动了程序(而不是在 C / C++ 中),因此 JVM 已经创建。在第二部分(C -> Java 部分)中,您无需创建另一个 JVM,只需重用已创建的 JVM 即可。有两种方法可以实现:
- 通过使用 JNI 调用从第一部分(Java -> C 部分)提供的 Java 环境指针 (
JNIEnv*
)。
- 通过在
JNI_OnLoad()
期间保存的 Java 虚拟机指针 (JavaVM*
)。
(答案尚未完成,稍后将继续更新。如果您喜欢这篇翻译,请点赞以示鼓励。谢谢!)
参考资料: