首先我们有个需求, 就是能够在 WebView 中适配控制 media 的播放控制, 但是查看 api 发现,webview 根本没有暴露 mediasssion 这方面的 api. 怎么办? 只能去看看源码。
通过搜索, 发现 chromium 项目中有 MediaSession 这个接口,然后找到了这个类的实现 org.chromium.content.browser.MediaSessionImpl
,发现这个类有一个静态方法。
@CalledByNative private static MediaSessionImpl create(long nativeMediaSession), 这个时候肯定是 native 调用了 java 的代码。
那么继续搜索 MediaSessionImpl_create 在 chromium 源码中, 继续搜索
➜ src git:(123.0.6312.121) ✗ grep -rn "MediaSessionImpl" --include="*.cc" |grep create
content/browser/media/session/media_session_android.cc:38: Java_MediaSessionImpl_create(env, reinterpret_cast<intptr_t>(this));
继续往下面看
MediaSessionAndroid::MediaSessionAndroid(MediaSessionImpl* session)
: media_session_(session) {
DCHECK(media_session_);
JNIEnv* env = base::android::AttachCurrentThread();
ScopedJavaLocalRef<jobject> j_media_session =
Java_MediaSessionImpl_create(env, reinterpret_cast<intptr_t>(this));
j_media_session_ = JavaObjectWeakGlobalRef(env, j_media_session);
WebContentsImpl* contents =
static_cast<WebContentsImpl*>(media_session_->web_contents());
if (contents) {
web_contents_android_ = contents->GetWebContentsAndroid();
DCHECK(web_contents_android_);
web_contents_android_->SetMediaSession(j_media_session);
}
session->AddObserver(observer_receiver_.BindNewPipeAndPassRemote());
}
发现了 mediaSession 被设置到 WebContents, 通过搜索发现 WebContents 是一个接口, 继续找到该类的子类 WebContentsImpl.java , 发现里面有一行代码
@CalledByNative
private final void setMediaSession(MediaSessionImpl mediaSession) {
mMediaSession = mediaSession;
}
, 哈哈惊喜过度, 对象 mediaSession 竟然还在。 那还不简单, 直接通过反射去拿这个对象?
这里直接反射的过程简单说一下, 首先我们通过 WebView 的 classloader 拿到 WebView 的 Class 对象 ,再反射拿到 WebView 类里面的 mProvider 对象, 这个 mProdiver 其实就是 WebViewChromium.java, 在通过 WebViewChromium.java 对象里面的 AwContents 拿到对象 WebContens ,找到 WebContens 对象后, 就能反射拿到 mediaSession 了, 这个里面有很多坑,反射通过字段很多拿不到,名字和类名被混淆了,所以需要写很多 hack 代码。
这个时候直接反射出了 mMediaSession , 但是发现这个字段怎么都不存在, 惊呆了我的下巴, 继续测试,发现 setMediaSession 这个方法存在的。 这是怎么回事儿了? 二进制和源代码不一致? 这个时候不得不使用一些反编译工具了, 反编译出 MediaSessionImpl.java 文件,发现 mMediaSession 字段不存在, 并且 mMediaSession = mediaSession; 代码也消失了。
这这这这这? 心里一万个草泥马。 经过多次测试,发现这段代码被编译器忽略掉了。
认真分析 media_session_android.cc , 发现 setMediaSession 这个方法肯定是调用了的。 这个时候又去查询了一些资料,发现 native 方法的调用的原理是反射, 这个时候就只能去修改 classloader 里面的 class 文件内容了, 奇奇怪怪的折腾。
未完待续, 后面会继续分析。
1
anbus 156 天前
这么抽象的吗
|
2
z836454898 156 天前
hack chromium 内核这是在给后人留坑啊,后人一更新 app 里的 chromium 程序就崩了,除非你的 app 像小程序一样有深度定制内核需求
|
3
jeesk OP @z836454898 自己处理好兼容性就行了.
|
5
jeesk OP @z836454898 我们自己定制了内核. 不过有些场景不需要定制内核 , 主要是为了减少体积.
|
6
HtPM 156 天前
“这个时候直接反射出了 mMediaSession , 但是发现这个字段怎么都不存在, 惊呆了我的下巴, 继续测试,发现 setMediaSession 这个方法存在的”
这句话什么意思?你是说反射出了 MediaSession 的这个对象,但是字段 mMediaSession 不存在?这正常啊,如果做了代码混淆,mMediaSession 不就不存在了?变成其它名称了 |
7
HtPM 156 天前
“认真分析 media_session_android.cc , 发现 setMediaSession 这个方法肯定是调用了的。 这个时候又去查询了一些资料,发现 native 方法的调用的原理是反射”
还有这句话是什么意思?你是说 setMediaSession 这个 Java 函数是被 native 方法反射调用的? |