Android WebView -> Показать WebArchive

Android WebView имеет этот метод saveWebArchive с уровня API 11: http://developer.android.com/ .

Он может сохранять целые веб-сайты в качестве веб-архивов, и это здорово! Но как получить загруженное содержимое обратно в веб-просмотр? Я пытался

webview.loadUrl(Uri.fromFile(mywebarchivefile)); 

Но на экране отображается только xml.

Solutions Collecting From Web of "Android WebView -> Показать WebArchive"

Обновление 21 февраля 2014 года

Мой ответ, размещенный ниже, не относится к файлам веб-архива, сохраненным в Android 4.4 KitKat и новее. Метод saveWebArchive () WebView под Android 4.4 «KitKat» (и, возможно, более новые версии) не сохраняет веб-архив в XML-коде, который указан ниже. Вместо этого он сохраняет страницы в формате MHT (MHTML). Легко прочитать файлы .mht – просто используйте:

 webView.loadUrl("file:///my_dir/mySavedWebPage.mht"); 

Все это намного проще, чем предыдущий метод, и совместим с другими платформами.

Ранее опубликовано

Я нуждался в нем сам, и везде, где я искал, остались без ответа такие вопросы. Поэтому мне пришлось самому это разобраться. Ниже приведен мой маленький класс WebArchiveReader и пример кода о том, как его использовать. Обратите внимание, что, несмотря на то, что Android-документы объявили, что mustInterceptRequest () был добавлен в WebViewClient в API11 (Honeycomb), этот код работает и был успешно протестирован в Android-эмуляторах до API8 (Froyo). Ниже приведен весь код, который необходим, я также загрузил полный проект в репозиторий GitHub по адресу https://github.com/gregko/WebArchiveReader

Файл WebArchiveReader.java:

 package com.hyperionics.war_test; import android.util.Base64; import android.webkit.WebResourceResponse; import android.webkit.WebView; import android.webkit.WebViewClient; import org.w3c.dom.*; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.util.ArrayList; public abstract class WebArchiveReader { private Document myDoc = null; private static boolean myLoadingArchive = false; private WebView myWebView = null; private ArrayList<String> urlList = new ArrayList<String>(); private ArrayList<Element> urlNodes = new ArrayList<Element>(); abstract void onFinished(WebView webView); public boolean readWebArchive(InputStream is) { DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = null; myDoc = null; try { builder = builderFactory.newDocumentBuilder(); } catch (ParserConfigurationException e) { e.printStackTrace(); } try { myDoc = builder.parse(is); NodeList nl = myDoc.getElementsByTagName("url"); for (int i = 0; i < nl.getLength(); i++) { Node nd = nl.item(i); if(nd instanceof Element) { Element el = (Element) nd; // siblings of el (url) are: mimeType, textEncoding, frameName, data NodeList nodes = el.getChildNodes(); for (int j = 0; j < nodes.getLength(); j++) { Node node = nodes.item(j); if (node instanceof Text) { String dt = ((Text)node).getData(); byte[] b = Base64.decode(dt, Base64.DEFAULT); dt = new String(b); urlList.add(dt); urlNodes.add((Element) el.getParentNode()); } } } } } catch (Exception e) { e.printStackTrace(); myDoc = null; } return myDoc != null; } private byte [] getElBytes(Element el, String childName) { try { Node kid = el.getFirstChild(); while (kid != null) { if (childName.equals(kid.getNodeName())) { Node nn = kid.getFirstChild(); if (nn instanceof Text) { String dt = ((Text)nn).getData(); return Base64.decode(dt, Base64.DEFAULT); } } kid = kid.getNextSibling(); } } catch (Exception e) { e.printStackTrace(); } return null; } public boolean loadToWebView(WebView v) { myWebView = v; v.setWebViewClient(new WebClient()); WebSettings webSettings = v.getSettings(); webSettings.setDefaultTextEncodingName("UTF-8"); myLoadingArchive = true; try { // Find the first ArchiveResource in myDoc, should be <ArchiveResource> Element ar = (Element) myDoc.getDocumentElement().getFirstChild().getFirstChild(); byte b[] = getElBytes(ar, "data"); // Find out the web page charset encoding String charset = null; String topHtml = new String(b).toLowerCase(); int n1 = topHtml.indexOf("<meta http-equiv=\"content-type\""); if (n1 > -1) { int n2 = topHtml.indexOf('>', n1); if (n2 > -1) { String tag = topHtml.substring(n1, n2); n1 = tag.indexOf("charset"); if (n1 > -1) { tag = tag.substring(n1); n1 = tag.indexOf('='); if (n1 > -1) { tag = tag.substring(n1+1); tag = tag.trim(); n1 = tag.indexOf('\"'); if (n1 < 0) n1 = tag.indexOf('\''); if (n1 > -1) { charset = tag.substring(0, n1).trim(); } } } } } if (charset != null) topHtml = new String(b, charset); else topHtml = new String(b); String baseUrl = new String(getElBytes(ar, "url")); v.loadDataWithBaseURL(baseUrl, topHtml, "text/html", "UTF-8", null); } catch (Exception e) { e.printStackTrace(); return false; } return true; } private class WebClient extends WebViewClient { @Override public WebResourceResponse shouldInterceptRequest(WebView view, String url) { if (!myLoadingArchive) return null; int n = urlList.indexOf(url); if (n < 0) return null; Element parentEl = urlNodes.get(n); byte [] b = getElBytes(parentEl, "mimeType"); String mimeType = b == null ? "text/html" : new String(b); b = getElBytes(parentEl, "textEncoding"); String encoding = b == null ? "UTF-8" : new String(b); b = getElBytes(parentEl, "data"); return new WebResourceResponse(mimeType, encoding, new ByteArrayInputStream(b)); } @Override public void onPageFinished(WebView view, String url) { // our WebClient is no longer needed in view view.setWebViewClient(null); myLoadingArchive = false; onFinished(myWebView); } } } 

Вот как использовать этот класс, образец класса MyActivity.java:

 package com.hyperionics.war_test; import android.app.Activity; import android.os.Bundle; import android.webkit.WebView; import android.webkit.WebViewClient; import java.io.IOException; import java.io.InputStream; public class MyActivity extends Activity { // Sample WebViewClient in case it was needed... // See continueWhenLoaded() sample function for the best place to set it on our webView private class MyWebClient extends WebViewClient { @Override public void onPageFinished(WebView view, String url) { Lt.d("Web page loaded: " + url); } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); WebView webView = (WebView) findViewById(R.id.webView); try { InputStream is = getAssets().open("TestHtmlArchive.xml"); WebArchiveReader wr = new WebArchiveReader() { void onFinished(WebView v) { // we are notified here when the page is fully loaded. continueWhenLoaded(v); } }; // To read from a file instead of an asset, use: // FileInputStream is = new FileInputStream(fileName); if (wr.readWebArchive(is)) { wr.loadToWebView(webView); } } catch (IOException e) { e.printStackTrace(); } } void continueWhenLoaded(WebView webView) { Lt.d("Page from WebArchive fully loaded."); // If you need to set your own WebViewClient, do it here, // after the WebArchive was fully loaded: webView.setWebViewClient(new MyWebClient()); // Any other code we need to execute after loading a page from a WebArchive... } } 

Чтобы сделать все, вот мой маленький класс Lt.java для вывода отладки:

 package com.hyperionics.war_test; import android.util.Log; public class Lt { private static String myTag = "war_test"; private Lt() {} static void setTag(String tag) { myTag = tag; } public static void d(String msg) { // Uncomment line below to turn on debug output Log.d(myTag, msg == null ? "(null)" : msg); } public static void df(String msg) { // Forced output, do not comment out - for exceptions etc. Log.d(myTag, msg == null ? "(null)" : msg); } } 

Надеюсь, это полезно.

Обновление 19 июля 2013 г.

На некоторых веб-страницах нет метатега, определяющего кодировку текста, а затем код, который мы показываем выше, не отображает символы правильно. В версии этого кода GitHub я теперь добавил алгоритм обнаружения набора символов, который догадывается о кодировании в таких случаях. Опять же, см. https://github.com/gregko/WebArchiveReader

Greg

Я нашел недокументированный способ чтения сохраненного веб-архива. Просто делать:

 String raw_data = (read the mywebarchivefile as a string) 

А затем позвоните

 webview.loadDataWithBaseURL(mywebarchivefile, raw_data, "application/x-webarchive-xml", "UTF-8", null); 

Ссылка: http://androidxref.com/4.0.4/xref/external/webkit/Source/WebCore/loader/archive/ArchiveFactory.cpp

Доступен с Android 3.0, уровень api 11.