首先我们有个需求, 就是能够在 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 文件内容了, 奇奇怪怪的折腾。
未完待续, 后面会继续分析。
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.