Android9(API28)下Apache Cordova绑定HTTP URL到HTML5 Video标签问题
问题:Cordova Android项目中HTML5 Video无法加载HTTP视频资源
- 本地绝对/相对路径的视频可正常播放
- 绑定localhost:8080、本地IPv4+8080地址的视频无法加载
- 绑定外部HTTP视频地址(如http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4)也无法加载
现有代码配置
sample_basic.html
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="utf-8"> <meta http-equiv="Content-Security-Policy" content="default-src 'self' data: https://ssl.gstatic.com http://192.168.1.142:8080 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; img-src 'self' data: content:;"> <meta name="format-detection" content="telephone=no"> <meta name="msapplication-tap-highlight" content="no"> <meta name="color-scheme" content="light dark"> <link rel="stylesheet" href="../css/index.css"> <title>Hello World</title> <script src="../js/cordova.js"></script> <script src="../js/jquery-3.7.1.min.js"></script> <script src="../js/sample_basic.js"></script> <style> .first { margin-left: 5%; margin-top: 33%; margin-bottom: 5%; width: 100vw; height: 25vw; } .first h1 { margin-bottom: 10%; width: 90vw; text-align: center; } .first textarea { width: 90vw; text-align: left; height: 17vw; padding-left: -50px; } .second { width: 100vw; height: 50vw; text-align; center; margin-top: 15%; margin-bottom: 10%; } .second video { width: 90vw; height: 50vw; margin-left: 5%; margin-top: 5%; } .third { width: 90vw; height: 25vw; text-align: center; margin-left: 5%; display: none; } .third textarea { width: 90vw; text-align: left; height: 12vw; padding-left: -50px; margin-top: 5%; } .fourth { width: 90vw; height: 25vw; text-align: center; margin-left: 5%; display: none; } .fourth button { width: 30vw; height: 10vw; text-align: center; color: white; background-color: blue; } </style> </head> <body> <div> <div class="first"> <h1>Intro VOD</h1> <textarea readonly> * Your new VOD viewing for 2024 has been uploaded. * Please watch the video below. * If you don't finish watching it, you will not be able to move to the next page. </textarea> </div> <div class="second"> <video id="myVideo" controls> <source src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4" type="video/mp4"> </video> </div> <div class="third"> <textarea readonly> * You've finished watching VOD. * I'll save the record of your viewing results. </textarea> </div> <div class="fourth"> <button id="vodYes"> YES </button> </div> </div> </body> </html>
AndroidManifest.xml
<?xml version='1.0' encoding='utf-8'?> <manifest xmlns:tools="http://schemas.android.com/tools" android:hardwareAccelerated="true" android:versionCode="10000" android:versionName="1.0.0" package="io.cordova.hellocordova" xmlns:android="http://schemas.android.com/apk/res/android"> <supports-screens android:anyDensity="true" android:largeScreens="true" android:normalScreens="true" android:resizeable="true" android:smallScreens="true" android:xlargeScreens="true" /> <uses-permission android:name="android.permission.INTERNET" /> <application android:hardwareAccelerated="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:usesCleartextTraffic="false" android:networkSecurityConfig="@xml/network_security_config"> <activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale" android:label="@string/activity_name" android:launchMode="singleTop" android:name="MainActivity" android:theme="@android:style/Theme.DeviceDefault.NoActionBar" android:windowSoftInputMode="adjustResize"> <intent-filter android:label="@string/launcher_name"> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <uses-library android:name="org.apache.http.legacy"/> </application> <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" /> </manifest>
network_security_config.xml
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <domain-config cleartextTrafficPermitted="true"> <domain includeSubdomains="true">10.0.2.2:8080</domain> <domain includeSubdomains="true">192.168.1.142:8080</domain> <domain includeSubdomains="true">localhost:8080</domain> <domain includeSubdomains="true">10.0.2.2</domain> <domain includeSubdomains="true">192.168.1.142</domain> <domain includeSubdomains="true">localhost</domain> </domain-config> </network-security-config>
MainActivity.java
/* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package io.cordova.hellocordova; import android.os.Bundle; import android.os.Environment; import android.util.Log; import org.apache.cordova.CordovaActivity; public class MainActivity extends CordovaActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // enable Cordova apps to be started in the background Bundle extras = getIntent().getExtras(); if (extras != null && extras.getBoolean("cdvStartInBackground", false)) { moveTaskToBack(true); } // Temporal Setting launchUrl = "file:///android_asset/www/html/sample_basic.html"; // Check paths String getDirectory = Environment.getExternalStorageDirectory() + "/Android/data"; Log.i(getDirectory, "Directory: " + getDirectory); // /storage/emulated/0/Android/data // Set by <content src="index.html" /> in config.xml loadUrl(launchUrl); } }
sample_basic.js
var watchTime = 0; $(document).ready(function() { // initialization var video = $("#myVideo")[0]; // Real-time viewer's viewing time record $(video).on('timeupdate', function() { watchTime = video.currentTime; }); // Prevent viewers from manipulating the playback button slider backwards $(video).on('seeking', function(event) { // Move playback time to viewing time if slider is manipulated backwards if (watchTime < video.currentTime) { alert('A slider operation has been detected.\nAfter a while, play back from the previous viewing point.'); video.currentTime = watchTime; video.play(); } }); // Processing events that occur when a video is played $(video).on('ended', function(event) { // Processed by comparing the maximum length of the video to the viewer's viewing time if (video.currentTime === video.duration) { // the content to be processed when the maximum length of the video matches the viewer's viewing time console.log("You have finished watching the video."); alert("You have finished watching the video."); $('.third').show(); $('.fourth').show(); } }); });
问题排查与解决方案
1. 网络安全配置问题
- 问题点:
AndroidManifest.xml中android:usesCleartextTraffic="false"全局禁用了明文HTTP流量,而network_security_config.xml仅添加了本地服务器域名,未包含外部HTTP视频的域名(如clips.vorwaerts-gmbh.de)。 - 修复:
- 若需允许所有HTTP明文流量,将
AndroidManifest.xml中android:usesCleartextTraffic改为true; - 若仅允许指定域名,在
network_security_config.xml的domain-config中添加外部视频域名:<domain includeSubdomains="true">clips.vorwaerts-gmbh.de</domain>
- 若需允许所有HTTP明文流量,将
2. Content-Security-Policy(CSP)配置
- 问题点:当前CSP的
default-src未包含外部HTTP视频的域名,尽管media-src设为*,但部分Android WebView可能仍受default-src限制。 - 修复:将外部视频域名添加到
default-src中,或确保media-src规则生效:<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: https://ssl.gstatic.com http://192.168.1.142:8080 http://clips.vorwaerts-gmbh.de 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; img-src 'self' data: content:;">
3. Cordova导航白名单
- 问题点:Cordova默认限制页面导航和资源加载,需在
config.xml中添加允许访问的域名。 - 修复:在项目根目录的
config.xml中添加:<allow-navigation href="http://*/*" /> <allow-navigation href="https://*/*" /> <!-- 或更严格的指定域名 --> <allow-navigation href="http://clips.vorwaerts-gmbh.de/*" /> <allow-navigation href="http://192.168.1.142:8080/*" />
4. 本地服务器连接问题
- 问题点:模拟器访问本地
localhost:8080需使用10.0.2.2:8080(Android模拟器的本地映射地址),真机需确保与服务器在同一局域网且服务器允许外部访问。 - 修复:
- 模拟器中使用
http://10.0.2.2:8080/xxx.mp4; - 真机测试时,确认服务器防火墙开放8080端口,且设备与服务器在同一WiFi下。
- 模拟器中使用
内容的提问来源于stack exchange,提问作者Jinwoo Jeon
相关产品推荐
相关产品推荐

