Android WebView -> Показать веб-архив
Android WebView имеет этот метод saveWebArchive начиная с уровня API 11: http://developer.android.com/reference/android/webkit/WebView.html.
Это может сохранить целые сайты как веб-архивы, и это здорово! Но как мне вернуть загруженное содержимое в веб-просмотр? Я старался
webview.loadUrl(Uri.fromFile(mywebarchivefile));
Но это только отображает XML на экране.
2 ответа
Обновление 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 заявляют, что в интерфейс WebViewClient в API11 (Honeycomb) был добавлен shouldInterceptRequest (), этот код работает и был успешно протестирован в эмуляторах 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.