Android 在WebView中处理屏幕旋转

Android 在WebView中处理屏幕旋转,android,webview,android-webview,android-view,android-configchanges,Android,Webview,Android Webview,Android View,Android Configchanges,我的web应用程序在Chrome上运行得非常好,它可以处理配置更改(如屏幕旋转)。一切都保存完好 将我的web应用加载到安卓应用中时,web应用将在屏幕方向更改时失去状态。它确实部分保留了状态,即它保留了表单元素的数据,但是所有JavaScript变量和DOM操作都丢失了 我希望我的WebView能够像Chrome那样运行,即完全保留状态,包括任何JavaScript变量。需要注意的是,虽然Chrome和WebView源自同一代码库,但Chrome内部并不使用WebView 屏幕方向发生的变化是

我的web应用程序在Chrome上运行得非常好,它可以处理配置更改(如屏幕旋转)。一切都保存完好

将我的web应用加载到安卓应用中时,web应用将在屏幕方向更改时失去状态。它确实部分保留了状态,即它保留了
表单元素的数据,但是所有JavaScript变量和DOM操作都丢失了

我希望我的WebView能够像Chrome那样运行,即完全保留状态,包括任何JavaScript变量。需要注意的是,虽然Chrome和WebView源自同一代码库,但Chrome内部并不使用WebView

屏幕方向发生的变化是活动(以及任何最终的片段)被破坏,然后重新创建
WebView
继承并覆盖这些方法,用于处理配置更改,因此它会自动保存和恢复任何HTML表单元素的内容以及后退/前进导航历史状态。但是,不会保存和恢复JavaScript变量和DOM的状态

提议的解决办法 已经提出了一些解决方案。所有这些都不工作,只保留部分状态或以其他方式次优

为WebView分配一个id
WebView
继承自
View
,该视图的方法也可以使用
元素声明中的属性在布局XML文件中声明。这是保存和恢复状态所必需的,但状态仅部分恢复。这将恢复表单输入元素,但不恢复JavaScript变量和DOM状态

onRetainNonConfigurationInstance和getLastNonConfigurationInstance 自API等级13起,已弃用

强制屏幕定向 通过设置
方法的属性,活动可以强制其屏幕方向。这是不希望的,因为它打破了屏幕旋转的预期。这也只处理屏幕方向的变化,而不处理其他配置的变化

保留片段实例 不起作用。对片段调用方法会保留片段(它不会被销毁),因此片段的所有实例变量都会被保留,但是它会破坏片段的视图,因此
WebView
会被销毁

手动处理配置更改 可以在
AndroidManifest.xml
文件中将
活动的属性声明为
android:configChanges=“orientation | screenSize”
以通过阻止它们来处理。这样做可以防止活动被破坏,因此
WebView
及其内容得到完全保留。然而,这一直被劝阻,据说只是作为最后的解决方案使用,因为它可能会导致应用程序以微妙的方式中断并出现bug。当设置
configChanges
属性时,将调用该方法

可变上下文包装器 我听说可以使用,但我还没有评估过这种方法

saveState()和restoreState()
WebView
有方法和步骤。注意:根据文档,
saveState
方法不再存储WebView的显示数据,无论这意味着什么。无论哪种方式,这些方法似乎都不能完全保留WebView的状态

WebViewFragment 这只是一个方便的片段,为您包装了
WebView
,因此可以轻松地使用较少的样板代码,就像
ListFragment
。它不做任何额外的状态保存来完全保存状态

该类在API级别28中被弃用

问题: 对于
WebView
在配置更改时被破坏并失去其状态的问题,有什么真正的解决方案吗?(如屏幕旋转)


一个完全保留所有状态的解决方案,包括JavaScript变量和DOM操作。一个干净的解决方案,不是建立在黑客或不推荐的方法之上。

我建议您重新渲染整个内容。我找了一会儿,找不到一个干净的现成的解决方案。所有的东西都表明你让网页在方向改变时重新呈现

但是,如果确实需要持久化JS变量,可以模仿
saveState
restoreState
所做的操作。理想情况下,这些方法分别使用活动的
onSaveInstanceState()
onRestoreInstanceState()
WebView
中保存和还原内容。由于潜在的内存泄漏,这些方法无法完成它们所做的工作

因此,您只需创建自己的webview(
MyWebView扩展webview
)。在这种情况下,有两种方法:
saveVariableState()
restoreVariableState()
。在您的
saveVariableState()
中,只需将所需的每个变量保存在一个包中并返回它(
public bundle saveVariableState(){}
)。现在,在
活动的
onSaveInstanceState()
中,调用
MyWebView.saveVariableState
并保存它返回的捆绑包。方向更改后,您可以从
onRestoreInstanceState
onCreate
获取捆绑包,并通过构造函数或
restoreVariableState
将其传递给
MyWebView


这不是黑客攻击,而是保存其他视图数据的正常方式。在
WebView
的情况下,不保存视图的数据,而是保存JS变量。

在研究和尝试不同的方法后,我发现我认为是最佳的解决方案

它用于在和方法中保留片段实例,以防止
WebView
被破坏

MainActivity.javapublic class MainActivity extends Activity { private static final String TAG_FRAGMENT = "webView"; @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); WebViewFragment fragment = (WebViewFragment) getFragmentManager().findFragmentByTag(TAG_FRAGMENT); if (fragment == null) { fragment = new WebViewFragment(); } getFragmentManager().beginTransaction().replace(android.R.id.content, fragment, TAG_FRAGMENT).commit(); } }
public class WebViewFragment extends Fragment {
    private WebView mWebView;

    public WebViewFragment() {
        setRetainInstance(true);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragment_webview, container, false);
        LinearLayout layout = (LinearLayout)v.findViewById(R.id.linearLayout);
        if (mWebView == null) {
            mWebView = new WebView(getActivity());
            setupWebView();
        }
        layout.removeAllViews();
        layout.addView(mWebView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));

        return v;
    }

    @Override
    public void onDestroyView() {
        if (getRetainInstance() && mWebView.getParent() instanceof ViewGroup) {
            ((ViewGroup) mWebView.getParent()).removeView(mWebView);
        }
        super.onDestroyView();
    }

    private void setupWebView() {
        mWebView.loadUrl("https:///www.example.com/");
    }
}
<activity android:name=".MyActivity">
<activity android:name=".MyActivity"
          android:configChanges="orientation|screenSize|keyboardHidden"
          android:label="@string/app_name">
WebView webView;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View v = inflater.inflate(R.layout.fragment_webview, container, false);

    webView = v.findViewById(R.id.webView);

    if(savedInstanceState != null){
        webView.restoreState(savedInstanceState);
    } else {
        loadUrl();
    }
    return v;
}

private void loadUrl(){
    webView.loadUrl("someUrlYouWant");
}

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    webView.saveState(outState);
}