اللغة الترميزية القابلة للامتداد (XML) هي مجموعة من قواعد ترميز المستندات بتنسيق يمكن للآلة قراءته. XML هو تنسيق شائع لمشاركة البيانات على الإنترنت.
غالبًا ما توفر مواقع الويب التي تحدِّث محتواها بشكل متكرر، مثل المواقع الإخبارية أو المدونات، خلاصة XML حتى تتمكن البرامج الخارجية من مواكبة التغييرات في المحتوى. يعد تحميل بيانات XML وتحليلها مهمة شائعة للتطبيقات المتصلة بالشبكة. يشرح هذا الموضوع كيفية تحليل مستندات XML واستخدام بياناتها.
لمعرفة المزيد حول إنشاء محتوى مستنِد إلى الويب في تطبيق Android، يُرجى الاطّلاع على المحتوى المستنِد إلى الويب.
اختيار محلّل لغوي
ننصح باستخدام XmlPullParser
، وهي طريقة فعالة
يمكن الحفاظ عليها لتحليل XML على Android. لدى Android
عمليتان لهذه الواجهة:
KXmlParser
، باستخدامXmlPullParserFactory.newPullParser()
ExpatPullParser
، باستخدامXml.newPullParser()
أي من الخيارين مناسبين. ويستخدم
المثال في هذا القسم السمتَين ExpatPullParser
وXml.newPullParser()
.
تحليل الخلاصة
تتمثّل الخطوة الأولى في تحليل الخلاصة في تحديد الحقول التي تهمّك. يستخرج المحلل بيانات هذه الحقول ويتجاهل الباقي.
يمكنك الاطّلاع على المقتطف التالي من خلاصة تم تحليلها في نموذج التطبيق. وتظهر كل مشاركة إلى StackOverflow.com في الخلاصة على شكل علامة entry
تحتوي على عدة علامات مدمجة:
<?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>
يستخرج نموذج التطبيق البيانات الخاصة بالعلامة entry
وعلاماتها المضمّنة
title
وlink
وsummary
.
إنشاء مثيل للمحلل اللغوي
تتمثّل الخطوة التالية في تحليل الخلاصة في إجراء تحليل فوري للمحلل وبدء عملية التحليل. ويعمل هذا المقتطف على إعداد محلّل لغوي لكي لا يعالج مساحات الاسم ويستخدم InputStream
كإدخال. تبدأ عملية التحليل باستدعاء nextTag()
، ويستدعي طريقة readFeed()
التي تستخرج وتعالج البيانات التي يهتم بها التطبيق:
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(); } } ... }
قراءة الخلاصة
تنفّذ طريقة readFeed()
عملية معالجة الخلاصة. تبحث عن العناصر التي تم وضع علامة "إدخال" عليها كنقطة بداية لمعالجة الخلاصة بشكل متكرّر. إذا لم تكن العلامة entry
، سيتم تخطّيها. بعد معالجة الخلاصة بالكامل بشكل متكرّر، تعرض readFeed()
القيمة List
التي تحتوي على الإدخالات (بما في ذلك عناصر البيانات المدمَجة) التي تم استخراجها من الخلاصة. ويتم بعد ذلك عرض List
هذا بواسطة المحلل اللغوي.
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
في ما يلي خطوات تحليل خلاصة XML:
- كما هو موضّح في قسم تحليل الخلاصة، حدِّد العلامات التي تريد تضمينها في تطبيقك. ويعمل هذا المثال على استخلاص بيانات العلامة
entry
وعلاماتها المدمجة:title
وlink
وsummary
. - أنشئ الطُرق التالية:
- طريقة "قراءة" لكل علامة تريد تضمينها، مثل
readEntry()
وreadTitle()
يقرأ المحلل اللغوي العلامات من ساحة مشاركات الإدخال. وعندما تصادف العلامة علامة باسم، في هذا المثال،entry
، أوtitle
، أوlink
، أوsummary
، يتم استدعاء الطريقة المناسبة لهذه العلامة. وإلا، سيتخطى العلامة. - يشير ذلك المصطلح إلى طرق لاستخراج البيانات لكل نوع مختلف من العلامات وكذلك التقدّم بالمحلل اللغوي إلى العلامة التالية. في هذا المثال، تكون الطرق ذات الصلة كما يلي:
- بالنسبة إلى العلامتين
title
وsummary
، يستدعي المحللreadText()
. تعمل هذه الطريقة على استخراج البيانات لهذه العلامات من خلال طلب الرمزparser.getText()
. - بالنسبة إلى العلامة
link
، يستخلص المحلل اللغوي بيانات الروابط من خلال تحديد أولاً ما إذا كان الرابط هو النوع محل الاهتمام أم لا. ثم يستخدمparser.getAttributeValue()
لاستخراج قيمة الرابط. - بالنسبة إلى العلامة
entry
، يطلب المحللreadEntry()
. تحلّل هذه الطريقة العلامات المتداخلة للإدخال وتعرض كائنEntry
مع أعضاء البياناتtitle
وlink
وsummary
.
- بالنسبة إلى العلامتين
- تمثّل هذه السمة طريقة
skip()
مساعِدة متكررة. لمزيد من المناقشة حول هذا الموضوع، راجع تخطي علامات لا تهمك.
- طريقة "قراءة" لكل علامة تريد تضمينها، مثل
يوضح هذا المقتطف كيفية تحليل المحلل اللغوي للإدخالات والعناوين والروابط والملخصات.
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; } ... }
تخطي العلامات التي لا تهمّك
يحتاج المحلل إلى تخطي العلامات التي لا يهمها. إليك طريقة skip()
الخاصة بالمحلل اللغوي:
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; } } }
إليك كيفية العمل:
- تقدِّم علامة استثناء إذا لم يكن الحدث الحالي
START_TAG
. - تستهلك هذه السمة
START_TAG
وجميع الأحداث وصولاً إلى قيمةEND_TAG
المطابقة. - ويتتبّع عمق التداخل للتأكّد من أنّه يتوقف عند
END_TAG
الصحيح وليس عند العلامة الأولى التي يصادفها بعد علامةSTART_TAG
الأصلية.
وبالتالي، إذا كان العنصر الحالي يحتوي على عناصر مدمجة، لن تكون قيمة
depth
هي 0 إلى أن يستخدِم المحلِّل كل الأحداث بين العنصر
START_TAG
الأصلي وتطابقه مع END_TAG
. على سبيل المثال، فكّر في الطريقة التي يتخطّاها المحلل اللغوي للعنصر <author>
الذي يحتوي على عنصرين متداخلين، هما <name>
و<uri>
:
- في المرة الأولى خلال التكرار الحلقي
while
، تكون العلامة التالية التي يصادفها المحلّل بعد<author>
هي العلامةSTART_TAG
للسمة<name>
. تزيد قيمةdepth
إلى 2. - في المرة الثانية خلال التكرار الحلقي
while
، تكون العلامة التالية التي يصادفها المحلل اللغوي هيEND_TAG
</name>
. تقل قيمةdepth
إلى 1. - في المرة الثالثة خلال التكرار الحلقي
while
، تكون العلامة التالية التي يصادفها المحلل اللغوي هيSTART_TAG
<uri>
. تزيد قيمةdepth
إلى 2. - في المرّة الرابعة خلال التكرار الحلقي
while
، تكون العلامة التالية التي يصادفها المحلل اللغوي هيEND_TAG
</uri>
. تقل قيمةdepth
إلى 1. - في المرة الخامسة والأخيرة من خلال التكرار الحلقي
while
، تكون العلامة التالية التي يصادفها المحلل اللغوي هيEND_TAG
</author>
. تقل قيمةdepth
إلى 0، ما يشير إلى أنّه تم تخطّي العنصر<author>
بنجاح.
استخدام بيانات XML
يجلب التطبيق النموذجي خلاصة XML ويحلّلها بشكل غير متزامن. يؤدي هذا الإجراء إلى إزالة المعالجة من سلسلة محادثات واجهة المستخدم الرئيسية. عند
اكتمال المعالجة، يحدّث التطبيق واجهة المستخدم في أنشطته الرئيسية،
NetworkActivity
.
في المقتطف التالي، تُنفِّذ طريقة loadPage()
ما يلي:
- تعمل هذه السياسة على إعداد متغيّر سلسلة باستخدام عنوان URL لخلاصة XML.
- يستدعي طريقة
downloadXml(url)
إذا كانت إعدادات المستخدم والاتصال بالشبكة تسمح بذلك. تؤدي هذه الطريقة إلى تنزيل الخلاصة وتحليلها وعرض نتيجة سلسلة لعرضها في واجهة المستخدم.
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
الطرق التالية في لغة Kotlin:
lifecycleScope.launch(Dispatchers.IO)
، الذي يستخدم coroutines في Kotlin لإطلاق الطريقةloadXmlFromNetwork()
على سلسلة IO. تمرِّر هذه الميزة عنوان URL للخلاصة كمَعلمة. تجلب الطريقةloadXmlFromNetwork()
الخلاصة وتعالجها. عند الانتهاء، تمرر سلسلة النتيجة.- إنّ الدالة
withContext(Dispatchers.Main)
، التي تستخدم الكوروتينات في لغة Kotlin للرجوع إلى سلسلة التعليمات الرئيسية، تأخذ السلسلة المعروضة وتعرضها في واجهة المستخدم.
في لغة البرمجة Java، تكون العملية كما يلي:
- تنفِّذ
Executor
الطريقةloadXmlFromNetwork()
في سلسلة محادثات في الخلفية. تمرِّر هذه الميزة عنوان URL للخلاصة كمَعلمة. تجلب الطريقةloadXmlFromNetwork()
الخلاصة وتعالجها. عند الانتهاء، تمرر سلسلة النتيجة. - تطلب دالة
Handler
post
للرجوع إلى سلسلة التعليمات الرئيسية، وتأخذ السلسلة المعروضة، وتعرضها في واجهة المستخدم.
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); }); }); }
يتم عرض طريقة loadXmlFromNetwork()
التي تم استدعاءها من
downloadXml
في المقتطف التالي. يؤدي هذا الإجراء إلى ما يلي:
- لإنشاء مثيل
StackOverflowXmlParser
. وتنشئ أيضًا متغيّراتList
من الكائناتEntry
(entries
) وtitle
وurl
وsummary
للاحتفاظ بالقيم المستخرَجة من خلاصة XML لهذه الحقول. - لاستدعاء
downloadUrl()
، الذي يجلب الخلاصة ويعرضها على شكلInputStream
. - تستخدِم
StackOverflowXmlParser
لتحليلInputStream
. تعملStackOverflowXmlParser
على تعبئةList
للسمةentries
ببيانات من الخلاصة. - تعالج هذه السمة
List
entries
وتدمج بيانات الخلاصة مع ترميز HTML. - لعرض سلسلة HTML يتم عرضها في واجهة مستخدم النشاط الرئيسية.
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(); }