Android webview和localStorage

295

我在一个Webview中遇到了问题,可能是由HTML5应用程序访问LocalStorage引起的。test.html文件告诉我我的浏览器(即webview)不支持LocalStorage。如果你有什么建议...

package com.test.HelloWebView; 
import android.app.Activity; 
import android.content.Context; 
import android.os.Bundle; 
import android.util.Log; 
import android.view.KeyEvent; 
import android.webkit.WebChromeClient; 
import android.webkit.WebSettings; 
import android.webkit.WebStorage; 
import android.webkit.WebView; 
import android.webkit.WebViewClient; 

public class HelloWebView extends Activity { 

    WebView webview;

    public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.main); 
        webview = (WebView) findViewById(R.id.webview); 
        webview.getSettings().setJavaScriptEnabled(true); 
        webview.setWebViewClient(new HelloWebViewClient()); 
        webview.loadUrl("file:///android_asset/test.html"); 
        WebSettings settings = webview.getSettings(); 
        settings.setJavaScriptEnabled(true); 
        settings.setDatabaseEnabled(true); 
        String databasePath = this.getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath(); 
        settings.setDatabasePath(databasePath);
        webview.setWebChromeClient(new WebChromeClient() { 
        public void onExceededDatabaseQuota(String url, String databaseIdentifier, long currentQuota, long estimatedSize, long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) { 
                quotaUpdater.updateQuota(5 * 1024 * 1024); 
            } 
        }); 
    }

    public boolean onKeyDown(int keyCode, KeyEvent event) { 
        if ((keyCode == KeyEvent.KEYCODE_BACK) && webview.canGoBack()) { 
            webview.goBack(); 
            return true; 
        } 
        return super.onKeyDown(keyCode, event); 
    }

    private class HelloWebViewClient extends WebViewClient { 
        public boolean shouldOverrideUrlLoading(WebView view, String url) { 
            view.loadUrl(url); 
            return true; 
        } 
    }
}

@Maetschl:如果你想把代码块从缩进改成反引号,请至少删除前导空格。 - BDL
8个回答

639
以下内容缺失:
settings.setDomStorageEnabled(true);

2
请确保您的目标版本至少是 Android 2.1 及以上:在清单文件中添加 android:minSdkVersion="7",并将项目构建目标(在 Eclipse 中)更改为至少为 2.1。 - danmux

65

setDatabasePath() 方法在 API 级别 19 中被弃用。我建议您使用存储位置,例如:

webView.getSettings().setDomStorageEnabled(true);
webView.getSettings().setDatabaseEnabled(true);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
    webView.getSettings().setDatabasePath("/data/data/" + webView.getContext().getPackageName() + "/databases/");
}

2
@Ram swaroop,也许只需要使用setDomStorageEnabled(true)来启用本地存储就足够了。也许你使用的是最新版本的Android和设备,这个问题在你那里没有出现过吧?(也许只需要使用setDomStorageEnabled(true)来启用本地存储就足够了),但是一些旧版本的Android存在使用本地存储后应用程序重新启动时丢失本地存储的问题。有人遇到了这个问题,并对我的答案进行了点赞。如果您有任何想法,也可以添加另一个答案。 - SBotirov
我尝试了你的解决方案,但是在应用程序被杀死/重新启动后localStorage仍然无法持久化,但有时经过多次重启后,localStorage恢复工作,并且存储在其中的数据可以正确检索。这很不寻常,但在我的情况下确实发生了。(在Sony Xperia SP上测试过) - Ram Patra
@Ramswaroop 我正在使用Nexus4,本地存储也没有问题。但是当我在三星Galaxy SII上进行测试时,出现了这个问题。我并不是说所有设备都能正常工作,只是有些设备会出现这种情况。 - SBotirov

27

我也曾经遇到过在应用程序重新启动后数据丢失的问题。添加以下内容可以解决这个问题:

webView.getSettings().setDatabasePath("/data/data/" + webView.getContext().getPackageName() + "/databases/");

它解决了数据丢失的问题,但这是什么?能详细说明一下吗? - Sorin Comanescu
为了使数据库存储API正常工作,必须使用应用程序可以写入的路径调用此方法。 http://developer.android.com/reference/android/webkit/WebSettings.html#setDatabasePath(java.lang.String) - iTake

17
一个适用于我的Android 4.2.2的解决方案,编译目标为Android 4.4W:
WebSettings settings = webView.getSettings();
settings.setDomStorageEnabled(true);
settings.setDatabaseEnabled(true);

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
  File databasePath = getDatabasePath("yourDbName");
  settings.setDatabasePath(databasePath.getPath());
}

5
如果您的应用程序使用多个Webview,则仍然会遇到麻烦:localStorage未在所有Webview之间正确共享。
如果您想在多个Webview中共享相同的数据,则唯一的方法是使用Java数据库和JavaScript接口进行修复。 此页面在github上展示了如何做到这一点。
希望这可以帮助您!

谢谢。我的老板刚刚问了同样的问题。这很有帮助。 - hsu.tw

5
这篇文章最近在我寻找类似解决方案时频繁出现。Webview的WebSettings对象自API 19起已经弃用数据库路径方法,现在返回空值,这意味着我们不能依靠它们来获取网页存储,无论我们是否在加载URL之前启用了它。
为了从网页中读取localStorage值,我们需要扩展WebViewClient()并覆盖onPageFinished(),在其中可以评估webview上的javascript,如下所示:
const val JAVASCRIPT_LOCAL_STORAGE_LOOKUP = "javascript:window.localStorage.getItem('KEY');"

...

override fun onPageFinished(view: WebView?, url: String?) {
    super.onPageFinished(view, url)
    view?.let { webView ->
        webView.evaluateJavascript(JAVASCRIPT_LOCAL_STORAGE_LOOKUP) { result ->
            // returns value from 'KEY'
        }
    }
}

只需将'KEY'替换为您想要访问的存储对象的键。这样可以避免提供可能与您已有的数据库实现冲突的任何数据库实现。请注意,这只会从刚刚完成加载的Web视图所在的域中轮询localStorage。我希望这能帮助其他人,因为我花了一点时间才弄清楚。


2

我有一个更好的解决方案,它可以在首次打开 WebView 时使用。

你只需要使用自定义回调函数,就像这样

public interface DataSavedCallback {
    void onDataSaved();
}

然后使用此函数从Webview本地存储中获取键的值

public static void getLocalStorageAndSaveOnAndroidPref(String[] keys, WebView view, dataSavedCallback savedCallback) {
    String JAVASCRIPT_LOCAL_STORAGE_LOOKUP = "javascript:window";
    view.evaluateJavascript(JAVASCRIPT_LOCAL_STORAGE_LOOKUP.concat(".localStorage.getItem('" + keys[i] + "')"), new ValueCallback<String>() {
        @Override
        public void onReceiveValue(String value) {
            Log.d(TAG, "getLocalStorageAndSaveOnAndroidPref: " + value);
        }
    });
}

您可以在webview中的任何地方调用上述函数

myWebView.post(() -> {
    String[] keys = {"androidDeliveryAppStickyNotificationTitle","androidDeliveryAppStickyNotificationSubtitle","androidAppNewSoundAlertTitle"};
    getLocalStorageAndSaveOnAndroidPref(keys, myWebView, new dataSavedCallback() {
        @Override
        public void onDataSaved() {
            //do whatever you want
        }
    });
});

0

如果你有多个 WebView,本地存储会出现问题。
两个建议:

  1. 使用 Java 数据库代替 WebView 本地存储,就像“@Guillaume Gendre”所解释的那样。(当然对我没有用)
  2. 本地存储类似于JSON,因此值存储为“键:值”。您可以将浏览器唯一标识符添加到其键中,并使用普通的 Android 本地存储。

你好!我们还开发了一个工具:Cobalt,这是一个用于构建多个 Webview 应用程序的开源框架。我们增强了 localStorage,并添加了一个 pubsub 插件,以便在 Webview 之间进行通信。(http://cobaltians.org) - Guillaume Gendre

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接