Android webview 视频没有达到 readyState=4
Android webview video does not reach readyState=4
我正在 Android 网络视图中自动播放视频。
这给互联网连接速度慢带来了问题。我收到 ANR 弹出窗口(应用程序无响应)
我认为这是因为视频在加载数据之前收到 video.play()。 5 秒后出现 ANR 弹出窗口。
我想解决这个问题的方法是等待视频完全加载,然后再调用 video.play()
问题是 Webview 中的视频没有收到 video.readystate == 4。它永远不会达到就绪状态 2。即使在正常的互联网连接下也是如此。
在我的 Chrome 浏览器中,它确实接收到就绪状态 4。那么这是为什么呢?为什么 Webview 中的视频没有收到 readystate 4?
下面是我的代码:
<!DOCTYPE html>
<html>
<head>
<title>Video</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
</head>
<body>
<div id="item" class="item">
<video preload="auto" autobuffer width="1024" height="576" id="video">
<source id="videomp4" src="http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4" type='video/mp4' />
</video>
<script>
var interval;
$(document).ready(function () {
interval = setInterval('playVideoWhenReady()', 1000);
/*
// this doesn't work either
video.addEventListener('loadeddata', function() {
// Video is loaded and can be played
console.log("video play is triggered");
video.play();
}, false);
*/
});
function playVideoWhenReady() {
var video = document.getElementById('video');
console.log("!!!!! curstate: " + video.readyState);
if ( video.readyState == 4 ) { // this is never true in Android Webview
video.play();
clearInterval(interval);
}
}
</script>
</div>
</body>
</html>
主要活动:
package net.eyefinder.www.testvideo;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
WebView webView = (WebView) findViewById(R.id.activity_main_webview);
final WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setMediaPlaybackRequiresUserGesture(false);
settings.setUseWideViewPort(true);
webView.setWebChromeClient(new WebChromeClient());
webView.loadUrl("file:///android_asset/video.html");
}
}
这是清单文件:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.eyefinder.www.testvideo">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:hardwareAccelerated="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-permission android:name="android.permission.INTERNET" />
</manifest>
则日志输出为:
10-31 10:55:37.369 2181-2195/net.myapp.www.testvideo D/MediaPlayerPrivateAndroid: load url=http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4
10-31 10:55:37.389 2181-2181/net.myapp.www.testvideo D/TilesManager: Starting TG #0, 0x67360d10
10-31 10:55:37.399 2181-2181/net.myapp.www.testvideo D/TilesManager: new EGLContext from framework: 63c6cd40
10-31 10:55:37.399 2181-2181/net.myapp.www.testvideo D/GLWebViewState: Reinit shader
10-31 10:55:37.429 2181-2181/net.myapp.www.testvideo D/GLWebViewState: Reinit transferQueue
10-31 10:55:37.449 2181-2181/net.myapp.www.testvideo D/VideoLayerManager: Reinit GLResource for VideoLayer
10-31 10:55:38.439 2181-2181/net.myapp.www.testvideo I/Web Console: !!!!! curstate: 2 at ../video.html:23
10-31 10:55:39.379 2181-2181/net.myapp.www.testvideo I/Web Console: !!!!! curstate: 2 at ../video.html:23
10-31 10:55:40.379 2181-2181/net.myapp.www.testvideo I/Web Console: !!!!! curstate: 2 at ../video.html:23
它一直挂在 curstate 2
以下配置对我有用:
WebView webView = (WebView) findViewById(R.id.webview);
final WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setMediaPlaybackRequiresUserGesture(false);
webView.setWebChromeClient(new WebChromeClient());
webView.loadUrl("file:///android_asset/video.html");
在src/main/assets/video.html
中:
<!DOCTYPE html>
<html>
<head>
<title>Video</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<video preload="auto" autoplay="autoplay" autobuffer width="1024" height="576" id="video" />
<script>
var video = document.getElementById('video');
video.src = 'http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4';
video.load();
video.addEventListener('loadeddata', function() {
video.play();
}, false);
</script>
</body>
</html>
并在清单中获得互联网许可:
<uses-permission android:name="android.permission.INTERNET" />
经过一番研究,我得出的结论是,在 Android Webview 中完全加载视频之前,没有合适的方法来等待它。
当然,还有一些解决方案,例如:
<video preload="auto" autobuffer width="1024" height="576" id="video"></video>
<script>
var interval;
$(function () {
var video = document.getElementById('video');
var req = new XMLHttpRequest();
req.open('GET', 'http://your-video-url.mp4', true);
req.responseType = 'blob';
req.onload = function() {
// Onload is triggered even on 404
// so we need to check the status code
if (this.status === 200) {
var videoBlob = req.response;
var vid = (window.URL || window.webkitURL || window || {}).createObjectURL(videoBlob); // IE10+
// Video is now downloaded
// and we can set it as source on the video element
video.src = vid;
video.load();
}
}
req.onerror = function() {
// Error
}
req.send();
video.addEventListener('progress', function() {
var range = 0;
var bf = this.buffered;
var time = this.currentTime;
while(!(bf.start(range) <= time && time <= bf.end(range))) {
range += 1;
}
var loadStartPercentage = bf.start(range) / this.duration;
var loadEndPercentage = bf.end(range) / this.duration;
var loadPercentage = loadEndPercentage - loadStartPercentage;
console.log("time: " + time);
console.log("loadStartPercentage: " + loadStartPercentage);
console.log("loadEndPercentage: " + loadEndPercentage);
console.log("loadPercentage: " + loadPercentage);
if ( loadPercentage == 1 ) {
video.play();
}
});
});
但此解决方案不适用于 Android 4.2(关于 Why can't my Android device play HTML5 video loaded as Blob via XHR?)。
所以我在 Android 中实现了一个视频下载器并将本地文件 URL 解析为 JavaScript。现在无需等待视频加载。
我的连接速度慢的问题现已解决!
我正在 Android 网络视图中自动播放视频。 这给互联网连接速度慢带来了问题。我收到 ANR 弹出窗口(应用程序无响应)
我认为这是因为视频在加载数据之前收到 video.play()。 5 秒后出现 ANR 弹出窗口。
我想解决这个问题的方法是等待视频完全加载,然后再调用 video.play()
问题是 Webview 中的视频没有收到 video.readystate == 4。它永远不会达到就绪状态 2。即使在正常的互联网连接下也是如此。 在我的 Chrome 浏览器中,它确实接收到就绪状态 4。那么这是为什么呢?为什么 Webview 中的视频没有收到 readystate 4?
下面是我的代码:
<!DOCTYPE html>
<html>
<head>
<title>Video</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
</head>
<body>
<div id="item" class="item">
<video preload="auto" autobuffer width="1024" height="576" id="video">
<source id="videomp4" src="http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4" type='video/mp4' />
</video>
<script>
var interval;
$(document).ready(function () {
interval = setInterval('playVideoWhenReady()', 1000);
/*
// this doesn't work either
video.addEventListener('loadeddata', function() {
// Video is loaded and can be played
console.log("video play is triggered");
video.play();
}, false);
*/
});
function playVideoWhenReady() {
var video = document.getElementById('video');
console.log("!!!!! curstate: " + video.readyState);
if ( video.readyState == 4 ) { // this is never true in Android Webview
video.play();
clearInterval(interval);
}
}
</script>
</div>
</body>
</html>
主要活动:
package net.eyefinder.www.testvideo;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
WebView webView = (WebView) findViewById(R.id.activity_main_webview);
final WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setMediaPlaybackRequiresUserGesture(false);
settings.setUseWideViewPort(true);
webView.setWebChromeClient(new WebChromeClient());
webView.loadUrl("file:///android_asset/video.html");
}
}
这是清单文件:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.eyefinder.www.testvideo">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:hardwareAccelerated="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-permission android:name="android.permission.INTERNET" />
</manifest>
则日志输出为:
10-31 10:55:37.369 2181-2195/net.myapp.www.testvideo D/MediaPlayerPrivateAndroid: load url=http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4
10-31 10:55:37.389 2181-2181/net.myapp.www.testvideo D/TilesManager: Starting TG #0, 0x67360d10
10-31 10:55:37.399 2181-2181/net.myapp.www.testvideo D/TilesManager: new EGLContext from framework: 63c6cd40
10-31 10:55:37.399 2181-2181/net.myapp.www.testvideo D/GLWebViewState: Reinit shader
10-31 10:55:37.429 2181-2181/net.myapp.www.testvideo D/GLWebViewState: Reinit transferQueue
10-31 10:55:37.449 2181-2181/net.myapp.www.testvideo D/VideoLayerManager: Reinit GLResource for VideoLayer
10-31 10:55:38.439 2181-2181/net.myapp.www.testvideo I/Web Console: !!!!! curstate: 2 at ../video.html:23
10-31 10:55:39.379 2181-2181/net.myapp.www.testvideo I/Web Console: !!!!! curstate: 2 at ../video.html:23
10-31 10:55:40.379 2181-2181/net.myapp.www.testvideo I/Web Console: !!!!! curstate: 2 at ../video.html:23
它一直挂在 curstate 2
以下配置对我有用:
WebView webView = (WebView) findViewById(R.id.webview);
final WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setMediaPlaybackRequiresUserGesture(false);
webView.setWebChromeClient(new WebChromeClient());
webView.loadUrl("file:///android_asset/video.html");
在src/main/assets/video.html
中:
<!DOCTYPE html>
<html>
<head>
<title>Video</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<video preload="auto" autoplay="autoplay" autobuffer width="1024" height="576" id="video" />
<script>
var video = document.getElementById('video');
video.src = 'http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4';
video.load();
video.addEventListener('loadeddata', function() {
video.play();
}, false);
</script>
</body>
</html>
并在清单中获得互联网许可:
<uses-permission android:name="android.permission.INTERNET" />
经过一番研究,我得出的结论是,在 Android Webview 中完全加载视频之前,没有合适的方法来等待它。
当然,还有一些解决方案,例如:
<video preload="auto" autobuffer width="1024" height="576" id="video"></video>
<script>
var interval;
$(function () {
var video = document.getElementById('video');
var req = new XMLHttpRequest();
req.open('GET', 'http://your-video-url.mp4', true);
req.responseType = 'blob';
req.onload = function() {
// Onload is triggered even on 404
// so we need to check the status code
if (this.status === 200) {
var videoBlob = req.response;
var vid = (window.URL || window.webkitURL || window || {}).createObjectURL(videoBlob); // IE10+
// Video is now downloaded
// and we can set it as source on the video element
video.src = vid;
video.load();
}
}
req.onerror = function() {
// Error
}
req.send();
video.addEventListener('progress', function() {
var range = 0;
var bf = this.buffered;
var time = this.currentTime;
while(!(bf.start(range) <= time && time <= bf.end(range))) {
range += 1;
}
var loadStartPercentage = bf.start(range) / this.duration;
var loadEndPercentage = bf.end(range) / this.duration;
var loadPercentage = loadEndPercentage - loadStartPercentage;
console.log("time: " + time);
console.log("loadStartPercentage: " + loadStartPercentage);
console.log("loadEndPercentage: " + loadEndPercentage);
console.log("loadPercentage: " + loadPercentage);
if ( loadPercentage == 1 ) {
video.play();
}
});
});
但此解决方案不适用于 Android 4.2(关于 Why can't my Android device play HTML5 video loaded as Blob via XHR?)。
所以我在 Android 中实现了一个视频下载器并将本地文件 URL 解析为 JavaScript。现在无需等待视频加载。
我的连接速度慢的问题现已解决!