T.M. SoftStudio

feci quod potui, faciant meliora potentes

Купить полную версию курса "Программирование мобильных приложений для платформы Android" (русскоязычная версия, лекции, тесты, лабораторные работы)

Программирование мобильных приложений для платформы Android

Лекция 13. Сеть. Часть 1

Одной из определяющих характеристик современных налодонных систем является то, что они могут связать нас и объединить в сеть без привязки нас к одному месту.

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

Поэтому я начну этот урок с обсуждения сетей в целом.

Это обсуждение будет сосредоточено на подключении вашего приложения к Интернету с помощью протокола передачи гипертекста или HTTP, особенно используя HTTP GET запросы.

После этого, я представлю несколько классов, которые Android предоставляет для поддержки такого рода сетей.

И, наконец, я обсужу, как ваши приложения могут обрабатывать данные, которые они получают в ответ на HTTP GET запросы.

В частности, я буду говорить о двух популярных языках форматирования данных.

Один из них, это язык Javascript Object Notation или JSON.

И второй, это расширяемый язык разметки Extensible Markup Language или XML.

И я буду говорить о том, как вы можете парсить эти HTTP ответы, когда они отформатированы на одном из этих языков.

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

Вы можете перемещаться из одного места в другое и продолжать выполнять полезные вычисления.

Однако сетевые возможности первых устройств были примитивными по сегодняшним меркам.

Теперь двигаясь вперед, сегодняшние устройства объединяют мощные процессоры с быстрым подключением к сети через WiFi и сотовые сети.

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

Теперь, чтобы помочь сделать это, Android предоставляет большое разнообразие классов для поддержки сетей, в том числе Socket и URL классы в пакете Java.net, а также HttpRequest и HTTPResponse классы в пакете org.apache и классы URI, AndroidHttpClient и AudioStream в пакете android.net.

В этом уроке мы рассмотрим некоторые из этих классов и используем каждый из них для реализации одно и того же примера приложения.

И это приложение взаимодействует с интернет-сервисом, чтобы получить информацию о землетрясениях, которые произошли в конкретной географической области.

И, как вы увидите, данные возвращаются в различных форматах.

Изначально мы просто отобразим скачанный текст как есть.

Позже в уроке, я покажу вам, как обработать данные, чтобы извлечь только ту информацию, которую вы хотите.

Да, и еще одна вещь. Как вы увидите, так как эти данные содержат географическую информацию, хотелось бы, чтобы они отображались на карте, а не в виде текста.

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

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

Android предоставляет несколько классов для этого.

Рассмотрим три таких класса – Socket, HttpURLConnection и AndroidHttpClient.

Итак, вот мое устройство. И я запущу NetworkingSockets приложение.

Как Вы можете видеть, это приложение изначально отображает одну кнопку с надписью Load Data.

Когда я нажимаю эту кнопку, приложение создает запрос GET HTTP к внешнему серверу.

И этот сервер ответит некоторым сложным текстом, содержащим запрашиваемые данные о землетрясениях.

Хорошо.

Так что теперь я нажму кнопку загрузки данных, и вы можете увидеть запрошенные данные.

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

Откроем основную активность для этого приложения.

И посмотрим на слушателя для кнопки загрузки данных.

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

Давайте посмотрим на этот класс.

Класс HttpGetTask сначала объявляет некоторые переменные, которые используются в создании HTTP GET запроса.

Когда execute метод вызывается, в классе HttpGetTask вызывается метод doInBackground.

И этот метод начинается с создания нового сокета, который будет подключен к хосту api.geonames.org через стандартный HTTP порт 80.

Затем код получает выходной поток сокета, а затем пишет HTTP_GET_COMMAND.

И эта строка будет передаваться на хост, который интерпретирует ее как HTTPGetRequest, а затем ответит, отправив обратно соответствующие данные.

А потом этот код продолжает выполняться, получая входной поток сокета и передавая его методу readStream.

Метод readStream в конечном счете считывает ответные данные из InputStream потока сокета. А потом возвращает ответ в виде отдельной строки.

И эта строка передается в оnPostExecute метод, который выполняется в основном потоке и который отображает ответ в View-представлении текста.

Если мы посмотрим обратно на приложение, можно заметить, что текст ответа включает в себя не только данные о землетрясениях, но также заголовки HTTP ответа.

И я бы не хотел, чтобы такой текст отображался здесь.

Я просто хочу показать вам данные о землетрясениях.

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

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

Использование сокетов – это низко-уровневое программирование, вы можете написать все, что вы хотите на сокете, но в свою очередь, вы должны обрабатывать много деталей, чтобы сделать HTTP запрос, обрабатывать все ошибки, и обрабатывать HTTP ответ.

Следующая реализация, которую мы будем рассматривать, это использование класса HttpURLConnection.

Этот класс обеспечивает более высоко-уровневый интерфейс, который обрабатывает больше деталей сетей, чем это делает класс Socket.

Но, как мы увидим чуть позже, этот класс также имеет менее гибкий API, чем наш последний вариант, Android-класс AndroidHttpClient.

Теперь, я также отмечу, что Android команда больше не работает с классом AndroidHttpClient, и он отменен, начиная с API level 22.

Итак, давайте взглянем на пример приложения, который реализован на этот раз с помощью класса HttpURLConnection.

Вот мое устройство и теперь я запустил приложение NetworkingURL.

Как и прежде, это приложение изначально отображает одну кнопку с надписью Load Data.

И, как и раньше, когда я нажимаю на эту кнопку, приложение делает HTTP GET запрос на внешний сервер, и сервер отвечает некоторым сложным текстом, содержащим запрашиваемые данные землетрясений.

Заметим, однако, что на этот раз, HTTP заголовки ответа были убраны.

Давайте посмотрим на исходный код и посмотрим, как это работает.

Откроем основную активность для этого приложения.

И здесь есть слушатель кнопки загрузки данных.

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

Давайте посмотрим на этот класс.

Когда метод execute вызывается экземпляром класса HttpGetTask, вызывается метод doInBackground.

Этот метод начинается с создания нового URL объекта, передавая URL строку запрашиваемого сервиса в качестве параметра.

Затем код вызывает метод openСonnection для объекта URL, который возвращает объект HttpURLConnection.

Код продолжает свое выполнение, получая HttpURLConnection входной поток и передавая его методу readStream.

И, как и раньше, метод readStream считывает данные ответа из входного потока, а затем возвращает ответ в виде одной строки.

На этот раз, однако, HttpURLConnection отсекает заголовки HTTP ответа и обрабатывает для вас проверку ошибок.

Далее эта строка передается в метод оnPostExecute, который отображает ответ в текстовом View-представлении.

Третий класс, который мы рассмотрим, это класс AndroidHttpClient.

Этот класс является реализацией DefaultHttpClient Apache проекта и обеспечивает большое количество настроек.

В частности, класс разбивает HTTP-транзакцию на объект запроса и объект ответа.

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

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

Откроем основную активность приложения NetworkingAndroidHttpClient.

И давайте сразу посмотрим на HttpGetTask класс.

Этот класс начинается с создания нового объекта AndroidHttpClient, вызовом метода класса newInstance.

Теперь, когда метод doInBackground вызывается, код создает объект HttpGet, передавая URL строку для этого запроса.

Затем создается объект обработчика ответа.

Этот объект отвечает за обработку ответа на HttpGet запрос.

В этом случае, обработчик ответа имеет тип BasicResponseHandler, который возвращает тело ответа.

Мы рассмотрим более сложный обработчик ответа позже в этом уроке.

И, наконец, запрос и ResponseHandler передаются в метод execute, который посылает запрос, получает ответ, пропуская его через ResponseHandler, и результат всего этого затем передается в метод оnPostExecute, который отображает ответ в текстовом View-представлении.

Лекция 13. Сеть. Часть 2

До сих пор, наши примеры приложений запрашивали данные, а потом просто отображали эти данные в TextView.

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

По сути, это становится все более популярным способом для передачи данных по всему Интернету, и многие веб-сервисы в настоящее время обеспечивают данные в таких форматах.

В частности, два формата, о которых мы будем говорить, это JavaScript Object Notation, JSON и Extensible Markup Language, XML.

Давайте поговорим о каждом из них по порядку.

Первый формат, о котором мы поговорим, это JavaScript Object Notation, JSON.

Этот формат предназначен, чтобы быть легковесным, и напоминает структуры данных, которые можно найти в традиционных языках программирования.

JSON данные упакованы одновременно в два типа структур данных.

Одна структура, это карты (таблицы), которые по существу являются наборами пар ключ-значение.

И вторая структура, которая включается в первую структуру, это упорядоченные списки значений.

Если вы хотите более подробную информацию о JSON, пожалуйста, посмотрите на этот сайт.

Теперь, давайте вернемся к нашему примеру приложения.

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

И ответ, который возвращается, на самом деле отформатирован в формате JSON.

Итак, вот эти данные давайте разберем на части.

Во-первых, данные содержат объект JSON

и этот объект представляет собой карту, которая имеет одну пару ключ-значение.

Ключ называется earthquakes, а значение представляет собой упорядоченный список.

Теперь, этот список имеет несколько объектов внутри.

И каждый из этих объектов сам является картой, которая снова, содержит пары ключ-значение.

В данном случае, есть ключ eqid и его значение это ID землетрясения.

Там также есть ключ lng и его значение это долгота, на которой был эпицентр землетрясения.

И там есть также куча других ключей.

И вместе, все эти значения обеспечивают данные для одного землетрясения.

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

Запустим NetworkingAndroidHttpClientJSON приложение.

Как и прежде, это приложение изначально отображает одну кнопку с надписью Загрузить данные.

И, как и раньше, когда я нажимаю эту кнопку, приложение выдает HTTP GET запрос внешнему серверу.

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

На этот раз, однако, эти данные будут обобщены и представлены в View-представлении списка.

Итак, теперь я нажму кнопку Загрузить данные.

И, там можно увидеть запрошенные данные, которые обобщены и представлены в виде списка.

Давайте посмотрим на исходный код, чтобы увидеть, как это работает.

Посмотрим на класс HTTPGetTask.

Здесь метод doInBackground аналогичен тому методу, что мы видели раньше, но на этот раз, он использует класс JSONResponseHandler, чтобы обработать ответ.

Давайте посмотрим на класс JSONResponseHandler.

Ключевым методом в этом классе является метод handleResponse.

Этот метод начинает с передачи первоначального ответа основному обработчику ответа BasicResponseHandler, который просто возвращает тело ответа без HTTP заголовков.

Затем код использует класс JSONTokener для парсинга JSON ответа в объект Java и последующего возврата объекта верхнего уровня, который в данном случае представляет собой карту.

Затем код извлекает значение, связанное с ключом earthquake.

И в этом случае, это упорядоченный список.

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

И это данные хранятся в Map-картах.

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

И, наконец, результат возвращается обратно вызывающему методу.

Теперь, после метода doInBackground, вызывается метод OnPostExecute.

При этом результат является его параметром.

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

Второй формат данных, о котором мы будем говорить, является расширяемым языком разметки Extensible Markup Language, XML.

XML является языком разметки для создания XML документов.

XML документы содержат разметку и содержание.

Разметка кодирует описание макета хранения документа и логическую структуру.

Содержание это все остальное.

И в частности, это содержание включает в себя данные ответа, когда XML используется для кодирования HTTP ответа.

Теперь, если вы хотите более подробную информацию о XML, пожалуйста, посмотрите на этот сайт.

Теперь давайте вернемся к нашему примеру приложения.

Если мы дадим несколько иной URL, то веб-сервис будет возвращать данные землетрясений в формате XML, а не в формате JSON.

Итак, вот эти данные, давайте разберем их на части.

Первым идет элемент с именем geonames.

В него вложена серия элементов earthquake.

И каждый из этих элементов earthquake содержит другие элементы, которые обеспечивают данные для одного землетрясения.

Так, сравнивая с тем, что мы видели в JSON формате, тут также есть элемент с именем eqid и его значение, это ID землетрясения.

Там также есть lng элемент, и его значением является долгота, на которой был эпицентр землетрясения.

И так же, как и в примере с JSON, есть куча других элементов.

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

Android предоставляет несколько различных типов XML-анализаторов, в том числе DOM-парсеры.

DOM расшифровывается как объектная модель документа Document Object Model.

И DOM-анализаторы считывают целиком весь XML документ и преобразовывают его в структуру модели документа или дерево.

И далее приложение обрабатывает эту древовидную структуру.

Такой вид парсера требует больше памяти, но позволяет приложению делать такие вещи как многопроходная обработка документа.

Еще есть SAX-анализаторы.

Эти анализаторы считывают XML документ как поток, и как только они сталкиваются с различными разделами документа, они делают обратный вызов в приложение, которое может затем обработать информацию документа.

Такие анализаторы используют меньше памяти, чем DOM парсеры, но они ограничены обработкой документа в один проход.

Pull-анализаторы, также как и SAX-анализаторы, считывают документ в виде потока.

Но Pull-анализаторы используют при этом итератор, где приложение, а не парсер, решает, когда двигать процесс разбора.

И, как и SAX-парсеры, Pull-анализаторы также используют меньше памяти, чем DOM-анализаторы.

Но Pull-анализаторы также дают приложению больший контроль над процессом синтаксического анализа, чем это делают SAX-анализаторы.

Теперь, пример приложения выглядит точно так же, как и тот пример, который мы показывали для разбора JSON ответа.

Давайте посмотрим на исходный код этого приложения.

И опять же, сразу перейдем к HTTPGetTask классу.

Метод doInBackground аналогичен тому, что мы видели раньше, но теперь он использует обработчик ответа XMLResponseHandler класс, чтобы обработать ответ.

Так давайте откроем этот класс и посмотрим, как это работает.

Теперь, как и раньше, главный метод в этом классе является метод handleResponse.

И этот метод начинается с создания объекта PullParser.

Затем код устанавливает вход синтаксического анализатора для XML документа, который был возвращен в теле HTTP ответа.

И после этого, программа получает первое событие анализатора, а затем начинает перебирать XML документ.

Теперь, в то время цикла, существуют три события, которые этот код проверяет: начальный XML тег, конечный XML тег и содержимое элемента.

Теперь, когда появляется событие, которое является событием начального тэга, вызывается метод startTag, передавая в него начальный тэг.

Этот метод определяет, является ли этот элемент данных тем, который должен быть сохранен.

И если это так, метод записывает его, устанавливая некоторые переменные.

Когда событие является событием закрывающего тэга, метод endTag вызывается, передавая конечный тэг.

И снова, этот метод идентифицирует, является ли этот элемент данных тем элементом, который нужно сохранить, и если да, он записывает его.

Кроме того, если это конечный тэг earthquake, тогда результирующая строка для этой части данных землетрясений добавляется в список результатов.

Когда событие представляет собой текстовое событие, вызывается метод text, передавая содержание элемента.

Этот метод определяет, какой тег в настоящее время анализируется и затем сохраняет содержимое для последующего использования.

И, как и прежде, когда метод doInBackground завершается, метод оnPostExecute вызывается с результатом, переданным в качестве параметра.

И этот метод создает и устанавливает адаптер списка для View-представления списка, передавая список результатов, который был вычислен в обработчике ответа.