XML verilerini ayrıştırma

Genişletilebilir Biçimlendirme Dili (XML), dokümanları kodlamaya ilişkin okunabilir bir biçimde olması gerekir. XML, internette veri paylaşımı için popüler bir biçimdir.

İçeriğini sık sık güncelleyen haber siteleri veya bloglar gibi web siteleri, harici programların içeriği takip edebilmesi için genellikle bir XML feed'i sağlar anlamına gelir. Ağa bağlı cihazlar için XML verilerini yükleme ve ayrıştırma, yaygın olarak kullanılan bir görevdir Bu konuda, XML dokümanlarının nasıl ayrıştırılacağı ve bu belgelerin nasıl kullanılacağı açıklanmaktadır.

Android uygulamanızda web tabanlı içerik oluşturma hakkında daha fazla bilgi edinmek için Web tabanlı içerik.

Ayrıştırıcı seçin

verimli ve etkili bir çözüm olan XmlPullParser XML'de XML ayrıştırmasının kolay bir yoludur. Android'de iki aşağıdaki adımları uygulayın:

Her iki seçenek de kullanılabilir. İlgili içeriği oluşturmak için kullanılan bu bölümdeki bir örnekte ExpatPullParser ve Xml.newPullParser().

Feed'i analiz etme

Bir feed'i ayrıştırmanın ilk adımı, ilgilendiğiniz alanlarla ilgili karar vermektir. Ayrıştırıcı, bu alanlar için verileri ayıklar ve geri kalanını yok sayar.

Örnek uygulamada ayrıştırılmış bir feed'den alınan aşağıdaki alıntıya bakın. Her biri StackOverflow.com'a gönderilen yayınlar feed'inizi, iç içe yerleştirilmiş birkaç etiket içeren bir entry etiketi olarak tanımlar:

<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:creativeCommons="http://backend.userland.com/creativeCommonsRssModule" ...">
<title type="text">newest questions tagged android - Stack Overflow</title>
...
    <entry>
    ...
    </entry>
    <entry>
        <id>http://stackoverflow.com/q/9439999</id>
        <re:rank scheme="http://stackoverflow.com">0</re:rank>
        <title type="text">Where is my data file?</title>
        <category scheme="http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest/tags" term="android"/>
        <category scheme="http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest/tags" term="file"/>
        <author>
            <name>cliff2310</name>
            <uri>http://stackoverflow.com/users/1128925</uri>
        </author>
        <link rel="alternate" href="http://stackoverflow.com/questions/9439999/where-is-my-data-file" />
        <published>2012-02-25T00:30:54Z</published>
        <updated>2012-02-25T00:30:54Z</updated>
        <summary type="html">
            <p>I have an Application that requires a data file...</p>

        </summary>
    </entry>
    <entry>
    ...
    </entry>
...
</feed>

Örnek uygulama entry etiketi ve iç içe geçmiş etiketleri için verileri ayıklar title, link ve summary.

Ayrıştırıcıyı örneklendirme

Bir feed'i ayrıştırmada bir sonraki adım, ayrıştırıcı örneklendirilir ve ayrıştırma işlemine başlar. Bu snippet ad alanlarını işlememesi ve sağlanan InputStream öğesini giriş olarak kullanması için bir ayrıştırıcı başlatır. Ayrıştırma sürecini başlatmak için bir nextTag() ve Uygulamanın verilerini ayıklayan ve işleyen readFeed() yöntemi ilgi alanları:

Kotlin

// We don't use namespaces.
private val ns: String? = null

class StackOverflowXmlParser {

    @Throws(XmlPullParserException::class, IOException::class)
    fun parse(inputStream: InputStream): List<*> {
        inputStream.use { inputStream ->
            val parser: XmlPullParser = Xml.newPullParser()
            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false)
            parser.setInput(inputStream, null)
            parser.nextTag()
            return readFeed(parser)
        }
    }
 ...
}

Java

public class StackOverflowXmlParser {
    // We don't use namespaces.
    private static final String ns = null;

    public List parse(InputStream in) throws XmlPullParserException, IOException {
        try {
            XmlPullParser parser = Xml.newPullParser();
            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
            parser.setInput(in, null);
            parser.nextTag();
            return readFeed(parser);
        } finally {
            in.close();
        }
    }
 ...
}

Feed'i oku

readFeed() yöntemi, feed'i seçin. "Giriş" etiketli öğeleri arar. yinelemeli bir başlangıç noktası olarak bir şablondur. Bir etiket entry etiketi değilse bu etiketi atlar. Tüm feed yinelemeli olarak işlenir, readFeed() bir List içeren tüm bu alt bölümlere (iç içe yerleştirilmiş veri üyeleri dahil) özet akışından alındı. Bu List, daha sonra ayrıştırıcıdır.

Kotlin

@Throws(XmlPullParserException::class, IOException::class)
private fun readFeed(parser: XmlPullParser): List<Entry> {
    val entries = mutableListOf<Entry>()

    parser.require(XmlPullParser.START_TAG, ns, "feed")
    while (parser.next() != XmlPullParser.END_TAG) {
        if (parser.eventType != XmlPullParser.START_TAG) {
            continue
        }
        // Starts by looking for the entry tag.
        if (parser.name == "entry") {
            entries.add(readEntry(parser))
        } else {
            skip(parser)
        }
    }
    return entries
}

Java

private List readFeed(XmlPullParser parser) throws XmlPullParserException, IOException {
    List entries = new ArrayList();

    parser.require(XmlPullParser.START_TAG, ns, "feed");
    while (parser.next() != XmlPullParser.END_TAG) {
        if (parser.getEventType() != XmlPullParser.START_TAG) {
            continue;
        }
        String name = parser.getName();
        // Starts by looking for the entry tag.
        if (name.equals("entry")) {
            entries.add(readEntry(parser));
        } else {
            skip(parser);
        }
    }
    return entries;
}

XML ayrıştırma

Bir XML feed'ini ayrıştırma adımları aşağıda açıklanmıştır:

  1. Feed'i analiz etme bölümünde açıklandığı gibi uygulamanıza dahil etmek istediğiniz etiketleri tanımlayın. Bu örnek, entry etiketi ve iç içe yerleştirilmiş etiketlerine ilişkin verileri ayıklar: title, link ve summary.
  2. Aşağıdaki yöntemleri oluşturun:

    • Bir "okuma" yöntemini kullanabilirsiniz (örneğin, readEntry() ve readTitle(). Ayrıştırıcı şu verileri okur: etiketleri arasındaki ilişkiyi belirtir. Bu örnekte, entry, title, link veya summary için uygun yöntemi çağırır bu etiket için geçerli olur. Aksi takdirde, etiketi atlar.
    • Her bir farklı etiket türü için veri ayıklama ve ayrıştırıcıyı sonraki etikete ekleyin. Bu örnekte, alakalı yöntemler aşağıdaki gibidir:
      • Ayrıştırıcı, title ve summary etiketleri için readText(). Bu yöntem, şunu çağırarak bu etiketlere ilişkin verileri ayıklar: parser.getText()
      • link etiketi için ayrıştırıcı, öncelikle bağlantının farklı türde belirli bir sayfadır. Daha sonra parser.getAttributeValue() kullanır. bağlantının değerini çıkarın.
      • Ayrıştırıcı, entry etiketi için readEntry() yöntemini çağırır. Bu yöntem, girişin iç içe yerleştirilmiş etiketlerini ayrıştırır ve bir Entry döndürür. title, link ve veri üyelerini içeren nesne summary.
    • Yinelemeli bir yardımcı skip() yöntemi. Bu konuyla ilgili daha fazla bilgi için Önem verdiğiniz etiketleri atlama bölümüne bakın.

Bu snippet, ayrıştırıcının girişleri, başlıkları, bağlantıları ve özetleri nasıl ayrıştırdığını gösterir.

Kotlin

data class Entry(val title: String?, val summary: String?, val link: String?)

// Parses the contents of an entry. If it encounters a title, summary, or link tag, hands them off
// to their respective "read" methods for processing. Otherwise, skips the tag.
@Throws(XmlPullParserException::class, IOException::class)
private fun readEntry(parser: XmlPullParser): Entry {
    parser.require(XmlPullParser.START_TAG, ns, "entry")
    var title: String? = null
    var summary: String? = null
    var link: String? = null
    while (parser.next() != XmlPullParser.END_TAG) {
        if (parser.eventType != XmlPullParser.START_TAG) {
            continue
        }
        when (parser.name) {
            "title" -> title = readTitle(parser)
            "summary" -> summary = readSummary(parser)
            "link" -> link = readLink(parser)
            else -> skip(parser)
        }
    }
    return Entry(title, summary, link)
}

// Processes title tags in the feed.
@Throws(IOException::class, XmlPullParserException::class)
private fun readTitle(parser: XmlPullParser): String {
    parser.require(XmlPullParser.START_TAG, ns, "title")
    val title = readText(parser)
    parser.require(XmlPullParser.END_TAG, ns, "title")
    return title
}

// Processes link tags in the feed.
@Throws(IOException::class, XmlPullParserException::class)
private fun readLink(parser: XmlPullParser): String {
    var link = ""
    parser.require(XmlPullParser.START_TAG, ns, "link")
    val tag = parser.name
    val relType = parser.getAttributeValue(null, "rel")
    if (tag == "link") {
        if (relType == "alternate") {
            link = parser.getAttributeValue(null, "href")
            parser.nextTag()
        }
    }
    parser.require(XmlPullParser.END_TAG, ns, "link")
    return link
}

// Processes summary tags in the feed.
@Throws(IOException::class, XmlPullParserException::class)
private fun readSummary(parser: XmlPullParser): String {
    parser.require(XmlPullParser.START_TAG, ns, "summary")
    val summary = readText(parser)
    parser.require(XmlPullParser.END_TAG, ns, "summary")
    return summary
}

// For the tags title and summary, extracts their text values.
@Throws(IOException::class, XmlPullParserException::class)
private fun readText(parser: XmlPullParser): String {
    var result = ""
    if (parser.next() == XmlPullParser.TEXT) {
        result = parser.text
        parser.nextTag()
    }
    return result
}
...

Java

public static class Entry {
    public final String title;
    public final String link;
    public final String summary;

    private Entry(String title, String summary, String link) {
        this.title = title;
        this.summary = summary;
        this.link = link;
    }
}

// Parses the contents of an entry. If it encounters a title, summary, or link tag, hands them off
// to their respective "read" methods for processing. Otherwise, skips the tag.
private Entry readEntry(XmlPullParser parser) throws XmlPullParserException, IOException {
    parser.require(XmlPullParser.START_TAG, ns, "entry");
    String title = null;
    String summary = null;
    String link = null;
    while (parser.next() != XmlPullParser.END_TAG) {
        if (parser.getEventType() != XmlPullParser.START_TAG) {
            continue;
        }
        String name = parser.getName();
        if (name.equals("title")) {
            title = readTitle(parser);
        } else if (name.equals("summary")) {
            summary = readSummary(parser);
        } else if (name.equals("link")) {
            link = readLink(parser);
        } else {
            skip(parser);
        }
    }
    return new Entry(title, summary, link);
}

// Processes title tags in the feed.
private String readTitle(XmlPullParser parser) throws IOException, XmlPullParserException {
    parser.require(XmlPullParser.START_TAG, ns, "title");
    String title = readText(parser);
    parser.require(XmlPullParser.END_TAG, ns, "title");
    return title;
}

// Processes link tags in the feed.
private String readLink(XmlPullParser parser) throws IOException, XmlPullParserException {
    String link = "";
    parser.require(XmlPullParser.START_TAG, ns, "link");
    String tag = parser.getName();
    String relType = parser.getAttributeValue(null, "rel");
    if (tag.equals("link")) {
        if (relType.equals("alternate")){
            link = parser.getAttributeValue(null, "href");
            parser.nextTag();
        }
    }
    parser.require(XmlPullParser.END_TAG, ns, "link");
    return link;
}

// Processes summary tags in the feed.
private String readSummary(XmlPullParser parser) throws IOException, XmlPullParserException {
    parser.require(XmlPullParser.START_TAG, ns, "summary");
    String summary = readText(parser);
    parser.require(XmlPullParser.END_TAG, ns, "summary");
    return summary;
}

// For the tags title and summary, extracts their text values.
private String readText(XmlPullParser parser) throws IOException, XmlPullParserException {
    String result = "";
    if (parser.next() == XmlPullParser.TEXT) {
        result = parser.getText();
        parser.nextTag();
    }
    return result;
}
  ...
}

İlgilenmediğiniz etiketleri atlayın

Ayrıştırıcının ilgilenmediği etiketleri atlaması gerekiyor. Ayrıştırıcının skip() yöntemi şöyledir:

Kotlin

@Throws(XmlPullParserException::class, IOException::class)
private fun skip(parser: XmlPullParser) {
    if (parser.eventType != XmlPullParser.START_TAG) {
        throw IllegalStateException()
    }
    var depth = 1
    while (depth != 0) {
        when (parser.next()) {
            XmlPullParser.END_TAG -> depth--
            XmlPullParser.START_TAG -> depth++
        }
    }
}

Java

private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
    if (parser.getEventType() != XmlPullParser.START_TAG) {
        throw new IllegalStateException();
    }
    int depth = 1;
    while (depth != 0) {
        switch (parser.next()) {
        case XmlPullParser.END_TAG:
            depth--;
            break;
        case XmlPullParser.START_TAG:
            depth++;
            break;
        }
    }
 }

Bu şöyle işler:

  • Geçerli etkinlik gerçek bir etkinlik değilse START_TAG
  • START_TAG veri türünün yanı sıra, şu tarihe kadar ve dahil tüm etkinlikleri kullanır: eşleşen END_TAG.
  • İç içe yerleştirme derinliğini takip ederek doğru END_TAG noktasında değil, doğru olduğunda durduğundan emin olmak için orijinal START_TAG etiketinden sonra karşılaştığı ilk etikettir.

Dolayısıyla, geçerli öğede iç içe yerleştirilmiş öğeler varsa depth, ayrıştırıcı tüm etkinlikleri tüketene kadar 0 olmaz orijinal START_TAG ve eşleşen END_TAG. Örneğin, ayrıştırıcının <author> öğesini nasıl atladığını düşünün İç içe yerleştirilmiş 2 öğesi olan <name> ve <uri>:

  • while döngüsünde ilk kez, ayrıştırıcının bir sonraki etiketi <author> sonrası START_TAG karşılaşma oranı <name>. depth değeri şu şekilde artar: 2.
  • while döngüsünden ikincisinde, ayrıştırıcı bir sonraki etiketle END_TAG </name>. Değer (depth) 1'e iner.
  • while döngüsünden üçüncü kez yararlanıldığında ayrıştırıcının bir sonraki etiketi START_TAG <uri>. Değer depth değeri 2'ye artar.
  • while döngüsünden dördüncü kez, ayrıştırıcının sonraki etiketi END_TAG </uri>. Değeri depth, 1'e düşer.
  • while döngüsünün beşinci ve son seferi, sonraki sefer ayrıştırıcının karşılaştığı etiket END_TAG </author>. depth değeri şu değere düşer: 0 (<author> öğesinin başarıyla tamamlandığını gösterir) atlandı.

XML verilerini kullan

Örnek uygulama, XML feed'ini eşzamansız olarak getirir ve ayrıştırır. Bu, işlemeyi ana kullanıcı arayüzü iş parçacığından kaldırır. Zaman Uygulama, kullanıcı arayüzünü ana etkinliğinde günceller. NetworkActivity

Aşağıdaki alıntıda loadPage() yöntemi aşağıdakileri yapar:

  • XML feed'inin URL'siyle bir dize değişkenini başlatır.
  • Kullanıcının ayarları ve ağ ise downloadXml(url) yöntemini çağırır buna izin vermiş olursunuz. Bu yöntem feed'i indirip ayrıştırır ve kullanıcı arayüzünde görüntülenir.

Kotlin

class NetworkActivity : Activity() {

    companion object {

        const val WIFI = "Wi-Fi"
        const val ANY = "Any"
        const val SO_URL = "http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest"
        // Whether there is a Wi-Fi connection.
        private var wifiConnected = false
        // Whether there is a mobile connection.
        private var mobileConnected = false

        // Whether the display should be refreshed.
        var refreshDisplay = true
        // The user's current network preference setting.
        var sPref: String? = null
    }
    ...
    // Asynchronously downloads the XML feed from stackoverflow.com.
    fun loadPage() {

        if (sPref.equals(ANY) && (wifiConnected || mobileConnected)) {
            downloadXml(SO_URL)
        } else if (sPref.equals(WIFI) && wifiConnected) {
            downloadXml(SO_URL)
        } else {
            // Show error.
        }
    }
    ...
}

Java

public class NetworkActivity extends Activity {
    public static final String WIFI = "Wi-Fi";
    public static final String ANY = "Any";
    private static final String URL = "http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest";

    // Whether there is a Wi-Fi connection.
    private static boolean wifiConnected = false;
    // Whether there is a mobile connection.
    private static boolean mobileConnected = false;
    // Whether the display should be refreshed.
    public static boolean refreshDisplay = true;
    public static String sPref = null;
    ...
    // Asynchronously downloads the XML feed from stackoverflow.com.
    public void loadPage() {

        if((sPref.equals(ANY)) && (wifiConnected || mobileConnected)) {
            downloadXml(URL);
        }
        else if ((sPref.equals(WIFI)) && (wifiConnected)) {
            downloadXml(URL);
        } else {
            // Show error.
        }
    }

downloadXml yöntemi, Kotlin'de aşağıdaki yöntemleri çağırır:

  • lifecycleScope.launch(Dispatchers.IO) (kotlin eş yordamlarını kullanarak) KS iş parçacığında loadXmlFromNetwork() yöntemini başlatın. Feed URL'sini parametresinden sonra bir değer girin. loadXmlFromNetwork() yöntemini getirme ve işleme feed'i seçin. Tamamlandığında, bir sonuç dizesi geri verir.
  • Ana iş parçacığına dönmek için Kotlin eş yordamlarını kullanan withContext(Dispatchers.Main), dizeyi döndürür ve kullanıcı arayüzünde görüntüler.

Java programlama dilinde süreç aşağıdaki gibidir:

  • Executor tarafından yürütülür bir arka plan ileti dizisinde loadXmlFromNetwork() yöntemini kullanır. Feed URL'sini parametresinden sonra bir değer girin. loadXmlFromNetwork() yöntemini getirme ve işleme feed'i seçin. Tamamlandığında, bir sonuç dizesi geri verir.
  • Handler post numaralı telefonu arar ana ileti dizisine dönmek için dizeyi döndürür ve kullanıcı arayüzünde görüntüler.

Kotlin

// Implementation of Kotlin coroutines used to download XML feed from stackoverflow.com.
private fun downloadXml(vararg urls: String) {
    var result: String? = null
    lifecycleScope.launch(Dispatchers.IO) {
        result = try {
            loadXmlFromNetwork(urls[0])
        } catch (e: IOException) {
            resources.getString(R.string.connection_error)
        } catch (e: XmlPullParserException) {
            resources.getString(R.string.xml_error)
        }
        withContext(Dispatchers.Main) {
            setContentView(R.layout.main)
            // Displays the HTML string in the UI via a WebView.
            findViewById<WebView>(R.id.webview)?.apply {
                loadData(result?: "", "text/html", null)
            }
        }
    }
}

Java

// Implementation of Executor and Handler used to download XML feed asynchronously from stackoverflow.com.
private void downloadXml(String... urls) {
    ExecutorService executor = Executors.newSingleThreadExecutor();
    Handler handler = new Handler(Looper.getMainLooper());
    executor.execute(() -> {
        String result;
            try {
                result = loadXmlFromNetwork(urls[0]);
            } catch (IOException e) {
                result = getResources().getString(R.string.connection_error);
            } catch (XmlPullParserException e) {
                result = getResources().getString(R.string.xml_error);
            }
        String finalResult = result;
        handler.post(() -> {
            setContentView(R.layout.main);
            // Displays the HTML string in the UI via a WebView.
            WebView myWebView = (WebView) findViewById(R.id.webview);
            myWebView.loadData(finalResult, "text/html", null);
        });
    });
}

Çağrılacak loadXmlFromNetwork() yöntemi Sonraki snippet'te downloadXml gösteriliyor. Aşağıdaki işlemler gerçekleşir:

  1. StackOverflowXmlParser oluşturur. Aynı zamanda Entry nesneden List tanesi (entries) ve için title, url ve summary, özelliklerini koruyacak bu alanlar için XML feed'inden ayıklanmış değerler.
  2. downloadUrl() öğesini çağırarak feed'i getirir ve InputStream.
  3. InputStream öğesini ayrıştırmak için StackOverflowXmlParser kullanır. StackOverflowXmlParser, bir Feed'den alınan verilerle birlikte List/entries.
  4. entries List işlemini işler ve feed verilerini HTML işaretlemesiyle birleştirir.
  5. Ana etkinlikte görüntülenen bir HTML dizesini döndürür Kullanıcı arayüzü.

Kotlin

// Uploads XML from stackoverflow.com, parses it, and combines it with
// HTML markup. Returns HTML string.
@Throws(XmlPullParserException::class, IOException::class)
private fun loadXmlFromNetwork(urlString: String): String {
    // Checks whether the user set the preference to include summary text.
    val pref: Boolean = PreferenceManager.getDefaultSharedPreferences(this)?.run {
        getBoolean("summaryPref", false)
    } ?: false

    val entries: List<Entry> = downloadUrl(urlString)?.use { stream ->
        // Instantiates the parser.
        StackOverflowXmlParser().parse(stream)
    } ?: emptyList()

    return StringBuilder().apply {
        append("<h3>${resources.getString(R.string.page_title)}</h3>")
        append("<em>${resources.getString(R.string.updated)} ")
        append("${formatter.format(rightNow.time)}</em>")
        // StackOverflowXmlParser returns a List (called "entries") of Entry objects.
        // Each Entry object represents a single post in the XML feed.
        // This section processes the entries list to combine each entry with HTML markup.
        // Each entry is displayed in the UI as a link that optionally includes
        // a text summary.
        entries.forEach { entry ->
            append("<p><a href='")
            append(entry.link)
            append("'>" + entry.title + "</a></p>")
            // If the user set the preference to include summary text,
            // adds it to the display.
            if (pref) {
                append(entry.summary)
            }
        }
    }.toString()
}

// Given a string representation of a URL, sets up a connection and gets
// an input stream.
@Throws(IOException::class)
private fun downloadUrl(urlString: String): InputStream? {
    val url = URL(urlString)
    return (url.openConnection() as? HttpURLConnection)?.run {
        readTimeout = 10000
        connectTimeout = 15000
        requestMethod = "GET"
        doInput = true
        // Starts the query.
        connect()
        inputStream
    }
}

Java

// Uploads XML from stackoverflow.com, parses it, and combines it with
// HTML markup. Returns HTML string.
private String loadXmlFromNetwork(String urlString) throws XmlPullParserException, IOException {
    InputStream stream = null;
    // Instantiates the parser.
    StackOverflowXmlParser stackOverflowXmlParser = new StackOverflowXmlParser();
    List<Entry> entries = null;
    String title = null;
    String url = null;
    String summary = null;
    Calendar rightNow = Calendar.getInstance();
    DateFormat formatter = new SimpleDateFormat("MMM dd h:mmaa");

    // Checks whether the user set the preference to include summary text.
    SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
    boolean pref = sharedPrefs.getBoolean("summaryPref", false);

    StringBuilder htmlString = new StringBuilder();
    htmlString.append("<h3>" + getResources().getString(R.string.page_title) + "</h3>");
    htmlString.append("<em>" + getResources().getString(R.string.updated) + " " +
            formatter.format(rightNow.getTime()) + "</em>");

    try {
        stream = downloadUrl(urlString);
        entries = stackOverflowXmlParser.parse(stream);
    // Makes sure that the InputStream is closed after the app is
    // finished using it.
    } finally {
        if (stream != null) {
            stream.close();
        }
     }

    // StackOverflowXmlParser returns a List (called "entries") of Entry objects.
    // Each Entry object represents a single post in the XML feed.
    // This section processes the entries list to combine each entry with HTML markup.
    // Each entry is displayed in the UI as a link that optionally includes
    // a text summary.
    for (Entry entry : entries) {
        htmlString.append("<p><a href='");
        htmlString.append(entry.link);
        htmlString.append("'>" + entry.title + "</a></p>");
        // If the user set the preference to include summary text,
        // adds it to the display.
        if (pref) {
            htmlString.append(entry.summary);
        }
    }
    return htmlString.toString();
}

// Given a string representation of a URL, sets up a connection and gets
// an input stream.
private InputStream downloadUrl(String urlString) throws IOException {
    URL url = new URL(urlString);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setReadTimeout(10000 /* milliseconds */);
    conn.setConnectTimeout(15000 /* milliseconds */);
    conn.setRequestMethod("GET");
    conn.setDoInput(true);
    // Starts the query.
    conn.connect();
    return conn.getInputStream();
}