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。现在无需等待视频加载。

我的连接速度慢的问题现已解决!