android 个具有在线和离线内容的 WebView 的同步本地存储

Synchronized localstorage for android WebViews with online and offline content

我有一个允许用户在线玩游戏或在本地缓存游戏的应用程序。当他们将游戏转换为缓存时,我不希望他们失去进度。

在 KitKat 之前,您可以设置本地存储目录,但我需要更新的手机才能支持此功能。

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

找不到替换方法。结果,这里有两个目录:

root@ghost:/data/data/com.myapp.android/app_webview/Local Storage #
ls -la
total 80
drwx------ 2 u0_a165 u0_a165 4096 2017-01-06 11:51 .
drwxrwx--x 4 u0_a165 u0_a165 4096 2017-01-06 11:41 ..
-rw------- 1 u0_a165 u0_a165 4096 2017-01-06 11:51 file__0.localstorage
-rw------- 1 u0_a165 u0_a165    0 2017-01-06 11:51 file__0.localstorage-journal
-rw------- 1 u0_a165 u0_a165 4096 2017-01-06 12:02 http_cdn.myapp.com_0.localstorage
-rw------- 1 u0_a165 u0_a165    0 2017-01-06 12:02 http_cdn.myapp.com_0.localstorage-journal

有人解决过这个问题吗?

我可能会尝试在 activity 的 onPause() 期间同步文件,但我想知道是否有更优雅的解决方案 - 例如合并存储。

也许使用符号链接,两个版本的 webview 将使用相同的文件:

Creating SymLinks in Android

这感觉有点 ,但它有效(在 KitKat 和更高版本上)!

下面的 LocalStorageCopier class 使用 SharedPreferences 商店来跟踪项目(请随意使用您想要的任何其他内容)。 加载 url(本地或远程)之前创建 LocalStorageCopier 的实例并将其作为 JavascriptInterface 添加到您的 webView:

webView.setWebViewClient(new WebClient());
storageBackup = new LocalStorageCopier(someContext, someId);
webView.addJavascriptInterface(storageBackup, "backup");
webView.loadUrl (someUrl);

注意:重要的是在加载页面之前添加javascript界面。我一直在摸不着头脑为什么 "backup is undefined" 直到我在加载 url 之前移动了这个位(我在执行备份之前添加它)。

现在您只需加入 backuprestore 操作。

备份: 正如您在上面提到的,onPause 方法是备份这些东西的好地方。在 onPause 中的适当位置添加以下代码:

webView.evaluateJavascript(BACKUP_JAVASCRIPT, new ValueCallback<String>() {
        @Override public void onReceiveValue(String s) {
            LOG.d("Backed up.");
        }
    });

其中 BACKUP_JAVASCRIPT"(function() { console.log('backing up'); backup.clear(); for (var key in localStorage) { backup.set(key, localStorage.getItem(key)); }})()"

所以现在每次 activity 暂停时,都会备份 localStorage

恢复 以非常相似的方式完成。您需要在页面加载后启动恢复操作。这是 WebClient class(下面的代码)出现的地方。页面加载完成后,您必须获取 LocalStorageCopier 实例中的所有项目并将它们放入 localStorageRESTORE_JAVASCRIPT 的 javascript 是:"(function(){console.log('Restoring'); backup.dump(); var len = backup.size(); for (i = 0; i < len; i++) { var key = backup.key(i); console.log(key); localStorage.setItem(key, backup.get(key));}})()"

LocalStorageCopier

public static class LocalStorageCopier {
    private final SharedPreferences store;
    private String[] keys = null;
    private String[] values = null;

    private boolean dumped = false;

    LocalStorageCopier(final Context context, final String id) {
        store = context.getSharedPreferences(id, Context.MODE_PRIVATE);
    }

    @JavascriptInterface
    public synchronized void dump() {
        if (dumped) throw new IllegalStateException("already dumped");
        final Map<String, ?> map = store.getAll();
        final int size = map.size();
        keys = new String[size];
        values = new String[size];

        int cur = 0;

        for (final String key : map.keySet()) {
            keys[cur] = key;
            values[cur] = (String) map.get(key);
            ++cur;
        }

        dumped = true;
    }

    @JavascriptInterface
    public synchronized int size() {
        if (!dumped) throw new IllegalStateException("dump() first");
        return keys.length;
    }

    @JavascriptInterface
    public synchronized String key(final int i) {
        if (!dumped) throw new IllegalStateException("dump() first");
        return keys[i];
    }

    @JavascriptInterface
    public synchronized String value(final int i) {
        if (!dumped) throw new IllegalStateException("dump() first");
        return values[i];
    }

    @JavascriptInterface
    public synchronized String get(final String key) {
        return store.getString(key, null);
    }

    @JavascriptInterface
    public synchronized void set(final String key, final String value) {
        if (dumped) throw new IllegalStateException("already dumped");
        store.edit().putString(key, value).apply();
    }

    @JavascriptInterface
    public synchronized void clear() {
        store.edit().clear().apply();
        keys = null;
        values = null;
        dumped = false;
    }

    @Override public synchronized String toString() {
        return store.getAll().toString();
    }
}

WebClient

class WebClient extends WebViewClient {
    @Override public void onPageFinished(WebView view, String url) {
        LOG.d("Page finished. Restoring storage.");

        view.evaluateJavascript(RESTORE_JAVASCRIPT, new ValueCallback<String>() {
            @Override public void onReceiveValue(String s) {
                LOG.d("Restored.");
            }
        });

        super.onPageFinished(view, url);
    }
}