1. WebRTC JNI接口文件生成
Webrtc 中,Java API是C++层的一层接口包装,但是没有像Android中那样,可以直接对应的找到Java层类
对应的C++层的JNI接口文件。
1. Webrtc中Java类中的注解
在Webrtc中,Java层使用注解的方式来标记需要进行JNI接口生成的头文件和函数选项:
用到的注解:
- @JNINamespace(“webrtc::jni”) 标记生成的头文件中的函数声明位于哪个命名空间,
这个表示生成的函数形式为
namespace webrtc {
namespace jni {
///函数...
}
}
- @NativeClassQualifiedName(“CPPClass::InnerClass”) 制定要调用的目标对象的方法,这个表示要调用的是CPPClass::InnerClass的本地方法(CPPClass指的是对应的包含生成的.h文件的.cpp/.cc文件中的类 CPPclass)
没有指定NativeClassQualifiedName的时候,调用的就是include 了生成的头文件的源文件的同名方法实现;
@NativeClassQualifiedName("CPPClass::InnerClass")
private native double nativeMethodOtherP0(long nativePtr);
对应调用c++层的
JNI_GENERATOR_EXPORT jdouble
Java_org_chromium_example_jni_1generator_SampleForTests_nativeMethodOtherP0(JNIEnv*
env, jobject jcaller,
jlong nativePtr) {
//指定调用用CPPClass::InnerClass中的方法
CPPClass::InnerClass* native =
reinterpret_cast<CPPClass::InnerClass*>(nativePtr);
CHECK_NATIVE_PTR(env, jcaller, native, "MethodOtherP0", 0);
//调用方法
return native->MethodOtherP0(env, base::android::JavaParamRef<jobject>(env,
jcaller));
}
- @CalledByNative(“InnerClass”) 指定该标记对象是由native向Java层调用的,参数告诉底层该方法属于哪一个类中的方法(底层需要获取Java层的包名路径来调用该方法)。 比如这个表示底层应该调用的是java层的InnerClass中的方法。
2. JNI头文件生成和代码跟踪方法
上面讲的Java层的注解只是一个标记,并不是用
Java的注解处理器
来进行处理的,而是使用Python脚本来解析和生成文件,类,函数声明和函数实现的。
-
JNI接口的头文件生成是使用文件:
src\base\android\jni_generator\jni_generator.py
来进行解析和生成的。 -
生成的文件为 “java_name” + _jni.h
比如Java文件为AudioTest
, 那么如果有JNINamespace注解,那就会生成一个AudioTest_jni.h
文件 -
调用跟踪方法:
比如:
- 1.java层:
@org.webrtc.JNINamespace("webrtc::jni") //首先有这个注解,命名空间为webrtc::jni
public class AudioTrack extends org.webrtc.MediaStreamTrack {
public AudioTrack(long nativeTrack) {
super(nativeTrack);
}
...
public void setVolume(double volume) {
nativeSetVolume(super.nativeTrack, volume);
}
private static native void nativeSetVolume(long track, double volume); //找这个native方法
}
- 2.生成的对应头文件为"AudioTrack_jni.h", 那么就找那个文件include了AudioTrack_jni.h:
这个只生成头文件,源文件需要自己实现,我们只需要找到对应的源文件就可以知道调用的对象是那个函数;
//src\sdk\android\src\jni\pc\audiotrack.cc
#include "api/mediastreaminterface.h"
#include "sdk/android/generated_peerconnection_jni/jni/AudioTrack_jni.h"
namespace webrtc {
namespace jni {
//调用的native方法就是这个
static void JNI_AudioTrack_SetVolume(JNIEnv*,
const JavaParamRef<jclass>&,
jlong j_p,
jdouble volume) {
rtc::scoped_refptr<AudioSourceInterface> source(
reinterpret_cast<AudioTrackInterface*>(j_p)->GetSource());
source->SetVolume(volume);
}
} // namespace jni
} // namespace webrtc
2. 生成规则
- 一般默认生成的头文件和Java的函数所在类的报名/类型/函数名拼接规则相匹配
- 在WebRTC中,也是同样适用,但是在没有生成的前提下,我们发现我们所调用的函数名字并不适应于上述规则,这个是因为在匹配生成的时候,也同时生成了一个简单的Native接口,作为JNI接口的转换,该转换接口就是我们在生成之前找到的源码中看到的函数接口,规则是:
JNI_ + "class_name" + "Method_name()" //注意,method匹配的方法去掉了native前缀的大驼峰形式;
比如:
package org.chromium.example.jni_generator;
@JNINamespace("base::android")
class SampleForTests {
private native void nativeSetNonPODDatatype(Rect rect);
}
会生成为
namespace base {
namespace android {
//转换的简单匹配接口,我们在生成之前看到的接口就是这个,原文件中会对这个接口进行实现;
static void JNI_SampleForTests_SetNonPODDatatype(JNIEnv* env, const
base::android::JavaParamRef<jobject>& jcaller,
const base::android::JavaParamRef<jobject>& rect);
//这个是生成的JNI规则匹配接口
JNI_GENERATOR_EXPORT void
Java_org_chromium_example_jni_1generator_SampleForTests_nativeSetNonPODDatatype(JNIEnv*
env, jobject jcaller,
jobject rect) {
//接口中默认调用了生成的转换的简单匹配接口
return JNI_SampleForTests_SetNonPODDatatype(env,
base::android::JavaParamRef<jobject>(env, jcaller),
base::android::JavaParamRef<jobject>(env, rect));
}
}
}
3. 总结
代码跟踪流程可以总结如下:
- 比如Java类是AudioTest, 其中有一个native方法 nativeTest:
@JNINamespace("base::android")
public class AudioTest {
public void nativeTest();
}
- 找include了 AudioTest_jni.h 的源文件;
- 找到之后, 就可找到调用的目标函数: JNI_AudioTest_Test();