概述
本文讲述的是,通过 WebView加载带有视频的网页,实现点击网页上的全屏按钮,实现视频横屏、全屏播放的功能。具体代码可见Demo ,展现的有腾讯视频,Bilibili视频,土豆视频。
需求
最近有个需求,加载一个腾讯视频的网页,用户点击网页上的全屏按钮,就能实现设备横屏,全屏播放。
前提
设置属性
在 AndroidManifest 声明网络权限
<uses-permission android:name="android.permission.INTERNET"/>
屏幕旋转的时候,默认会重新走 Activity 的生命周期,所以要在所在的 Activity,声明当下面情况发生的时候,不重新走生命周期。
android:configChanges="orientation|keyboard|keyboardHidden|screenSize"
声明 WebView settings 属性
BaseWebView.java (继承 WebView 的父类)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37mSettings = this.getSettings(); //这里的 this 是指代 WebView
if (null != mSettings) {
// 网页内容的宽度是否可大于WebView控件的宽度
mSettings.setLoadWithOverviewMode(false);
// 保存表单数据
mSettings.setSaveFormData(true);
// 是否应该支持使用其屏幕缩放控件和手势缩放
mSettings.setSupportZoom(true);
mSettings.setBuiltInZoomControls(true);
mSettings.setDisplayZoomControls(false);
// 启动应用缓存
mSettings.setAppCacheEnabled(true);
// 设置缓存模式
mSettings.setCacheMode(WebSettings.LOAD_DEFAULT);
// setDefaultZoom api19被弃用
// 设置此属性,可任意比例缩放。
mSettings.setUseWideViewPort(true);
// 缩放比例 1
this.setInitialScale(1);
// 告诉WebView启用JavaScript执行。默认的是false。
mSettings.setJavaScriptEnabled(true);
// 页面加载好以后,再放开图片
//mSettings.setBlockNetworkImage(false);
// 使用localStorage则必须打开
mSettings.setDomStorageEnabled(true);
// 排版适应屏幕
mSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
// WebView是否支持多个窗口。
mSettings.setSupportMultipleWindows(true);
// webview从5.0开始默认不允许混合模式,https中不能加载http资源,需要设置开启。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
// 设置字体默认缩放大小(改变网页字体大小,setTextSize api14被弃用)
//mSettings.setTextZoom(100);
}
特殊情况处理
在尝试优酷视频加载的时候,
开始返回 url 是正常类型的
1 | http://m.youku.com/video/id_XMjY2OTk2MDMwOA==.html?from=s1.8-1-1.2&spm=a2h0k.8191407.0.0 |
但是有时候返回的url是这样的,包含对应的 APP packegeName 的action。这时候如果 WebView 没做处理,就会导致页面加载不出来的情况。
1 | intent://play?vid=XMjY2OTk2MDMwOA==&refer=&tuid=&ua=Mozilla%2F5.0%20(Linux%3B%20Android%204.4.2%3B%20LG-D802%20Build%2FKOT49I.D80220c)%20AppleWebKit%2F537.36%20(KHTML%2C%20like%20Gecko)%20Version%2F4.0%20Chrome%2F30.0.0.0%20Mobile%20Safari%2F537.36&source=exclusive-pageload&cookieid=1493731828915oxvHJU|seP6GQ#Intent;scheme=youku;package=com.youku.phone;end; |
解决办法:
在 shouldOverrideUrlLoading 的时候,找出这种类型,判断是非当前有安装 action对应 APP,有的话用对应的 APP 打开,没有的话提取 url 加载。
1 | private class CustomWebClient extends WebViewClient { |
销毁须知
WebView 很容易引发内存泄漏,解决方法有:
简单的方式:WebView 所在的 Activity 声明在另外一个进程里面,
android:process=":VideWebView"
并在 Activity onDestory的时候,杀死这个进程
android.os.Process.killProcess(android.os.Process.myPid());
常规的方式:不新开进程,在 Activity onDestroy 的时候,手动调 webView.onDestory() 销毁方法。不然,除了会发生内存泄漏外,播放视频的时候,会因为 WebView 没有销毁,即使 back 键返回 Activity,还会出现音频持续播放的尴尬情况哈。
VideoWebView.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17/**
* 自定义 WebView 手动销毁方法
*/
public void onDestory() {
super.onDestory();
mOnVideoWebViewListener = null;
mCallback = null;
stopLoading();
clearCache(true);
clearFormData();
clearMatches();
clearHistory();
clearDisappearingChildren();
clearAnimation();
removeAllViews();
destroy();
}好了,接下来看看我们应该如何解决全屏的问题,先从官方Webview文档找找答案吧,
官方解决方法
实现方法
打开硬件加速
此处为 WebView 所在的 Activity 开启硬件加速。在 Mainifest 声明即可。
android:hardwareAccelerated="true"
setWebChromeClient
并且重写 onShowCustomView(点击全屏按钮时候回调) 和 onHideCustomView(再次点击退出全屏的时候回调)
VideoWebView.java
this.setWebChromeClient(new CustomWebChromClient());
1 | /** |
VideoWebViewActivity.java
1 | /** |
布局
webview 撑满界面,同时还有个 FrameLayout撑满。
(因为最外层为 LinearLayout,第一个 Child(WebView) 已经撑满了,所以 FrameLayout在默认加载的时候并不会出现在界面上)
1 | <?xml version="1.0" encoding="utf-8"?> |
在 Activity 处理全屏逻辑
进入横屏:设置屏幕为横屏,隐藏 WebView,显示 mContainerLayout(就是刚刚布局里面撑满的 FrameLayout ),并把 WebView 内部全屏触发时候回调的 view add 当 FrameLayout上。
退出全屏:设置屏幕为竖屏,显示 WebView,隐藏 mContainerLayout。
VideoWebViewActivity.java
1 | mVideoWebView.setOnVideoWebViewListener(new VideoWebViewListenerImp()); |
1 | /** |
1 | /** |
结论
实际使用中,这个官方方法对于腾讯视频是没有效果的,因为点击全屏按钮的时候,根本不会回调 onShowCustomView 和 onHideCustomView。
对于土豆、优酷的视频,我用多部 Android 5.0,6.0的测试机,验证是有效的。但是我用的 4.4.2 的 LG G2 测试的时候,就是没效果,不会回调。
这种方式不是太稳定,同个网站,不同设备,因为 Android 版本差异,WebView 内核版本差异,有些有效,有些无效..
JS 注入
实现方法
在页面加载完成的时候,注入 JS。这样在点击网页全屏按钮的时候,就能回调我们本地的方法了。
onPageFinished 注入 JS
VideoWebView.java
1 | this.setWebViewClient(new CustomWebClient()); |
Tag 的获取方式
TagUtils.java
1 | public class TagUtils { |
这里全屏网页指的是,整个页面都是全屏的网页:
普通网页指的是:
以普通的网页为例,说下tag应该这样获取,
用 chrome 打开网页,右键打开 inspect ,选中全屏按钮,就可以定位到对于的 tag。
本地方法定义
打开 JS 调用许可
VideoWebView.java
1
this.addJavascriptInterface(new VideoJsObject(), "onClickFullScreenBtn");
接口定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17private class VideoJsObject {
/**
* 从线程回调,要更新 UI 操作,要 post 到主线程
*/
@JavascriptInterface
public void fullscreen() {
if (null != mOnVideoWebViewListener) {
if (mFullScreenMode) {
mOnVideoWebViewListener.onJsExitFullScreenMode();
} else {
mOnVideoWebViewListener.onJsEnterFullSceenMode();
}
mFullScreenMode = !mFullScreenMode; //重置全屏状态
}
}
}在 Activity 处设置监听,处理横竖屏逻辑。
1
2
3
4
5
6
7
8
9
10
11
12
13
14class VideoWebViewListenerImp implements VideoWebView.OnVideoWebViewListener {
@Override
public void onJsEnterFullSceenMode() {
setLandscape();
}
@Override
public void onJsExitFullScreenMode() {
setPortrait();
}
//.....
}
结论
在使用中,该方法对于不同机型,不同Android版本是适用的,如果出现偶尔回调实效的情况的同学,可以尝试在加载网页的不同进度阶段 onProgressChanged 尝试注入。终于解决腾讯视频加载问题,休息一下,就到这里哈。
后续问题
白屏
原本还是可以的,突然间用 WebView 打开网页就白屏了,难道是 URL 对应的页面改了?
我用浏览器,打开发现是这样的显示:NET::ERR_CERT_COMMON_NAME_INVALID
细想了下,所在环境的网络是发生了变化,现在变成了能科学上网了。所以 SSL 认证是时候发生错误,认定为不安全,WebView 处理的是终止继续访问,所以出现了白屏。
这样的体验肯定是不好的,遇到这种情况我们可不可以监听得到呢?答案是可以的。
private boolean mIgnoreSslError; //是否忽略ssl证书错误
1 | private class CustomWebClient extends WebViewClient { |
可以使用 handler.proceed() 忽略 SSL 错误,让 WebView 继续加载这个网页,但是强制加载不一定成功,而且这种加载方式是不安全的,GooglePlay 上架很可能是不能通过安全验证。另外一种处理方法是,让 WebView 执行原本逻辑(停止加载),此时显示自定义的提示界面。
相关参考
[1]WebView实现全屏播放的一种方法
[2]WebView中的视频全屏4种方法,特别是腾讯视频,真正解决全屏问题
[3]android内存优化之webview
[4]Webview avoid security alert from google play upon implementation of onReceivedSslError