Activity再生成時にWebViewのHTMLを維持する方法

## 画面回転するとWebViewの入力内容が破棄される問題ついての解決方法

画面を回転すると、Activityは再生成されます。
その時に保持しているWebViewのHTMLのFORMの内容がクリアされてしまいます。
どうしましょう...？

大抵の場合、
じゃあ、「"android:configChanges" の属性指定で Activity再生成抑止」したらいいんじゃない？といわれますが...

画面回転以外のActivity再生成はどうするの？
物理キーボードがスライドされ有効になった時は？充電ドッグに載せた時は？


### **Activityが再生成されても問題の無いアプリケーションを作りましょう!**


## Activity/Fragment再生成時の保存/復元処理には以下のような方法があります。
- Activity#onSaveInstanceStateで保存 / Activity#onCreateで復元
- Activity#onRetainNonConfigurationInstanceで保存 / Activity#getLastNonConfigurationInstanceで復元
- Fragment#onSaveInstanceStateで保存 / Fragment#onCreateで復元
- Fragment#setRetainInstance(true)で、 Fragment が破棄されないようにする。


## ActivityのWebView
*Activity#onRetainNonConfigurationInstanceで保存 / Activity#getLastNonConfigurationInstanceで復元 する方法で行います。*

```java:WebViewActivity
public class WebViewActivity extends AppCompatActivity {
    private View mContent;
    private WebView mWebView;
    protected boolean mHasSavedInstanceState;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LOGV(TAG, "onCreate()");

        mHasSavedInstanceState = (savedInstanceState != null);

        // 保存されたWebViewを取得する。
        mContent = (View) getLastCustomNonConfigurationInstance();

        if (mContent == null) {
            // 取得できなかった場合は、Viewを作成する。
            LayoutInflater inflater = getLayoutInflater()
                    .cloneInContext(new MutableContextWrapper(this));
            ViewGroup parent = (ViewGroup) getWindow().getDecorView()
                    .findViewById(Window.ID_ANDROID_CONTENT);
            mContent = inflater.inflate(R.layout.activity_webview, parent, false);
        } else {
            // 取得したViewをsetContentViewする。
            MutableContextWrapper context = (MutableContextWrapper) mContent.getContext();
            // Change the base context for this ContextWrapper.
            context.setBaseContext(this);
        }

        setContentView(mContent);
        mWebView = (WebView) findViewById(R.id.web_view);
    }

    @Override
    public Object onRetainCustomNonConfigurationInstance() {
        LOGV(TAG, "onRetainCustomNonConfigurationInstance()");

        // WebViewを保存する。
        ((ViewGroup) findViewById(Window.ID_ANDROID_CONTENT)).removeView(mContent);
        return mContent;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        LOGV(TAG, "onDestroy()");

        // Activity破棄の理由を取得
        int changingConfigurations = getChangingConfigurations();
        LOGV(TAG, "getChangingConfigurations() :" + changingConfigurations + " (" + String.format("0x%08x", changingConfigurations) + ")");

        // Activity再作成以外の破棄の場合。
        if (changingConfigurations == 0) {
            // WebViewを破棄する。
            if (mWebView != null) {
                mWebView.stopLoading();
                mWebView.setWebChromeClient(null);
                mWebView.setWebViewClient(null);
                unregisterForContextMenu(mWebView);
                mWebView.destroy();
                mWebView = null;
            }
        }
    }
}
```

## FragmentのWebView
*Fragment#setRetainInstance(true)で、 Fragment が破棄されないようにする方法で行います。*

```java:WebViewFragment
public class WebViewFragment extends Fragment{
    View mContent;
    WebView mWebView;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        LOGV(TAG, "onCreateView()");

        // Activity再生成時にFragmentを破棄させない。
        setRetainInstance(true);

        // Viewが生成されていない場合に、Viewをインフレートして作成する。
        if (mContent == null) {
            mContent = inflater.inflate(R.layout.fragment_webview, container, false);
        }
        return mContent;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        LOGV(TAG, "onViewCreated()");
        super.onViewCreated(view, savedInstanceState);

　　　　 // WebViewがまだ設定されていない場合にメンバー変数へ格納する。
        if (mWebView == null) {
            mWebView = (WebView) view.findViewById(R.id.web_view);
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        LOGV(TAG, "onDestroy()");

        // Activityの再生成ではない場合には呼ばれる。

        // WebViewを破棄する。
        if (mWebView != null) {
            mWebView.stopLoading();
            mWebView.setWebChromeClient(null);
            mWebView.setWebViewClient(null);
            unregisterForContextMenu(mWebView);
            mWebView.destroy();
            mWebView = null;
        }
    }
```
Fragment#onDestroyは、Activity再生成時でない場合にはちゃんと呼ばれるので、WebViewの破棄の処理を行っおきます。

***
参考)
- Android WebView handling orientation changes - Stack Overflow http://stackoverflow.com/questions/1002085/android-webview-handling-orientation-changes
- slightfoot/android-web-wrapper https://github.com/slightfoot/android-web-wrapper/tree/48cb3c48c457d889fc16b4e3eba1c9e925f42cfb
- Activity再生成時のデータの保存・復元(Fragment#setRetainInstance) - プログラマってこんなかんじ？？ http://daichan4649.hatenablog.jp/entry/20120530/1338367072

***

GitHub)
granoeste/WebViewFragmentExample https://github.com/granoeste/WebViewFragmentExample