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 将使用相同的文件:
这感觉有点 脏 ,但它有效(在 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 之前移动了这个位(我在执行备份之前添加它)。
现在您只需加入 backup 和 restore 操作。
备份: 正如您在上面提到的,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
实例中的所有项目并将它们放入 localStorage
。 RESTORE_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);
}
}
我有一个允许用户在线玩游戏或在本地缓存游戏的应用程序。当他们将游戏转换为缓存时,我不希望他们失去进度。
在 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 将使用相同的文件:
这感觉有点 脏 ,但它有效(在 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 之前移动了这个位(我在执行备份之前添加它)。
现在您只需加入 backup 和 restore 操作。
备份: 正如您在上面提到的,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
实例中的所有项目并将它们放入 localStorage
。 RESTORE_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);
}
}