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();