本文以调用Android Java中的Mediaplayer相关接口为例,实现C++调用Java接口的功能,但对于其他Java接口,其原理是一样的。
在C++项目中,C++与Java通信是需要通过ndk的,在项目中build.gradle文件中可以配置ndk的版本,如下:
android { compileSdk 29 ndkVersion "22.1.7171670" buildToolsVersion = "30.0.3"
目录
在Android Java中通过MediaPlayer播放音乐是通过如下步骤:
在Android Java中通过MediaPlayer播放音乐是通过如下步骤:
//音乐路径
String path = "/sdcard/test.wav";
//实例化MediaPlayer
MediaPlayer mediaPlayer = new MediaPlayer();//指定Stream类型
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);//设置音乐播放路径
mediaPlayer.setDataSource(path);
那C++中如何访问Java接口呢?
由于是Native项目,所以应用启动后会调用android_main函数,可以在项目中,在这个函数中,一般会对初始化JVM.
void android_main(struct android_app* app) { JNIEnv* Env; //Env赋值 app->activity->vm->AttachCurrentThread(&Env, nullptr); app->userData = &appState; app->onAppCmd = app_handle_cmd; jobject activityInstance = app->activity->clazz; JavaVM* jvm = app->activity->vm; ...
在android_main中可以得到Env, 在C++中Java的函数都是通过Env去调用的。
上述知道了Java是如何播放音乐的,那么我们就要相应的在C++中去实现上面这些,在C++中实现如下:
实例化MediaPlayer
//获取MediaPlayer类,需要类的(包名+类名)这个路径 jclass MediaPlayer = Env->FindClass("android/media/MediaPlayer"); //无参构造函数,其方法都是<init>,无返回值,所以此处是这样写的,具有通用性 jmethodID gouzao = Env->GetMethodID(MediaPlayer, "<init>", "()V"); __android_log_print(ANDROID_LOG_DEBUG, "test", "gouzao"); //实例化MediaPlayer类 jobject jobj = Env->NewObject(MediaPlayer, gouzao);
执行setAudioStreamType方法
//获取MediaPlayer中的setAudioStreamType方法ID jmethodID setAudioStreamType = Env->GetMethodID(MediaPlayer, "setAudioStreamType", "(I)V"); //设置stream类型为STREAM_MUSIC jint streamType = 3; //调用MediaPlayer.setAudioStreamType Env->CallVoidMethod(jobj, setAudioStreamType, streamType); __android_log_print(ANDROID_LOG_DEBUG, "test", "setAudioStreamType");
执行setDataSource方法
//获取MediaPlayer中的setDataSource方法ID jmethodID setDataSource = Env->GetMethodID(MediaPlayer, "setDataSource", "(Ljava/lang/String;)V"); //设置文件播放路径 jstring path = Env->NewStringUTF("/sdcard/test.wav"); //调用MediaPlayer.setDataSource Env->CallVoidMethod(jobj, setDataSource, path); __android_log_print(ANDROID_LOG_DEBUG, "test", "setDataSource");
执行prepare方法
//获取MediaPlayer中的prepare方法ID jmethodID prepare = Env->GetMethodID(MediaPlayer, "prepare", "()V"); //调用MediaPlayer.prepare Env->CallVoidMethod(jobj, prepare); __android_log_print(ANDROID_LOG_DEBUG, "test", "prepare");
执行start方法
//获取MediaPlayer中的start方法ID jmethodID start = Env->GetMethodID(MediaPlayer, "start", "()V"); //调用MediaPlayer.start Env->CallVoidMethod(jobj, start); __android_log_print(ANDROID_LOG_DEBUG, "test", "start");
AndroidManifest中添加存储相关权限,然后编译出APK,打开APK的访问存储空间权限,并在sdcard下放置音乐test.wav, 再打开APK,音乐播放成功。
通过这段代码,我们可以知道,C++获取Android Java接口不是很复杂,需要像实现Java一样去依次调用相应函数。那相应的如果要调用其他Java 接口,其实其实现原理都是一样的。
以下对一些函数做一些了解
1. FindClass
jclass MediaPlayer = Env->FindClass("android/media/MediaPlayer")
jclass FindClass(const char* name) { return functions->FindClass(this, name); }
入参为需要查找的类名,但需要包名+类名这两个的组合。
2.GetMethodID
jmethodID gouzao = Env->GetMethodID(MediaPlayer, "<init>", "()V");
jmethodID setAudioStreamType = Env->GetMethodID(MediaPlayer, "setAudioStreamType", "(I)V");
jmethodID prepare = Env->GetMethodID(MediaPlayer, "prepare", "()V");
jmethodID GetMethodID(jclass clazz, const char* name, const char* sig) { return functions->GetMethodID(this, clazz, name, sig); }
入参第一个为1中获取到的Class类名,入参第二个为要获取的方法名,此方法必须在入参1中实现,且类型是public类型,入参3为入参2的参数列表类型。
重点介绍一些最后一位的含义:
可参考lidongxiu0714的介绍,他讲的很完善了
Android Jni GetMethodID中函数标识的简单解释_lidongxiu0714的博客-CSDN博客
3.NewObject
jobject jobj = Env->NewObject(MediaPlayer, gouzao);
jobject NewObject(jclass clazz, jmethodID methodID, ...) { va_list args; va_start(args, methodID); jobject result = functions->NewObjectV(this, clazz, methodID, args); va_end(args); return result; }
此函数用于实例化Java对象,入参1为Class类名,入参2为构造函数ID,后面的入参为入参2构造函数的函数入参。注意此处是构造函数ID,如果是其他函数ID,会报错。
4.CallVoidMethod
Env->CallVoidMethod(jobj, setDataSource, path);
Env->CallVoidMethod(jobj, start);
void CallVoidMethod(jobject obj, jmethodID methodID, ...) { va_list args; va_start(args, methodID); functions->CallVoidMethodV(this, obj, methodID, args); va_end(args); }
此方法入参1位实例化后的Class类名,也就是上述3中的jobject , 入参2为方法ID,也就是上述2中的jmethodID,后面的入参为jmethodID的入参列表。