javascript - 设备旋转/拆分视图后使用 ViewModel 恢复 Android WebView 页面
问题描述
我使用 WebView 来显示数据,数据通过 JS 函数得到增强。在设备旋转之前它工作正常。使用 ViewModel 保留页面数据并在配置更改后恢复它似乎是正确的想法,但我遇到了问题,页面没有得到恢复。我将代码缩减为一个极简示例,包括在此处(没有 XML 布局)
DataStore 类来存储数据,覆盖 ViewModel,简单的将一些字符串存储在一个列表中
package com.automationce.labyrinth.webview;
import android.arch.lifecycle.ViewModel;
import java.util.ArrayList;
import java.util.List;
/* The idea is to use ViewModel to store page data
* so it can be restored on device rotate/split view */
public final class DataStore extends ViewModel {
public List<String> records = new ArrayList<>();
public DataStore() {
super();
}
}
简单的 Web 视图类,覆盖 WebView
package com.automationce.labyrinth.webview;
import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.webkit.WebView;
public final class SimpleWebView extends WebView {
// Use to keep and restore data between config changes
// (not related to browser history by any means)
private DataStore dataStore;
public SimpleWebView(Context context, AttributeSet attrs) {
super(context, attrs);
setBackgroundColor(Color.LTGRAY);
getSettings().setJavaScriptEnabled(true);
setWebContentsDebuggingEnabled(true);
// Define style, write JS function(s)
String content = "<!DOCTYPE html><html><head>" +
"<style>" +
"span { font-size: " +
0.875 +
"em; } " +
"</style>" +
"</head><body id=\"body\">" +
jsFunctions() + "</body></html>";
loadDataWithBaseURL(null, content, "text/html", "utf-8", null);
}
// JS function(s) as written to the page
private String jsFunctions() {
return "<script>" +
"function append (string) { " +
"var body = document.getElementById('body');" +
"var block = document.createElement('span');" +
"var text = document.createTextNode(string);" +
"var br = document.createElement('br'); " +
"block.appendChild(text);" +
"block.appendChild(br); " +
"body.appendChild(block);" +
"block.scrollIntoView();" +
" }" +
"</script>";
}
public void setHistory (final DataStore storedData) {
dataStore = storedData;
}
// Restore page from DataStore data
public void restore() {
for(String record : dataStore.records) {
evaluateJavascript("append ('" + record + "');", null);
}
}
// Append new data to existing page
public void append(final String string) {
dataStore.records.add(string);
evaluateJavascript("append ('" + string + "');", null);
}
}
最后是 MainActivity 本身
package com.automationce.labyrinth.webview;
import android.arch.lifecycle.ViewModelProviders;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
private SimpleWebView pageView;
private Button appendButton;
private int counter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DataStore storedData = ViewModelProviders.of(this).get(DataStore.class);
appendButton = findViewById(R.id.button);
pageView = findViewById(R.id.webView);
pageView.setHistory(storedData);
pageView.restore();
appendButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
append();
}
});
}
private void append() {
counter++;
pageView.append("Some kind of new text " + counter);
}
}
想法是保留 ViewModel 中的数据并在创建过程中恢复它。我花了一段时间才弄清楚为什么它不起作用。DataStore 确实在旋转期间得到了保留(我可以看到 DataStore 对象中的所有历史数据),但是虽然我看到正在执行恢复代码,但它永远不会在 SimpleWebView 中呈现。
原因是,虽然 JavaScript 函数确实正确写入页面,但我在页面实际呈现之前从 MainActivity.OnCreate 调用 restore()。JS 函数被调用并很快被忽略,因为实际上,就 WebView 而言,该页面并不存在(我认为)。令人惊讶的是,这实际上适用于我使用的大多数模拟器 - 现在我认为它不应该而且我不知道为什么会这样 - 直到我在物理设备(Galaxy S7)上运行它,其中恢复被忽略
我的解决方案是给我的 SimpleWebView 一个新的 WebViewClient 并覆盖其 OnPageFinished 方法,并在页面完成加载后在其中恢复页面的内容。SimpleView 构造函数(以下代码中的“firstView”布尔标志在设备旋转后第一次重新加载视图后失效):
public SimpleWebView(Context context, AttributeSet attrs) {
super(context, attrs);
setBackgroundColor(Color.LTGRAY);
getSettings().setJavaScriptEnabled(true);
setWebContentsDebuggingEnabled(true);
setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
Log.v("PAGE", "finished");
if (firstView) {
((SimpleWebView)view).restore();
}
}
});
.
.
现在,网页在设备旋转/拆分窗口后从持久数据中重建。它似乎运行良好,没有任何明显的加载延迟(我现在正在处理相对较小的数据集)。
我一直在寻找解决方案,我真的不想在 OnSaveInstanceState / onRestoreInstanceState 中保存和恢复整个页面的字符串内容,因为在这种情况下,它对我没有任何好处 - 我将拥有数据但我仍然需要在页面渲染 后恢复它
我从集体中寻求明智的话:
- 我的分析正确吗?
- 有没有更好的方法来处理这个?
- 有什么我应该注意的(令人不快的生产代码惊喜等 - 我对 Android 很陌生)吗?
谢谢, 吉里
解决方案
推荐阅读
- python - 减去日期最相似的两个 pandas 数据帧的行
- javascript - 使 Snowpack 与 Babel 一起工作,“require is not defined”。我错过了需求转换吗?
- c++ - 将 OpenCV 图像数据获取到 C# 的问题
- python - 有人可以帮我解决这个输入错误吗?
- python - 为什么 hexdump 在我的 python 输出开头显示 0a?
- javascript - 发送 1927 年之前的日期时,邮寄请求中的奇怪日期
- pandas - Pandas - 使用另一列的 N 行降序获取一列的平均值
- python - eval() 的这种用法安全吗?
- haskell - 我如何从 ghci 取消设置:set -v?
- r - R将因子变量的级别堆叠到数据框中