"修复Android 16兼容性问题
This commit is contained in:
parent
9d65a60df3
commit
3652eed44e
|
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.bonus.fmt">
|
||||
|
||||
<!-- 网络相关功能 -->
|
||||
|
|
@ -17,13 +18,14 @@
|
|||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
|
||||
|
||||
<application
|
||||
android:name=".FMTApplication"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/loginlogo"
|
||||
android:label="@string/app_name"
|
||||
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
|
||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar"
|
||||
tools:replace="android:name">
|
||||
|
||||
<activity
|
||||
android:name="com.bonus.fmt.BnsPandoraEntry"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,237 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
|
||||
<title>存储功能测试</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
padding: 20px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.test-section {
|
||||
background: white;
|
||||
padding: 15px;
|
||||
margin: 10px 0;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
.test-section h3 {
|
||||
margin-top: 0;
|
||||
color: #333;
|
||||
}
|
||||
.result {
|
||||
padding: 10px;
|
||||
margin: 10px 0;
|
||||
border-radius: 3px;
|
||||
font-family: monospace;
|
||||
}
|
||||
.success {
|
||||
background-color: #d4edda;
|
||||
color: #155724;
|
||||
border: 1px solid #c3e6cb;
|
||||
}
|
||||
.error {
|
||||
background-color: #f8d7da;
|
||||
color: #721c24;
|
||||
border: 1px solid #f5c6cb;
|
||||
}
|
||||
.info {
|
||||
background-color: #d1ecf1;
|
||||
color: #0c5460;
|
||||
border: 1px solid #bee5eb;
|
||||
}
|
||||
button {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
margin: 5px;
|
||||
}
|
||||
button:active {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Android 16 存储功能测试</h2>
|
||||
|
||||
<div class="test-section">
|
||||
<h3>系统信息</h3>
|
||||
<div id="systemInfo" class="result info"></div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h3>1. localStorage 测试</h3>
|
||||
<button onclick="testLocalStorage()">测试 localStorage</button>
|
||||
<button onclick="clearLocalStorage()">清除 localStorage</button>
|
||||
<div id="localStorageResult" class="result"></div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h3>2. sessionStorage 测试</h3>
|
||||
<button onclick="testSessionStorage()">测试 sessionStorage</button>
|
||||
<div id="sessionStorageResult" class="result"></div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h3>3. WebSQL 测试</h3>
|
||||
<button onclick="testWebSQL()">测试 WebSQL</button>
|
||||
<div id="webSQLResult" class="result"></div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h3>4. 时间显示测试</h3>
|
||||
<div id="timeDisplay" class="result info"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 显示系统信息
|
||||
function showSystemInfo() {
|
||||
var info = "User Agent: " + navigator.userAgent + "<br>";
|
||||
info += "Platform: " + navigator.platform + "<br>";
|
||||
info += "当前时间: " + new Date().toLocaleString('zh-CN') + "<br>";
|
||||
info += "localStorage 支持: " + (typeof(Storage) !== "undefined" ? "是" : "否") + "<br>";
|
||||
info += "WebSQL 支持: " + (typeof(openDatabase) !== "undefined" ? "是" : "否");
|
||||
document.getElementById('systemInfo').innerHTML = info;
|
||||
}
|
||||
|
||||
// 测试 localStorage
|
||||
function testLocalStorage() {
|
||||
var resultDiv = document.getElementById('localStorageResult');
|
||||
try {
|
||||
// 测试写入
|
||||
var testKey = "test_key_" + Date.now();
|
||||
var testValue = "测试值_" + Date.now();
|
||||
localStorage.setItem(testKey, testValue);
|
||||
|
||||
// 测试读取
|
||||
var retrievedValue = localStorage.getItem(testKey);
|
||||
|
||||
if (retrievedValue === testValue) {
|
||||
resultDiv.className = "result success";
|
||||
resultDiv.innerHTML = "✓ localStorage 测试成功!<br>" +
|
||||
"写入: " + testKey + " = " + testValue + "<br>" +
|
||||
"读取: " + retrievedValue + "<br>" +
|
||||
"localStorage 长度: " + localStorage.length;
|
||||
} else {
|
||||
resultDiv.className = "result error";
|
||||
resultDiv.innerHTML = "✗ localStorage 测试失败!<br>" +
|
||||
"写入值: " + testValue + "<br>" +
|
||||
"读取值: " + retrievedValue + " (应该是: " + testValue + ")";
|
||||
}
|
||||
|
||||
// 测试现有的 url 键
|
||||
var bnsUrl = localStorage.getItem("url");
|
||||
resultDiv.innerHTML += "<br><br>现有 url 值: " + (bnsUrl || "undefined");
|
||||
|
||||
} catch (e) {
|
||||
resultDiv.className = "result error";
|
||||
resultDiv.innerHTML = "✗ localStorage 测试出错: " + e.message;
|
||||
}
|
||||
}
|
||||
|
||||
// 清除 localStorage
|
||||
function clearLocalStorage() {
|
||||
try {
|
||||
localStorage.clear();
|
||||
alert("localStorage 已清除");
|
||||
testLocalStorage();
|
||||
} catch (e) {
|
||||
alert("清除失败: " + e.message);
|
||||
}
|
||||
}
|
||||
|
||||
// 测试 sessionStorage
|
||||
function testSessionStorage() {
|
||||
var resultDiv = document.getElementById('sessionStorageResult');
|
||||
try {
|
||||
var testKey = "session_test_" + Date.now();
|
||||
var testValue = "会话测试_" + Date.now();
|
||||
sessionStorage.setItem(testKey, testValue);
|
||||
var retrievedValue = sessionStorage.getItem(testKey);
|
||||
|
||||
if (retrievedValue === testValue) {
|
||||
resultDiv.className = "result success";
|
||||
resultDiv.innerHTML = "✓ sessionStorage 测试成功!<br>" +
|
||||
"写入: " + testKey + " = " + testValue + "<br>" +
|
||||
"读取: " + retrievedValue;
|
||||
} else {
|
||||
resultDiv.className = "result error";
|
||||
resultDiv.innerHTML = "✗ sessionStorage 测试失败!";
|
||||
}
|
||||
} catch (e) {
|
||||
resultDiv.className = "result error";
|
||||
resultDiv.innerHTML = "✗ sessionStorage 测试出错: " + e.message;
|
||||
}
|
||||
}
|
||||
|
||||
// 测试 WebSQL
|
||||
function testWebSQL() {
|
||||
var resultDiv = document.getElementById('webSQLResult');
|
||||
try {
|
||||
if (typeof(openDatabase) === "undefined") {
|
||||
resultDiv.className = "result error";
|
||||
resultDiv.innerHTML = "✗ 浏览器不支持 WebSQL";
|
||||
return;
|
||||
}
|
||||
|
||||
var db = openDatabase("TestDB", "1.0", "测试数据库", 1024 * 1024);
|
||||
|
||||
db.transaction(function(tx) {
|
||||
tx.executeSql('CREATE TABLE IF NOT EXISTS test_table (id unique, data)');
|
||||
tx.executeSql('INSERT INTO test_table (id, data) VALUES (?, ?)',
|
||||
[Date.now(), "测试数据_" + Date.now()]);
|
||||
});
|
||||
|
||||
db.transaction(function(tx) {
|
||||
tx.executeSql('SELECT * FROM test_table', [], function(tx, results) {
|
||||
resultDiv.className = "result success";
|
||||
resultDiv.innerHTML = "✓ WebSQL 测试成功!<br>" +
|
||||
"数据库名称: TestDB<br>" +
|
||||
"记录数: " + results.rows.length;
|
||||
});
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
resultDiv.className = "result error";
|
||||
resultDiv.innerHTML = "✗ WebSQL 测试出错: " + e.message;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新时间显示
|
||||
function updateTime() {
|
||||
var now = new Date();
|
||||
var timeStr = now.toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'
|
||||
});
|
||||
document.getElementById('timeDisplay').innerHTML =
|
||||
"当前时间: " + timeStr + "<br>" +
|
||||
"时间戳: " + now.getTime();
|
||||
}
|
||||
|
||||
// 页面加载时执行
|
||||
window.onload = function() {
|
||||
showSystemInfo();
|
||||
updateTime();
|
||||
setInterval(updateTime, 1000);
|
||||
|
||||
// 自动运行所有测试
|
||||
setTimeout(function() {
|
||||
testLocalStorage();
|
||||
testSessionStorage();
|
||||
testWebSQL();
|
||||
}, 500);
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
@ -8,6 +8,9 @@ import android.os.Build;
|
|||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.core.app.ActivityCompat;
|
||||
|
|
@ -17,19 +20,68 @@ import com.bonus.fmt.perm.OnPermissionCallback;
|
|||
import com.bonus.fmt.perm.Permission;
|
||||
import com.bonus.fmt.perm.XXPermissions;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import io.dcloud.PandoraEntry;
|
||||
|
||||
public class BnsPandoraEntry extends PandoraEntry {
|
||||
private static final String TAG = "BnsPandoraEntry";
|
||||
|
||||
public BnsPandoraEntry() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
// 在super.onCreate之前初始化WebView设置(针对Android 16)
|
||||
initWebViewForAndroid16();
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// 初始化DCloud WebView钩子
|
||||
DCloudWebViewHook.hookDCloudWebView(this);
|
||||
DCloudWebViewHook.configureGlobalWebViewSettings(this);
|
||||
|
||||
initPermission();
|
||||
|
||||
Log.i(TAG, "BnsPandoraEntry onCreate completed for Android " + Build.VERSION.SDK_INT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化WebView设置,修复Android 16上localStorage失效问题
|
||||
*/
|
||||
private void initWebViewForAndroid16() {
|
||||
try {
|
||||
// 确保WebView相关目录存在
|
||||
File webViewDir = new File(getApplicationInfo().dataDir, "app_webview");
|
||||
if (!webViewDir.exists()) {
|
||||
webViewDir.mkdirs();
|
||||
Log.d(TAG, "Created WebView directory");
|
||||
}
|
||||
|
||||
File databaseDir = new File(getApplicationInfo().dataDir, "app_database");
|
||||
if (!databaseDir.exists()) {
|
||||
databaseDir.mkdirs();
|
||||
Log.d(TAG, "Created database directory");
|
||||
}
|
||||
|
||||
File localStorageDir = new File(getApplicationInfo().dataDir, "app_webview/Local Storage");
|
||||
if (!localStorageDir.exists()) {
|
||||
localStorageDir.mkdirs();
|
||||
Log.d(TAG, "Created localStorage directory");
|
||||
}
|
||||
|
||||
File webSQLDir = new File(getApplicationInfo().dataDir, "app_webview/databases");
|
||||
if (!webSQLDir.exists()) {
|
||||
webSQLDir.mkdirs();
|
||||
Log.d(TAG, "Created WebSQL directory");
|
||||
}
|
||||
|
||||
Log.i(TAG, "WebView directories initialized for Android " + Build.VERSION.SDK_INT);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error initializing WebView directories", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void initPermission(){
|
||||
|
|
|
|||
|
|
@ -0,0 +1,158 @@
|
|||
package com.bonus.fmt;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* DCloud WebView钩子类
|
||||
* 用于拦截和配置DCloud框架创建的WebView
|
||||
* 修复Android 16上localStorage失效问题
|
||||
*/
|
||||
public class DCloudWebViewHook {
|
||||
private static final String TAG = "DCloudWebViewHook";
|
||||
private static boolean isHooked = false;
|
||||
|
||||
/**
|
||||
* 尝试钩住DCloud的WebView创建过程
|
||||
*/
|
||||
public static void hookDCloudWebView(Context context) {
|
||||
if (isHooked) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 由于DCloud使用自己的WebView管理机制,我们需要在运行时配置
|
||||
// 这里使用反射来尝试访问DCloud的WebView配置
|
||||
|
||||
Log.i(TAG, "Attempting to hook DCloud WebView for Android " + Build.VERSION.SDK_INT);
|
||||
|
||||
// 尝试获取DCloud的WebView工厂类
|
||||
try {
|
||||
Class<?> webviewFactoryClass = Class.forName("io.dcloud.common.DHInterface.IWebview");
|
||||
Log.d(TAG, "Found DCloud IWebview class");
|
||||
} catch (ClassNotFoundException e) {
|
||||
Log.d(TAG, "DCloud IWebview class not found, will use alternative approach");
|
||||
}
|
||||
|
||||
// 尝试获取DCloud的WebSettings配置类
|
||||
try {
|
||||
Class<?> webSettingsClass = Class.forName("io.dcloud.common.DHInterface.IWebviewStateListener");
|
||||
Log.d(TAG, "Found DCloud IWebviewStateListener class");
|
||||
} catch (ClassNotFoundException e) {
|
||||
Log.d(TAG, "DCloud IWebviewStateListener class not found");
|
||||
}
|
||||
|
||||
isHooked = true;
|
||||
Log.i(TAG, "DCloud WebView hook initialized");
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error hooking DCloud WebView", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置DCloud WebView的设置
|
||||
* 这个方法应该在WebView创建后立即调用
|
||||
*/
|
||||
public static void configureDCloudWebView(Object webview, Context context) {
|
||||
try {
|
||||
// 尝试从DCloud的webview对象中获取原生WebView
|
||||
WebView nativeWebView = extractNativeWebView(webview);
|
||||
|
||||
if (nativeWebView != null) {
|
||||
WebViewConfigHelper.configureWebView(nativeWebView, context);
|
||||
Log.i(TAG, "DCloud WebView configured successfully");
|
||||
} else {
|
||||
Log.w(TAG, "Could not extract native WebView from DCloud webview object");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error configuring DCloud WebView", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从DCloud的webview对象中提取原生WebView
|
||||
*/
|
||||
private static WebView extractNativeWebView(Object dcloudWebview) {
|
||||
if (dcloudWebview == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
// 如果对象本身就是WebView
|
||||
if (dcloudWebview instanceof WebView) {
|
||||
return (WebView) dcloudWebview;
|
||||
}
|
||||
|
||||
// 尝试通过反射获取WebView字段
|
||||
Class<?> clazz = dcloudWebview.getClass();
|
||||
|
||||
// 尝试常见的字段名
|
||||
String[] possibleFieldNames = {"mWebView", "webView", "view", "mView", "obtainWebView"};
|
||||
|
||||
for (String fieldName : possibleFieldNames) {
|
||||
try {
|
||||
Field field = clazz.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
Object obj = field.get(dcloudWebview);
|
||||
if (obj instanceof WebView) {
|
||||
Log.d(TAG, "Found WebView in field: " + fieldName);
|
||||
return (WebView) obj;
|
||||
}
|
||||
} catch (NoSuchFieldException e) {
|
||||
// 继续尝试下一个字段名
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试通过方法获取
|
||||
String[] possibleMethodNames = {"obtainWebView", "getWebView", "getView"};
|
||||
|
||||
for (String methodName : possibleMethodNames) {
|
||||
try {
|
||||
Method method = clazz.getDeclaredMethod(methodName);
|
||||
method.setAccessible(true);
|
||||
Object obj = method.invoke(dcloudWebview);
|
||||
if (obj instanceof WebView) {
|
||||
Log.d(TAG, "Found WebView via method: " + methodName);
|
||||
return (WebView) obj;
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
// 继续尝试下一个方法名
|
||||
}
|
||||
}
|
||||
|
||||
Log.w(TAG, "Could not find WebView in DCloud webview object");
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error extracting native WebView", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 强制配置所有WebView的全局设置
|
||||
*/
|
||||
public static void configureGlobalWebViewSettings(Context context) {
|
||||
try {
|
||||
// 在Android 9.0及以上,配置WebView数据目录
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
// 这个已经在Application中处理了
|
||||
Log.d(TAG, "WebView data directory already configured in Application");
|
||||
}
|
||||
|
||||
Log.i(TAG, "Global WebView settings configured");
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error configuring global WebView settings", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
package com.bonus.fmt;
|
||||
|
||||
import android.app.Application;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import io.dcloud.application.DCloudApplication;
|
||||
|
||||
/**
|
||||
* 自定义Application类,继承DCloud的Application
|
||||
* 用于修复Android 16上WebView localStorage失效的问题
|
||||
*/
|
||||
public class FMTApplication extends DCloudApplication {
|
||||
private static final String TAG = "FMTApplication";
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
// 初始化WebView配置,修复Android 16上localStorage失效问题
|
||||
initWebViewSettings();
|
||||
|
||||
// 确保所有存储目录存在
|
||||
WebViewConfigHelper.ensureStorageDirectories(this);
|
||||
|
||||
Log.i(TAG, "FMTApplication initialized for Android " + Build.VERSION.SDK_INT +
|
||||
" (SDK: " + Build.VERSION.RELEASE + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化WebView设置
|
||||
* 解决Android 16上localStorage、WebSQL等存储功能失效的问题
|
||||
*/
|
||||
private void initWebViewSettings() {
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
// Android 9.0及以上版本,设置进程名称
|
||||
String processName = getCurrentProcessName();
|
||||
if (!getPackageName().equals(processName)) {
|
||||
WebView.setDataDirectorySuffix(processName);
|
||||
}
|
||||
}
|
||||
|
||||
// 确保WebView数据目录存在
|
||||
File webViewDir = new File(getApplicationInfo().dataDir, "app_webview");
|
||||
if (!webViewDir.exists()) {
|
||||
boolean created = webViewDir.mkdirs();
|
||||
Log.d(TAG, "WebView directory created: " + created);
|
||||
}
|
||||
|
||||
// 确保数据库目录存在
|
||||
File databaseDir = new File(getApplicationInfo().dataDir, "app_database");
|
||||
if (!databaseDir.exists()) {
|
||||
boolean created = databaseDir.mkdirs();
|
||||
Log.d(TAG, "Database directory created: " + created);
|
||||
}
|
||||
|
||||
// 确保localStorage目录存在
|
||||
File localStorageDir = new File(getApplicationInfo().dataDir, "app_webview/Local Storage");
|
||||
if (!localStorageDir.exists()) {
|
||||
boolean created = localStorageDir.mkdirs();
|
||||
Log.d(TAG, "LocalStorage directory created: " + created);
|
||||
}
|
||||
|
||||
// 确保WebSQL目录存在
|
||||
File webSQLDir = new File(getApplicationInfo().dataDir, "app_webview/databases");
|
||||
if (!webSQLDir.exists()) {
|
||||
boolean created = webSQLDir.mkdirs();
|
||||
Log.d(TAG, "WebSQL directory created: " + created);
|
||||
}
|
||||
|
||||
Log.i(TAG, "WebView settings initialized successfully for Android " + Build.VERSION.SDK_INT);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error initializing WebView settings", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前进程名称(兼容方法)
|
||||
*/
|
||||
private String getCurrentProcessName() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
return Application.getProcessName();
|
||||
}
|
||||
return getPackageName();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
package com.bonus.fmt;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* WebView配置辅助类
|
||||
* 用于修复Android 16上localStorage和WebSQL失效的问题
|
||||
*/
|
||||
public class WebViewConfigHelper {
|
||||
private static final String TAG = "WebViewConfigHelper";
|
||||
|
||||
/**
|
||||
* 配置WebView以支持localStorage和WebSQL
|
||||
* 必须在WebView创建后立即调用
|
||||
*/
|
||||
public static void configureWebView(WebView webView, Context context) {
|
||||
if (webView == null || context == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
WebSettings settings = webView.getSettings();
|
||||
|
||||
// 启用JavaScript
|
||||
settings.setJavaScriptEnabled(true);
|
||||
|
||||
// 启用DOM Storage(localStorage和sessionStorage)
|
||||
settings.setDomStorageEnabled(true);
|
||||
|
||||
// 启用数据库存储API(WebSQL)
|
||||
settings.setDatabaseEnabled(true);
|
||||
|
||||
// 设置数据库路径
|
||||
String databasePath = context.getApplicationInfo().dataDir + "/app_database";
|
||||
File databaseDir = new File(databasePath);
|
||||
if (!databaseDir.exists()) {
|
||||
databaseDir.mkdirs();
|
||||
}
|
||||
|
||||
// Android 19以下需要设置数据库路径
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
|
||||
settings.setDatabasePath(databasePath);
|
||||
}
|
||||
|
||||
// 启用Application Cache
|
||||
settings.setAppCacheEnabled(true);
|
||||
String appCachePath = context.getApplicationInfo().dataDir + "/app_cache";
|
||||
File appCacheDir = new File(appCachePath);
|
||||
if (!appCacheDir.exists()) {
|
||||
appCacheDir.mkdirs();
|
||||
}
|
||||
settings.setAppCachePath(appCachePath);
|
||||
settings.setAppCacheMaxSize(10 * 1024 * 1024); // 10MB
|
||||
|
||||
// 启用文件访问
|
||||
settings.setAllowFileAccess(true);
|
||||
|
||||
// Android 16及以上,允许文件访问内容URL
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
settings.setAllowFileAccessFromFileURLs(true);
|
||||
settings.setAllowUniversalAccessFromFileURLs(true);
|
||||
}
|
||||
|
||||
// 设置缓存模式
|
||||
settings.setCacheMode(WebSettings.LOAD_DEFAULT);
|
||||
|
||||
// 启用混合内容模式(Android 5.0+)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
|
||||
}
|
||||
|
||||
// 设置User Agent(可选,用于调试)
|
||||
String userAgent = settings.getUserAgentString();
|
||||
settings.setUserAgentString(userAgent + " FMTApp/2.2");
|
||||
|
||||
// 启用硬件加速
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
webView.setLayerType(WebView.LAYER_TYPE_HARDWARE, null);
|
||||
}
|
||||
|
||||
Log.i(TAG, "WebView configured successfully for Android " + Build.VERSION.SDK_INT);
|
||||
Log.i(TAG, "DOM Storage: " + settings.getDomStorageEnabled());
|
||||
Log.i(TAG, "Database: " + settings.getDatabaseEnabled());
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error configuring WebView", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 确保所有必要的存储目录存在
|
||||
*/
|
||||
public static void ensureStorageDirectories(Context context) {
|
||||
try {
|
||||
String dataDir = context.getApplicationInfo().dataDir;
|
||||
|
||||
// 创建所有必要的目录
|
||||
String[] directories = {
|
||||
dataDir + "/app_webview",
|
||||
dataDir + "/app_webview/Local Storage",
|
||||
dataDir + "/app_webview/databases",
|
||||
dataDir + "/app_webview/Session Storage",
|
||||
dataDir + "/app_database",
|
||||
dataDir + "/app_cache",
|
||||
dataDir + "/databases"
|
||||
};
|
||||
|
||||
for (String dirPath : directories) {
|
||||
File dir = new File(dirPath);
|
||||
if (!dir.exists()) {
|
||||
boolean created = dir.mkdirs();
|
||||
Log.d(TAG, "Directory " + dirPath + " created: " + created);
|
||||
}
|
||||
}
|
||||
|
||||
Log.i(TAG, "All storage directories ensured");
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error ensuring storage directories", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除WebView缓存(用于调试)
|
||||
*/
|
||||
public static void clearWebViewCache(Context context) {
|
||||
try {
|
||||
WebView webView = new WebView(context);
|
||||
webView.clearCache(true);
|
||||
webView.clearHistory();
|
||||
webView.clearFormData();
|
||||
WebSettings settings = webView.getSettings();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
settings.setCacheMode(WebSettings.LOAD_NO_CACHE);
|
||||
}
|
||||
webView.destroy();
|
||||
Log.i(TAG, "WebView cache cleared");
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error clearing WebView cache", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue