Обработка поворота экрана в WebView

мое веб-приложение отлично работает в Chrome, который обрабатывает изменения конфигурации (например, поворот экрана) отлично. Все прекрасно сохранилось.

При загрузке моего веб-приложения в WebView в моем Android-приложении веб-приложение теряет состояние при изменении ориентации экрана. Это частично сохранить состояние, то есть сохранить данные <input> элементы формы, однако все переменные JavaScript и манипуляции DOM получают потерянный.

я хотел бы, чтобы мой WebView вел себя так, как Chrome, т. е. полностью сохраняя состояние, включая любые переменные JavaScript. Следует отметить, что в то время как Chrome и WebView происходят из одной и той же базы кода, Chrome не использует WebView.

что происходит при изменении ориентации экрана, так это то, что активность (и любые возможные фрагменты) уничтожается, а затем воссоздается. WebView наследует от View и переопределяет методы onSaveInstanceState и onRestoreInstanceState для обработки изменений конфигурации, следовательно, он автоматически сохраняет и восстанавливает содержимое любых элементов формы HTML, а также состояние истории навигации назад/вперед. Однако состояние переменных JavaScript и DOM не сохраняется и не восстанавливается.

предлагаемые решения

было предложено несколько решений. Все они нерабочие, только сохраняя частичное состояние или другими способами субоптимальный.

присвоение WebView идентификатора

WebView наследует от View, который был методом setId, которая также может быть объявлен в XML-файл макета с помощью android:id атрибут в объявлении <WebView> элемент. Это необходимо для сохранения и восстановления состояния, однако состояние восстанавливается только частично. Это восстанавливает входные элементы формы, но не переменные JavaScript и состояние DOM.

onRetainNonConfigurationInstance и getLastNonConfigurationInstance

onRetainNonConfigurationInstance и getLastNonConfigurationInstance are устаревший начиная с уровня API 13.

принудительная ориентация экрана

активность может иметь свою ориентацию экрана принудительно, установив screenOrientation на <Activity> в элементе AndroidManifest.xml файл или через setRequestedOrientation метод. Это нежелательно, так как это нарушает ожидание вращения экрана. Это также касается только изменения ориентации экрана, а не других изменений конфигурации.

сохранение экземпляра фрагмента

не работает. Вызов метода setRetainInstance на фрагменте сохраняет фрагмент (он не разрушается), следовательно, все переменные экземпляра фрагмента сохраняются, однако это тут уничтожить вид фрагмента отсюда WebView does разрушается.

ручная обработка изменений конфигурации

на configChanges атрибут может быть объявлен для Activity на как android:configChanges="orientation|screenSize" обработки изменения конфигурации по их предотвращению. Это работает, это предотвращает разрушение активности, следовательно,WebView и его содержимое полностью сохраняется. Однако это было обескуражено и, как говорят, используется только в крайнем случае, так как это может привести к разрыву приложения тонкими способами и получить багги. Метод onConfigurationChanged вызывается, когда установлен.

MutableContextWrapper

я слышал MutableContextWrapper можно использовать, но я не оценил этот подход.

saveState () и restoreState ()

WebView методы saveState и restoreState. Примечание accoriding к документации saveState метод больше не хранит данные отображения для WebView, что бы это ни значило. В любом случае эти методы, похоже, не полностью сохраняют состояние WebView.

WebViewFragment

на WebViewFragment это просто удобный фрагмент, который обертывает WebView для вас, так что вы можете легко получить с меньшим шаблонным кодом, как ListFragment. Он не делает никакого дополнительного сохранения состояния, чтобы полностью сохранить государство.

вопрос

есть ли какое-либо реальное решение проблемы WebView разрушается и теряет свое состояние при изменении конфигурации? (например, вращение экрана)

решение полностью сохраняет все состояние, включая переменные JavaScript и манипуляции DOM. Решение, которое является чистым и не построено на взломах или устаревших методах.

4 ответов


после исследования и опробования различных подходов я обнаружил, что я пришел к выводу, что это оптимальное решение.

он использует setRetainInstance чтобы сохранить экземпляр фрагмента вместе с addView и removeView на onCreateView и onDestroyView методы профилактики 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();
    }
}

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/");
    }
}

я бы посоветовал вам перерисовать все это снова. Я искал некоторое время, и я не мог найти чистое готовое решение. Все там говорится, что вы позволяете веб-странице повторно визуализировать изменение ориентации.

но если вам действительно нужно сохранить ваши переменные JS, вы можете имитировать то, что saveState и restoreState сделал. То, что эти методы идеально делают, это сохранить и восстановить материал в WebView использование действия onSaveInstanceState() и onRestoreInstanceState() соответственно. Эти методы ничего не делают. они сделали это из-за потенциальных утечек памяти.

Итак, все, что вам нужно сделать, это создать свой собственный webview (MyWebView extends WebView). В этом есть два метода: saveVariableState() и restoreVariableState(). В saveVariableState() просто сохраните каждую переменную, которую вы хотите в комплекте, и верните ее (public Bundle saveVariableState(){}). Теперь в onSaveInstanceState() на Activity, называют MyWebView.saveVariableState и сохраните пакет, который он возвращает. Как только ориентация изменится, вы получите пакет из onRestoreInstanceState или onCreate и передать его в MyWebView через конструктор или restoreVariableState.

это не хак, но обычный способ сохранить материал данных других представлений. В случае WebView, вместо сохранения данных представления, вы собираетесь сохранить переменные JS.


следующий код манифеста объявляет действие, которое обрабатывает изменение ориентации экрана и изменение доступности клавиатуры: этот код:

<activity android:name=".MyActivity">

будет :

<activity android:name=".MyActivity"
          android:configChanges="orientation|screenSize|keyboardHidden"
          android:label="@string/app_name">

здесь вы только добавляете: screenSize, тогда он работает нормально.

ссылка: https://developer.android.com/guide/topics/resources/runtime-changes.html#RetainingAnObject


простая вещь, которую вы могли бы сделать, это что-то вроде:

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);
}

Примечание: не пробовал этот код, просто написал его, но думаю, что он должен работать.