JNI方法使用--C++项目如何访问Java接口(一)

本文以调用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播放音乐是通过如下步骤:

那C++中如何访问Java接口呢?

实例化MediaPlayer

执行setAudioStreamType方法

执行setDataSource方法

执行prepare方法

执行start方法 

以下对一些函数做一些了解

1. FindClass

2.GetMethodID

3.NewObject

4.CallVoidMethod


在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的入参列表。

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值