JNI(Java Native Interface,Java本地接口),是用于实现Java与Native代码相互调用的编程框架。
Native调用Java
void ObjFunc_int_voidret(JNIEnv *env, jobject obj, const char *funcname, jint param) { jclass cls = (*env)->GetObjectClass(env, obj); jmethodID mid = (*env)->GetMethodID(env, cls, funcname, "(I)V"); if (mid != 0) { (*env)->CallVoidMethod(env, obj, mid, param); // 调用obj对象的成员函数:void funcname(int param) } } void StaticFunc_string_string_voidret(JNIEnv *env, const char *classname, const char *funcname, jstring s1, jstring s2) { jclass cls = env->FindClass(classname); if (cls != 0) { jmethodID mid = (*env)->GetStaticMethodID(cls, funcname, "(Ljava/lang/String;Ljava/lang/String;)V"); if (mid != 0) { (*env)->CallStaticVoidMethod(env, mid, s1, s2); // 调用classname类的静态成员函数:void funcname(string s1, string s2) } } }
字符串格式为(arg-type;arg-type) ret-type。arg-type之间通过分号分割。
JNI字段描述符(JavaNative Interface FieldDescriptors) 参考:JNI Types and Data Structures
Java类型 | JNI类型 | 符号 | 备注 |
void | void | V | |
boolean | jboolean | Z |
#define JNI_FALSE 0 |
byte | jbyte | B | |
char | jchar | C | |
short | jshort | S | |
int | jint | I | typedef jint jsize; |
long | jlong | J | |
float | jfloat | F | |
double | jdouble | D | |
boolean[] | jbooleanArray | [Z | |
byte[] | jbyteArray | [B | |
char[] | jcharArray | [C | |
short[] | jshortArray | [S | |
int[] | jintArray | [I |
JNIEnv *env; jintArray iarr = env->NewIntArray(5); jint tmp[5]; |
long[] | jlongArray | [J | |
float[] | jfloatArray | [F | |
double[] | jdoubleArray | [D | |
byte[][] | [[B | ||
int[][][] | [[[I | ||
String | Ljava/lang/String; | ||
String[] | [Ljava/lang/String; |
JNIEnv *env; char *data[5]= {"A", "B", "C", "D", "E"}; |
|
Class | jclass | Ljava/lang/Class; | typedef jobject jclass; |
Object | jobject | Ljava/lang/Object; |
typedef _jobject *jobject; |
Object[] | jobjectArray | [Ljava/lang/Object; | |
FileStatus | Landroid/os/FileUtils; | ||
FileStatus为FileUtils的嵌套类 | Landroid/os/FileUtils$FileStatus; |
Java调用Native
AndroidJavasrccomepicgamesue4GameActivity.java.template
public class GameActivity extends NativeActivity implements SurfaceHolder.Callback2, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, SensorEventListener, Logger.ILoggerCallback, ComponentCallbacks2 { // ... ... public native void nativeConsoleCommand(String commandString); public void cmdTest() { nativeConsoleCommand("god"); } }
LaunchAndroid.cpp
//This function is declared in the Java-defined class, GameActivity.java: "public native void nativeConsoleCommand(String commandString);" JNI_METHOD void Java_com_epicgames_ue4_GameActivity_nativeConsoleCommand(JNIEnv* jenv, jobject thiz, jstring commandString) { FString Command = FJavaHelper::FStringFromParam(jenv, commandString); if (GEngine != NULL) { // Run on game thread to avoid race condition with DeferredCommands AsyncTask(ENamedThreads::GameThread, [Command]() { GEngine->DeferredCommands.Add(Command); }); } else { FPlatformMisc::LowLevelOutputDebugStringf(TEXT("Ignoring console command (too early): %s"), *Command); } }
注:#define JNI_METHOD __attribute__ ((visibility ("default"))) extern "C"
UE4从Java传入命令行参数
GameActivity.java.template
public String AndroidThunkJava_GetExtraCommandLineArg() { String extraArg = ""; try { if (_extrasBundle != null) { extraArg = _extrasBundle.getString("extraArg"); } } catch (Exception e) { Log.debug("[JAVA] - AndroidThunkJava_GetExtraCommandLineArg exception " + e.toString()); } //Log.debug("AndroidThunkJava_GetExtraCommandLineArg returning " + extraArg); return extraArg; }
AndroidJNI.h
class FJavaWrapper { public: // ... ... static jmethodID AndroidThunkJava_GetExtraCommandLineArg; // ... ... };
AndroidJNI.cpp
void FJavaWrapper::FindClassesAndMethods(JNIEnv* Env) { // ... ... AndroidThunkJava_GetExtraCommandLineArg = FindMethod(Env, GameActivityClassID, "AndroidThunkJava_GetExtraCommandLineArg", "()Ljava/lang/String;", bIsOptional); // ... ... } jmethodID FJavaWrapper::AndroidThunkJava_GetExtraCommandLineArg; FString AndroidThunkCpp_GetExtraCommandLineArg() { FString ExtraArg; if (JNIEnv* Env = FAndroidApplication::GetJavaEnv()) { jstring extraArg = (jstring)FJavaWrapper::CallObjectMethod(Env, FJavaWrapper::GameActivityThis, FJavaWrapper::AndroidThunkJava_GetExtraCommandLineArg); if (!Env->IsSameObject(extraArg, NULL)) { const char *nativeandroidextraArgString = Env->GetStringUTFChars(extraArg, 0); ExtraArg = FString(nativeandroidextraArgString); Env->ReleaseStringUTFChars(extraArg, nativeandroidextraArgString); Env->DeleteLocalRef(extraArg); } } return ExtraArg; }
LaunchAndroid.cpp
static void InitCommandLine() { // ... ... #if !UE_BUILD_SHIPPING { extern FString AndroidThunkCpp_GetExtraCommandLineArg(); FString ExtraArg = AndroidThunkCpp_GetExtraCommandLineArg(); if (!ExtraArg.IsEmpty()) { FCommandLine::Append(TEXT(" ")); FCommandLine::Append(*ExtraArg); FPlatformMisc::LowLevelOutputDebugStringf(TEXT("Extra Command Line Arg: %s"), *ExtraArg); UE_LOG(LogAndroid, Log, TEXT("Extra Command Line Arg: %"), *ExtraArg); } } #endif // ... ... }
带命令行参数来启动游戏
adb.exe shell am start -e extraArg '-NoVerifyGC' -n com.tencent.mf.test1/com.epicgames.ue4.GameActivity
adb.exe shell am start -e extraArg '-messaging -TcpMessagingConnect=10.168.0.95:7777 -SessionOwner=nicochen -SessionName=test1' -n com.tencent.mf.test1/com.epicgames.ue4.GameActivity
扩展阅读:
Java Programming Tutorial:Java Native Interface (JNI)
Java Native Interface Specification
https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/jniTOC.html
https://docs.oracle.com/javase/8/docs/api/java/net/NetworkInterface.html
https://docs.oracle.com/javase/9/docs/specs/jni/index.html
https://developer.android.com/reference/classes
Android Stuido Ndk-Jni 开发(五):Jni回调java静态方法和非静态方法
Android NDK JNI 入门笔记-day03-引用数据类型
Developer Perspective: UE4 Logging and Console Commands for Mobile VR