在WebView中处理屏幕旋转

Fre*_*red 8 android webview android-webview android-configchanges android-view

我的网络应用程序在Chrome中运行良好,可以很好地处理配置更改(例如屏幕旋转).一切都完好无损.

将我的网络应用加载到WebView我的Android应用中时,网络应用会在屏幕方向更改时失去状态.它确实部分保留了状态,即它保留了<input>表单元素的数据,但是所有的JavaScript变量和DOM操作都会丢失.

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

屏幕方向更改发生的情况是Activity(以及任何最终的片段)被销毁然后随后重新创建.WebView继承View和覆盖方法onSaveInstanceState以及 onRestoreInstanceState处理配置更改因此它会自动保存和恢复任何HTML表单元素的内容以及后退/前进导航历史状态.但是,不保存和恢复JavaScript变量和DOM的状态.

提出的解决方案

已经提出了一些解决方案.所有这些都是非工作的,只保留部分状态或其他方式不理想.

为WebView分配id

WebView继承了View具有方法的方法setId,该方法也可以使用元素android:id声明中的属性在布局XML文件中声明<WebView>.这是保存和恢复状态所必需的,但状态仅部分恢复.这将恢复表单输入元素,但不恢复JavaScript变量和DOM的状态.

onRetainNonConfigurationInstance和getLastNonConfigurationInstance

onRetainNonConfigurationInstance并且自API级别13以来getLastNonConfigurationInstance弃用.

强制屏幕方向

通过在文件中或通过方法设置元素的screenOrientation属性,可以强制活动屏幕方向.这是不希望的,因为它打破了屏幕旋转的期望.这也只涉及屏幕方向的变化而不是其他配置变化.<Activity>AndroidManifest.xmlsetRequestedOrientation

保留片段实例

不行.setRetainInstance在片段上调用方法确实会保留片段(它不会被破坏),因此片段的所有实例变量都会被保留,但是它破坏片段的视图,因此WebView它会被破坏.

手动处理配置更改

configChanges属性可以被声明为ActivityAndroidManifest.xml文件android:configChanges="orientation|screenSize"处理配置更改,防止它们.这有效,它可以防止活动被破坏,因此WebView它的内容被完全保留.然而,这是不鼓励的,并且据说仅作为最后的解决方案使用,因为它可能导致应用程序以微妙的方式打破并且变得越来越多.设置属性onConfigurationChanged时会调用该方法configChanges.

MutableContextWrapper

我听说MutableContextWrapper可以使用,但我没有评估这种方法.

saveState()和restoreState()

WebView有方法saveStaterestoreState.请注意,根据文档,该saveState方法不再存储WebView的显示数据,无论这意味着什么.无论哪种方式,这些方法似乎都不能完全保留WebView的状态.

WebViewFragment

WebViewFragment只是一个方便的片段,包装WebView给你,所以可以轻松地使用更少的样板代码,就像ListFragment.它没有做任何额外的状态保持完全保持状态.

是否有任何真正的解决方案来解决WebView在配置更改后被破坏和丢失状态的问题?(如屏幕旋转)

一种完全保留所有状态的解决方案,包括JavaScript变量和DOM操作.一个干净且不基于黑客或弃用方法的解决方案.

Fre*_*red 8

在研究和尝试不同的方法之后,我发现了我所相信的最佳解决方案.

它采用setRetainInstance沿与保留片段的实例addView,并removeViewonCreateViewonDestroyView防止方法WebView从被破坏掉了.

MainActivity.java

public 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

WebViewFragment.java

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/");
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 保留`new WebView(getActivity())`会在配置更改时泄漏Activity上下文(因为WebView仍然保存对初始Activity的引用).在配置更改后打开某些表单元素(如下拉列表)时,WebView可能会崩溃. (2认同)