Главная страница
Образовательный портал Как узнать результаты егэ Стихи про летний лагерь 3агадки для детей

Программирование под Android. Для профессионалов


Скачать 19.35 Mb.
НазваниеПрограммирование под Android. Для профессионалов
АнкорBrayn_Khardi_Bill_Fillips_-_Programmirovanie_po.
Дата23.05.2017
Размер19.35 Mb.
Формат файлаpdf
Имя файлаBrayn_Khardi_Bill_Fillips_-_Programmirovanie_po.pdf
оригинальный pdf просмотр
ТипДокументы
#21061
страница39 из 55
КаталогОбразовательный портал Как узнать результаты егэ Стихи про летний лагерь 3агадки для детей
Образовательный портал Как узнать результаты егэ Стихи про летний лагерь 3агадки для детей
1   ...   35   36   37   38   39   40   41   42   ...   55
Глава 26. HTTP и фоновые задачи
Рис. 26.3. Создание приложения PhotoGallery
Прикажите мастеру создать пустую активность с именем
PhotoGalleryActivity
В приложении PhotoGallery используется такая же архитектура, как во всех предыдущих приложениях. Активность
PhotoGalleryActivity будет субклассом
SingleFragmentActivity
, а ее представлением будет контейнерное представление, определяемое в файле activity_fragment.xml
. Эта активность станет хостом фрагмента, а именно экземпляра
PhotoGalleryFragment
, который мы вскоре создадим.
Скопируйте файлы
SingleFragmentActivity.java и activity_fragment.xml в свой проект из предыдущего проекта.
В файле
PhotoGalleryActivity.java настройте
PhotoGalleryActivity как
SingleFrag- mentActivity
; для этого удалите код, сгенерированный шаблоном, и замените его реализацией createFragment()
. Метод createFragment()
должен возвращать экземпляр
PhotoGalleryFragment
(Пока не обращайте внимания на ошибку, которая будет обнаружена в этом коде.
Она исчезнет после того, как вы создадите класс
PhotoGalleryFragment
.)
Листинг 26.1. Настройка активности (PhotoGalleryActivity.java)
public class PhotoGalleryActivity extends Activity {
public class PhotoGalleryActivity extends SingleFragmentActivity {
/* Код, сгенерированный шаблоном */
@Override
public Fragment createFragment() {
return new PhotoGalleryFragment();
}
}

Создание приложения PhotoGallery
423
PhotoGallery будет отображать свои результаты в виджете
GridView
, который станет представлением для
PhotoGalleryFragment
Класс
GridView происходит от
AdapterView
, поэтому он работает по тем же прин- ципам, что и
ListView
. Однако в отличие от
ListView
,
GridView не имеет удобного класса
GridFragment
, который подключит все за вас. А это означает, что вам при- дется создать файл макета и заполнить его в
PhotoGalleryFragment
. Позднее в этой главе мы подключим в
PhotoGalleryFragment адаптер, который будет поставлять
GridView названия отображаемых фотографий.
Чтобы создать макет фрагмента, переименуйте файл layout/activity_photo_gallery.xml в layout/fragment_photo_gallery.xml
. Затем замените его содержимое определением
GridView
, приведенным на рис. 26.4.
Рис. 26.4. GridView (layout/fragment_photo_gallery.xml)
Здесь мы назначаем столбцам ширину 120dp и приказываем
GridView создать столь- ко столбцов, сколько поместится на экране. Если в выделенной области осталось место размером менее 120dp, атрибут stretchMode приказывает
GridView равномерно распределить его между столбцами.
Наконец, создайте класс
PhotoGalleryFragment
. Сохраните фрагмент, заполните созданный макет и получите ссылку на
GridView
(листинг 26.2).
Листинг 26.2. Заготовка кода (PhotoGalleryFragment.java)
package com.bignerdranch.android.photogallery;
public class PhotoGalleryFragment extends Fragment {
GridView mGridView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
продолжение


424
Глава 26. HTTP и фоновые задачи
Листинг 26.2 (продолжение)
View v = inflater.inflate(R.layout.fragment_photo_gallery, container,
false);
mGridView = (GridView)v.findViewById(R.id.gridView);
return v;
}
}
Прежде чем переходить к следующему разделу, запустите приложение PhotoGal- lery и убедитесь в том, что все работает правильно (то есть если вы стали гордым владельцем пустого экрана).
Основы сетевой поддержки
В нашем приложении все сетевые взаимодействия PhotoGallery будут обеспечивать- ся одним классом. Создайте новый класс Java. Поскольку мы будем подключаться к Flickr, назовите класс
FlickrFetchr
Исходная версия
FlickrFetchr будет состоять всего из двух методов: getUrlBytes(String)
и getUrl(String)
. Метод getUrlBytes(String)
получает низкоуровневые данные по URL и возвращает их в виде массива байтов. Метод getUrl(String)
преобразует результат из getUrlBytes(String)
в
String
Добавьте в файл
FlickrFetchr.java реализации getUrlBytes(String)
и getUrl(String)
(листинг 26.3).
Листинг 26.3. Основной сетевой код (FlickrFetchr.java)
package com.bignerdranch.android.photogallery;
public class FlickrFetchr {
byte[] getUrlBytes(String urlSpec) throws IOException {
URL url = new URL(urlSpec);
HttpURLConnection connection = (HttpURLConnection)url.openConnection();
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
InputStream in = connection.getInputStream();
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
return null;
}
int bytesRead = 0;
byte[] buffer = new byte[1024];
while ((bytesRead = in.read(buffer)) > 0) {
out.write(buffer, 0, bytesRead);
}
out.close();
return out.toByteArray();
} finally {
connection.disconnect();
}
}

Основы сетевой поддержки
425
public String getUrl(String urlSpec) throws IOException {
return new String(getUrlBytes(urlSpec));
}
}
Этот код создает объект
URL
на базе строки — например, https://www.google.com.
Затем вызов метода openConnection()
создает объект подключения к заданному
URL-адресу. Вызов
URL.openConnection()
возвращает
URLConnection
, но поскольку подключение осуществляется по протоколу HTTP, мы можем преобразовать его в
HttpURLConnection
. Это открывает доступ к HTTP-интерфейсам для работы с ме- тодами запросов, кодами ответов, методами потоковой передачи и т. д.
Объект
HttpURLConnection представляет подключение, но связь с конечной точкой будет установлена только после вызова getInputStream()
(или getOutputStream()
для POST-вызовов). До этого момента вы не сможете получить действительный код ответа.
После создания объекта
URL
и открытия подключения программа многократно вызывает read()
, пока в подключении не кончатся данные. Объект
InputStream предоставляет байты по мере их доступности. Когда чтение будет завершено, про- грамма закрывает его и выдает массив байтов из
ByteArrayOutputStream
Хотя всю основную работу выполняет метод getUrlBytes(String)
, в этой главе мы будем использовать метод getUrl(String)
. Он преобразует байты, полученные вызовом getUrlBytes(String)
, в
String
. На данный момент это решение смотрится немного странно — зачем разбивать выполняемую работу на два метода? Однако наличие двух методов будет полезно в следующей главе, когда мы займемся за- грузкой данных изображений.
Разрешение на работу с сетью
Для работы сетевой поддержки необходимо сделать еще одно: вы должны попро- сить разрешения. Никому из пользователей не понравится, если вы будете тайком загружать их фотографии.
Чтобы запросить разрешение на работу с сетью, добавьте следующую строку в файл
AndroidManifest.xml
Листинг 26.4. Включение разрешения на работу с сетью в манифест (AndroidManifest.xml)
package="com.bignerdranch.android.photogallery"
android:versionCode="1"
android:versionName="1.0" >
android:targetSdkVersion="15" />



426
Глава 26. HTTP и фоновые задачи
Использование AsyncTask для выполнения
в фоновом потоке
На следующем шаге мы должны вызвать и протестировать только что добавленный сетевой код. Однако мы не можем просто вызвать
FlickrFetchr.getURL(String)
прямо из
PhotoGalleryFragment
. Вместо этого необходимо создать фоновый про- граммный поток и выполнить код в нем.
Для работы с фоновыми потоками проще всего использовать вспомогательный класс с именем
AsyncTask
AsyncTask создает фоновый поток и выполняет в нем код, содержащийся в методе doInBackground(…)
В файле
PhotoGalleryFragment.java добавьте в конце
PhotoGalleryFragment новый вну- тренний класс с именем
FetchItemsTask
. Переопределите метод
AsyncTask.doIn-
Background(…)
для получения данных с сайта и их регистрации в журнале. Затем используйте новый класс внутри
PhotoGalleryFragment.onCreate(…)
Листинг 26.5. Реализация AsyncTask (PhotoGalleryFragment.java)
public class PhotoGalleryFragment extends Fragment {
private static final String TAG = "PhotoGalleryFragment";
GridView mGridView;
private class FetchItemsTask extends AsyncTask {
@Override
protected Void doInBackground(Void... params) {
try {
String result = new FlickrFetchr().getUrl("https://www.google.com");
Log.i(TAG, "Fetched contents of URL: " + result);
} catch (IOException ioe) {
Log.e(TAG, "Failed to fetch URL: ", ioe);
}
return null;
}
}
}
Затем в методе
PhotoGalleryFragment.onCreate(…)
вызовите метод execute()
для нового экземпляра
FetchItemsTask
Листинг 26.6. Реализация AsyncTask (PhotoGalleryFragment.java)
public class PhotoGalleryFragment extends Fragment {
private static final String TAG = "PhotoGalleryFragment";
GridView mGridView;
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);

Главный программный поток
427
new FetchItemsTask().execute();
}
}
Вызов execute()
активизирует класс
AsyncTask
, который запускает свой фоновый поток и вызывает doInBackground(…)
. Выполните свой код и вы увидите, что в LogCat появляется разметка HTML домашней страницы Google Javascriptlicious — при- мерно так, как показано на рис. 26.5.
Рис. 26.5. Разметка HTML от Google в LogCat
Теперь, когда мы создали фоновый поток и выполнили в нем сетевой код, давайте поближе познакомимся с программным потоками в Android.
Главный программный поток
Сетевые взаимодействия не происходят моментально. Веб-серверу может потре- боваться одна-две секунды на ответ, а загрузка файла может занять еще больше времени. Из-за продолжительности сетевых операций Android запрещает их вы- полнение в главном программном потоке, начиная с Honeycomb и последующих версий Android. Если вы попытаетесь нарушить это ограничение, Android выдает исключение
NetworkOnMainThreadException
. Почему? Чтобы понять это, необходимо понимать, что такое программный поток (thread), что собой представляет главный программный поток приложения и что он делает.
Программным потоком
(thread) называется отдельная последовательность вы- полнения программы. Жизненный цикл каждого приложения Android начинается с главного потока. Однако главный поток не является заранее определенной после- довательностью действий. Он в бесконечном цикле ожидает событий, иницииро- ванных пользователем или системой, и выполняет код как реакцию на эти события по мере их возникновения.
Представьте, что ваше приложение — огромный обувной магазин, и у вас всего один работник: Флэш
1
. В магазине необходимо многое делать для того, чтобы покупатели
1
Персонаж комиксов, наделенный способностью к сверхъестественно быстрым перемещени- ям. — Примеч. перев.

428
Глава 26. HTTP и фоновые задачи остались довольны — расставлять товары на полках, приносить обувь для примерки, определять размер ноги покупателя. С таким продавцом, как Флэш, все делается своевременно, хотя всю работу выполняет всего один человек.
События
Поток
(выполнение кода)
Готово
Главный поток
(от Android или от пользователя)
(обработка события)
Рис. 26.6. Обычные потоки и главный поток
Чтобы эта ситуация работала, Флэш не может тратить слишком много времени на что-то одно. Что, если часть товара будет распродана? Кому-то придется потратить много времени за беседой по телефону, заказывая у поставщика новую партию.
Пока Флэш будет занят, покупатели начнут сердиться.
Флэш — аналог главного потока вашего приложения. Он выполняет весь код, об- новляющий пользовательский интерфейс. В частности, сюда относится код реак- ции на различные события пользовательского интерфейса — запуск активностей, нажатия кнопок и т. д. (Поскольку все события тем или иным образом связаны с пользовательским интерфейсом, главный поток иногда называется потоком пользовательского интерфейса, или UI-потоком.)
Цикл событий обеспечивает последовательное выполнение кода пользовательского интерфейса. Он гарантирует, что операции не будут «перебегать дорогу» друг другу, одновременно обеспечивая своевременное выполнение кода. Таким образом, весь написанный вами код (за исключением кода, написанного для
AsyncTask
) выпол- нялся в главном потоке.
Кроме главного потока
Сетевые операции можно сравнить с телефонным звонком поставщику обуви: они занимают много времени по сравнению с другими задачами. В это время пользова- тельский интерфейс будет полностью парализован, что может привести к ошибке
ANR (Application Not Responding). Эта ошибка происходит тогда, когда система мониторинга Android обнаруживает, что главный поток не среагировал на важное событие — такое, как нажатие кнопки
Back
. Для пользователя это выглядит так.

Загрузка XML из Flickr
429
Рис. 26.7. Приложение не отвечает
Вот почему в Android, начиная с версии Honeycomb, запрещено выполнение се- тевых операций в главном потоке. В обувном магазине вы бы решили проблему вполне естественным образом — наймом второго работника, который будет звонить поставщику. Похожее решение используется и в Android — вы создаете фоновый поток и выполняете сетевые операции из него.
А как проще всего работать с фоновым потоком? При помощи
AsyncTask
Позднее в этой главе вы увидите, на что еще способен класс
AsyncTask
. Но прежде чем изучать его возможности, давайте выполним какую-нибудь реальную работу в сетевом коде.
Загрузка XML из Flickr
Flickr предлагает удобный XML API. Вся необходимая информация доступна в до- кументации по адресу www.flickr.com/services/api/. Загрузите ее в своем браузере и найдите список
Request
Formats
. Мы будем использовать простейший формат —
REST, соответственно конечной точкой API становится адрес https://api.flickr.com/
services/rest/
. Вы можете вызывать методы, которые Flickr предоставляет в этой конечной точке.
Вернитесь к главной странице документации API и найдите список
API
Methods
. Про- крутите его до раздела photos и найдите элемент flickr.photos.getRecent
. Щелкните на нем; в открывшейся документации говорится, что этот метод «Возвращает список

430
Глава 26. HTTP и фоновые задачи последних общедоступных фотографий, отправленных на flickr». Это именно то, что нам нужно для PhotoGallery.
Единственным обязательным параметром метода getRecent является ключ API.
Чтобы получить ключ API, обратитесь по адресу https://www.flickr.com/services/
api/
и проследуйте по ссылке
API
keys
. Для входа вам понадобится идентификатор
Yahoo. После входа зарегистрируйте новый некоммерческий ключ API; обычно эта процедура занимает несколько секунд. Ваш ключ API будет выглядеть примерно так: 4f721bgafa75bf6d2cb9af54f937bb70.
После получения ключа вам остается лишь обратиться с запросом к веб-службе Flickr.
Используйте GET-запрос по адресу https://api.flickr.com/services/rest/?method=flickr.
photos.getRecent&api_key=xxx
Переходим к программированию. Начнем с добавления нескольких констант в FlickrFetchr.
Листинг 26.7. Добавление констант (FlickrFetchr.java)
public class FlickrFetchr {
public static final String TAG = "FlickrFetchr";
private static final String ENDPOINT = "https://api.flickr.com/services/rest/";
private static final String API_KEY = "yourApiKeyHere";
private static final String METHOD_GET_RECENT = "flickr.photos.getRecent";
private static final String PARAM_EXTRAS = "extras";
private static final String EXTRA_SMALL_URL = "url_s";
Эти константы определяют конечную точку, имя метода, ключ API и один до- полнительный параметр с именем extras и значением url_s
. Задание дополнения url_s приказывает Flickr включить URL уменьшенной версии изображения, если она доступна.
Используйте эти константы для написания метода, который строит соответствую- щий URL-адрес запроса и загружает его содержимое.
Листинг 26.8. Добавление метода fetchItems() (FlickrFetchr.java)
public class FlickrFetchr {
String getUrl(String urlSpec) throws IOException {
return new String(getUrlBytes(urlSpec));
}
public void fetchItems() {
try {
String url = Uri.parse(ENDPOINT).buildUpon()
.appendQueryParameter("method", METHOD_GET_RECENT)
.appendQueryParameter("api_key", API_KEY)
.appendQueryParameter(PARAM_EXTRAS, EXTRA_SMALL_URL)
.build().toString();
String xmlString = getUrl(url);

Загрузка XML из Flickr
431
Log.i(TAG, "Received xml: " + xmlString);
} catch (IOException ioe) {
Log.e(TAG, "Failed to fetch items", ioe);
}
}
}
Здесь мы используем класс
Uri.Builder для построения полного URL-адреса для
API-запроса к Flickr.
Uri.Builder
— вспомогательный класс для создания пара- метризованных URL-адресов с правильным кодированием символов. Метод
Uri.
Builder.appendQueryParameter(String,String)
автоматически кодирует строки запросов.
Наконец, измените код
AsyncTask в
PhotoGalleryFragment для вызова нового метода fetchItems()
Листинг 26.9. Вызов fetchItems() (PhotoGalleryFragment.java)
private class FetchItemsTask extends AsyncTask {
@Override protected Void doInBackground(Void... params) {
try {
String result = new FlickrFetchr().getUrl("https://www.google.com");
Log.i(TAG, "Fetched contents of URL: " + result);
} catch (IOException ioe) {
Log.e(TAG, "Failed to fetch URL: ", ioe);
}
new FlickrFetchr().fetchItems();
return null;
}
}
Запустите приложение PhotoGallery. В LogCat отображается полноценная, насто- ящая разметка XML.
Рис. 26.8. Разметка XML в Flickr

432
1   ...   35   36   37   38   39   40   41   42   ...   55

перейти в каталог файлов
Образовательный портал Как узнать результаты егэ Стихи про летний лагерь 3агадки для детей
Образовательный портал Как узнать результаты егэ Стихи про летний лагерь 3агадки для детей