Android JNI调用

JNI(Java Native InterfaceJava本地接口),是用于实现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 
#define JNI_TRUE 1

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];
for(int i = 0; i < 5; i++) {
   tmp[i] = i;
}
env->SetIntArrayRegion(iarr, 0, 5, tmp);

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"};
jobjectArray sa= (jobjectArray)env->NewObjectArray(5,env->FindClass("java/lang/String"),env->NewStringUTF(""));
for(int i=0;i<5;i++) {
    env->SetObjectArrayElement(sa,i,env->NewStringUTF(data[i]));
}

Class jclass Ljava/lang/Class; typedef jobject jclass;
Object jobject Ljava/lang/Object;

typedef _jobject *jobject; 
typedef _jclass *jclass;

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)

JNI Functions

JNIEnv Class 

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

JNI 提示

Android JNI原理分析

java_net_NetworkInterface.cpp

ndk-samples(github)

libandroidjni(github)

Android NDK学习--jni访问java层方法

Android Stuido Ndk-Jni 开发(五):Jni回调java静态方法和非静态方法

如何操作jni-String 

Android NDK JNI 入门笔记-day03-引用数据类型

Android NDK开发之JNI基础

JNI的某些数组和字符串类型转换

Developer Perspective: UE4 Logging and Console Commands for Mobile VR

UE:UPL 与 JNI 调用的最佳实践 

原文地址:https://www.cnblogs.com/kekec/p/12595607.html