diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1cd3816..266b01e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ @@ -17,13 +18,14 @@ + android:theme="@style/Theme.AppCompat.Light.NoActionBar" + tools:replace="android:name"> + + + + + 存储功能测试 + + + +

Android 16 存储功能测试

+ +
+

系统信息

+
+
+ +
+

1. localStorage 测试

+ + +
+
+ +
+

2. sessionStorage 测试

+ +
+
+ +
+

3. WebSQL 测试

+ +
+
+ +
+

4. 时间显示测试

+
+
+ + + + + diff --git a/app/src/main/java/com/bonus/fmt/BnsPandoraEntry.java b/app/src/main/java/com/bonus/fmt/BnsPandoraEntry.java index 0d12249..e6b4ed8 100644 --- a/app/src/main/java/com/bonus/fmt/BnsPandoraEntry.java +++ b/app/src/main/java/com/bonus/fmt/BnsPandoraEntry.java @@ -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(){ diff --git a/app/src/main/java/com/bonus/fmt/DCloudWebViewHook.java b/app/src/main/java/com/bonus/fmt/DCloudWebViewHook.java new file mode 100644 index 0000000..a028f1d --- /dev/null +++ b/app/src/main/java/com/bonus/fmt/DCloudWebViewHook.java @@ -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); + } + } +} + diff --git a/app/src/main/java/com/bonus/fmt/FMTApplication.java b/app/src/main/java/com/bonus/fmt/FMTApplication.java new file mode 100644 index 0000000..3dd0629 --- /dev/null +++ b/app/src/main/java/com/bonus/fmt/FMTApplication.java @@ -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(); + } +} + diff --git a/app/src/main/java/com/bonus/fmt/WebViewConfigHelper.java b/app/src/main/java/com/bonus/fmt/WebViewConfigHelper.java new file mode 100644 index 0000000..5da298a --- /dev/null +++ b/app/src/main/java/com/bonus/fmt/WebViewConfigHelper.java @@ -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); + } + } +} +