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

Программирование под 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
страница45 из 55
КаталогОбразовательный портал Как узнать результаты егэ Стихи про летний лагерь 3агадки для детей
Образовательный портал Как узнать результаты егэ Стихи про летний лагерь 3агадки для детей
1   ...   41   42   43   44   45   46   47   48   ...   55
Глава 29. Фоновые службы будет завершен,
AlarmManager будет выдавать интенты, снова и снова запуская
PollService
(Конечно, такое поведение в высшей степени возмутительно. Возможно, вам стоит удалить приложение, пока ситуация не будет исправлена.)
PendingIntent
Давайте поближе познакомимся с
PendingIntent
PendingIntent представляет собой объект-маркер. Когда вы получаете такой объект вызовом
PendingIntent.getSer- vice(…)
, вы тем самым говорите ОС: «Пожалуйста, запомни, что я хочу отправлять этот интент вызовом startService(Intent)
». Позднее вы можете вызвать send()
для
PendingIntent
, и ОС отправит изначально упакованный интент — точно так, как вы приказали.
А лучше всего здесь то, что если передать маркер
PendingIntent другой стороне и эта сторона его использует, маркер будет отправлен от имени вашего приложе-
ния
. А поскольку объект
PendingIntent существует в ОС, вы сохраняете полный контроль над ним. Например, вы можете (просто из вредности) предоставить ко- му-нибудь объект
PendingIntent и немедленно отменить его, так что вызов send()
ничего не сделает.
Если вы запросите
PendingIntent дважды с одним интентом, то получите тот же
PendingIntent
. Например, таким образом можно проверить существование
PendingIntent или отменить ранее выданный объект
PendingIntent
Управление сигналами с использованием PendingIntent
Для каждого объекта
PendingIntent можно зарегистрировать только один сигнал.
Именно так работает вызов setServiceAlarm(boolean)
при ложном значении isOn
: он вызывает
AlarmManager.cancel(PendingIntent)
для отмены сигнала, связанного с вашим объектом
PendingIntent
, а потом отменяет
PendingIntent
Так как
PendingIntent также удаляется при отмене сигнала, вы можете проверить, существует ли
PendingIntent
, чтобы узнать, активен сигнал или нет. Эта операция выполняется передачей флага
PendingIntent.FLAG_NO_CREATE
вызову
PendingIn- tent.getService(…)
. Флаг говорит, что если объект
PendingIntent не существует, то вместо его создания следует вернуть null
Напишите новый метод isServiceAlarmOn(Context)
, использующий флаг
Pendin- gIntent.FLAG_NO_CREATE
для проверки сигнала.
Листинг 29.10. Добавление метода isServiceAlarmOn() (PollService.java)
public static void setServiceAlarm(Context context, boolean isOn) {
}
public static boolean isServiceAlarmOn(Context context) {
Intent i = new Intent(context, PollService.class);
PendingIntent pi = PendingIntent.getService(

Управление сигналом
483
context, 0, i, PendingIntent.FLAG_NO_CREATE);
return pi != null;
}
Так как этот объект
PendingIntent используется только для установки сигнала, null вместо
PendingIntent означает, что сигнал не установлен.
Управление сигналом
Теперь, когда мы можем включать и отключать сигнал (а также определять, включен ли он), давайте добавим интерфейс для его включения и выключения. Добавьте в файл menu/fragment_photo_gallery.xml новую команду меню.
Листинг 29.11. Переключение режима опроса (menu/fragment_photo_gallery.xml)

android:title="@string/search"
android:icon="@android:drawable/ic_menu_search"
android:showAsAction="ifRoom"
android:actionView
/>
android:title="@string/clear_search"
android:icon="@android:drawable/ic_menu_close_clear_cancel"
android:showAsAction="ifRoom"
/>

android:title="@string/start_polling"
android:showAsAction="ifRoom"
/>

Затем необходимо добавить несколько новых строк — одну для начала опроса, одну для завершения опроса. (Позднее нам понадобится еще пара строк для оповещений на панели состояния; добавим их сейчас.)
Листинг 29.12. Добавление строк для режима опроса (res/values/strings.xml)

Search
Clear Search
Poll for new pictures
Stop polling
New PhotoGallery Pictures
You have new pictures in PhotoGallery.
string>

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

484
Глава 29. Фоновые службы
Листинг 29.13. Реализация команды переключения режима опроса
(PhotoGalleryFragment.java)
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
setHasOptionsMenu(true);
updateItems();
PollService.setServiceAlarm(getActivity(), true);
mThumbnailThread = new ThumbnailDownloader(new Handler());
}
@Override
@TargetApi(11)
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_search:
case R.id.menu_item_clear:
updateItems();
return true;
case R.id.menu_item_toggle_polling:
boolean shouldStartAlarm = !PollService.isServiceAlarmOn(getActivity());
PollService.setServiceAlarm(getActivity(), shouldStartAlarm);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
Теперь вы сможете включать и отключать сигнал.
А как насчет команды меню?
Обновление элемента командного меню
Обычно командное меню достаточно просто заполнить. Однако в некоторых си- туациях требуется обновлять его элементы в соответствии с текущим состоянием приложения.
Командные меню не заполняются при каждом использовании, даже если это команд- ные меню «старого стиля». Если вам потребуется обновить содержимое элемента командного меню, поместите соответствующий код в onPrepareOptionsMenu(Menu)
Этот метод вызывается при каждом определении конфигурации меню, а не только при его изначальном создании.

Управление сигналом
485
Добавьте реализацию onPrepareOptionsMenu(Menu)
, которая проверяет, установлен ли сигнал, а затем изменяет текст menu_item_toggle_polling
, чтобы в меню ото- бражался соответствующий текст.
Листинг 29.14. Добавление метода onPrepareOptionsMenu(Menu) (PhotoGalleryFragment.java)
@Override public boolean onOptionsItemSelected(MenuItem item) {
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
MenuItem toggleItem = menu.findItem(R.id.menu_item_toggle_polling);
if (PollService.isServiceAlarmOn(getActivity())) {
toggleItem.setTitle(R.string.stop_polling);
} else {
toggleItem.setTitle(R.string.start_polling);
}
}
На устройствах с версией до 3.0 этот метод вызывается при каждом отображении меню. Это гарантирует, что элементы меню всегда будут содержать правильный текст. Если хотите удостовериться в этом, запустите приложение PhotoGallery в эмуляторе с версией до 3.0.
Однако для версий после 3.0 этого недостаточно. Панель действий не об- новляется автоматически. Вам придется специально приказать ей вызвать onPrepareOptionsMenu(Menu)
и обновить элементы вызовом
Activity.invalida- teOptionsMenu()
Добавьте следующий код в onOptionsItemSelected(MenuItem)
, чтобы приказать устройствам после 3.0 обновить свои панели действий.
Листинг 29.15. Командное меню объявляется недействительным (PhotoGalleryFragment.java)
@Override
@TargetApi(11)
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_toggle_polling:
boolean shouldStartAlarm = !PollService.isServiceAlarmOn(getActivity());
PollService.setServiceAlarm(getActivity(), shouldStartAlarm);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
getActivity().invalidateOptionsMenu();
return true;
default:
return super.onOptionsItemSelected(item);
}
}

486
Глава 29. Фоновые службы
После этого код будет отлично работать в новом эмуляторе 4.2.
И все же в приложении кое-чего не хватает.
Оповещения
Служба успешно работает в фоновом режиме. Однако пользователь об этом понятия не имеет, так что пользы от нее будет немного.
Когда службе требуется передать какую-то информацию пользователю, для этого она почти всегда использует оповещения (notifications) — элементы, которые по- являются на выдвижной панели оповещений, которую пользователь вызывает, проводя пальцем вниз от верха экрана.
Чтобы опубликовать оповещение, необходимо сначала создать объект
Notification
Объекты
Notification создаются с использованием объектов-построителей — по- добно тому, как это делалось с
AlertDialog из главы 12. Как минимум объект
No- tification должен иметь:

текст бегущей строки
, отображаемый на панели состояния при первом по- явлении оповещения;

значок
, отображаемый на панели состояния после исчезновения бегущей строки;

представление
, отображаемое на выдвижной панели оповещений для вывода оповещения;

объект PendingIntent
, срабатывающий при нажатии на оповещении на выдвиж- ной панели.
После того как объект
Notification будет создан, его можно отправить вызовом метода notify(int,
Notification)
для системной службы
NotificationManager
Чтобы служба
PollService оповещала пользователя о появлении нового результата, добавьте код из листинга 29.16. Этот код создает объект
Notification и вызывает
NotificationManager.notify(int,
Notification)
Листинг 29.16. Добавление оповещения (PollService.java)
@Override public void onHandleIntent(Intent intent) {
String resultId = items.get(0).getId();
if (!resultId.equals(lastResultId)) {
Log.i(TAG, "Got a new result: " + resultId);
Resources r = getResources();
PendingIntent pi = PendingIntent
.getActivity(this, 0, new Intent(this, PhotoGalleryActivity.class), 0);
Notification notification = new NotificationCompat.Builder(this)
.setTicker(r.getString(R.string.new_pictures_title))
.setSmallIcon(android.R.drawable.ic_menu_report_image)
.setContentTitle(r.getString(R.string.new_pictures_title))

Управление сигналом
487
.setContentText(r.getString(R.string.new_pictures_text))
.setContentIntent(pi)
.setAutoCancel(true)
.build();
NotificationManager notificationManager = (NotificationManager)
getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(0, notification);
}
prefs.edit()
.putString(FlickrFetchr.PREF_LAST_RESULT_ID, resultId)
.commit();
}
Пройдемся по этому коду сверху вниз. Сначала мы задаем текст бегущей строки и значок вызовами setTicker(CharSequence)
и setSmallIcon(int)
После этого задается внешний вид оповещения на самой выдвижной панели.
Можно полностью определить внешний вид оповещения, но проще использовать стандартное оформление со значком, заголовком и текстовой областью. Для значка будет использоваться значение из setSmallIcon(int)
. Заголовок и текст задаются вызовами setContentTitle(CharSequence)
и setContentText(CharSequence)
соот- ветственно.
Теперь необходимо указать, что происходит при нажатии на оповещении. Как и в случае с
AlarmManager
, для этого используется
PendingIntent
. Объект
Pendin- gIntent
, передаваемый setContentIntent(PendingIntent)
, будет запускаться при нажатии пользователем на вашем оповещении на выдвижной панели. Вызов setAutoCancel(true)
слегка изменяет это поведение: с этим вызовом оповещение при нажатии также будет удаляться с выдвижной панели оповещений.
Код завершается вызовом
NotificationManager.notify(…)
. Передаваемый целочис- ленный параметр содержит идентификатор оповещения, уникальный в границах приложения. Если вы отправите второе оповещение с тем же идентификатором, оно заменит последнее оповещение, отправленное с этим идентификатором. Так реализуются индикаторы прогресса и другие динамические визуальные эффекты.
Собственно, это все. Весь пользовательский интерфейс для фонового опроса! Ког- да вы убедитесь в том, что все работает правильно, замените константу интервала опроса более разумным значением.
Листинг 29.17. Изменение периодичности опроса (PollService.java)
public class PollService extends IntentService {
private static final String TAG = "PollService";
public static final int POLL_INTERVAL = 1000 * 15; // 15 секунд
public static final int POLL_INTERVAL = 1000 * 60 * 5; // 5 минут
public PollService() {
super(TAG);
}

488
Глава 29. Фоновые службы
Для любознательных: подробнее о службах
Мы рекомендуем использовать
IntentService для большинства реализаций служб.
Если паттерн
IntentService не подходит для вашей архитектуры, вам придется поближе познакомиться со службами для составления собственной реализации.
Приготовьтесь, при изучении служб приходится учитывать множество подроб- ностей и нюансов.
Что делают (и чего не делают) службы
Служба представляет собой компонент приложения, предоставляющий обратные вызовы жизненного цикла (в этом она похожа на активность). Эти обратные вы- зовы даже выполняются в главном потоке без каких-либо усилий с вашей стороны, как и в случае с активностью.
В своем исходном состоянии служба не выполняет никакой код в фоновом потоке.
Это главная причина, по которой мы рекомендуем
IntentService
. Для большинства нетривиальных служб потребуется тот или иной фоновый поток, а
IntentService ав- томатически управляет шаблонным кодом, необходимым для достижения этой цели.
Давайте посмотрим, какие обратные вызовы жизненного цикла предоставляет служба.
Жизненный цикл службы
Жизненный цикл службы, запущенной startService(Intent)
, весьма прост. Ниже перечислены методы обратного вызова жизненного цикла.

onCreate(…)
— вызывается при создании службы.

onStartCommand(Intent,int,int)
— вызывается каждый раз, когда компонент запускает службу вызовом startService(Intent)
. Два целочисленных параметра содержат набор флагов и идентификатор запуска. Флаги указывают, является ли интент повторной отправкой ранее доставленного интента, или повторной попыткой после неудачи при доставке. Идентификатор запуска отличается при разных вызовах onStartCommand(Intent,int,int)
, поэтому по нему можно отличить эту команду от других.

onDestroy()
— вызывается, когда дальнейшее существование службы не требу- ется. Часто происходит после остановки службы.
Остается один вопрос: как происходит остановка службы? Это можно сделать раз- ными способами в зависимости от типа службы. Тип службы определяется значе- нием, возвращаемым методом onStartCommand(…)
; возможные значения —
Service.
START_NOT_STICKY
,
START_REDELIVER_INTENT
или
START_STICKY
Незакрепляемые службы
IntentService является незакрепляемой (non-sticky) службой, поэтому начнем с нее. Незакрепляемая служба останавливается, когда сама служба сообщает

Для любознательных: подробнее о службах
489
о завершении своей работы. Чтобы сделать свою службу незакрепляемой, верните
START_NOT_STICKY
или
START_REDELIVER_INTENT
Чтобы сообщить Android о завершении работы службы, вызовите метод stopSelf()
или stopSelf(int)
. Первый метод, stopSelf()
, является безусловным. Он всегда останавливает службу независим от того, сколько раз был вызван метод onStart-
Command(…)
IntentService использует вместо него stopSelf(int)
. Этот метод получает иденти- фикатор запуска, полученный в onStartCommand(…)
. Служба останавливается только в том случае, если этот идентификатор является самым последним из полученных идентификаторов запуска (так работает внутренняя реализация
IntentService
).
Чем же отличаются
START_NOT_STICKY
и
START_REDELIVER_INTENT
? Поведени- ем службы, если системе потребуется завершить ее преждевременно. Служба
START_NOT_STICKY
просто прекращает существование и уходит в никуда. Служба
START_REDELIVER_INTENT
, напротив, попытается запуститься позднее, когда ресурсы не будут так ограничены.
Выбор между
START_NOT_STICKY
и
START_REDELIVER_INTENT
определяется важностью этой операции для вашего приложения. Если служба не критична, выберите режим
START_NOT_STICKY
. В PhotoGallery служба запускается по сигналу. Пропажа одного вызова не критична, поэтому мы выбираем
START_NOT_STICKY
. Такое поведение используется по умолчанию для
IntentService
. Чтобы переключиться на режим
START_REDELIVER_INTENT
, вызовите
IntentService.setIntentRedelivery(true)
Закрепляемые службы
Закрепляемая
(sticky) служба остается запущенной, пока кто-то находящийся вне службы не прикажет ей остановиться, вызвав метод
Context.stopService(Intent)
Чтобы сделать службу закрепляемой, верните значение
START_STICKY
После запуска закрепляемая служба остается «включенной», пока компонент не вызовет
Context.stopService(Intent)
. Если службу по какой-то причине потре- буется уничтожить, она будет снова перезапущена с передачей onStartCommand(…)
null
-интента.
Закрепляемый режим хорошо подходит для долгоживущих служб (например, проигрывателя музыки), которые должны работать до тех пор, пока пользователь не прикажет им остановиться. Впрочем, даже в этом случае стоит рассмотреть альтернативную архитектуру с использованием незакрепляемых служб. Управлять закрепляемыми службами неудобно, потому что трудно определить, была ли уже запущена служба.
Привязка к службам
Также существует возможность привязки (binding) к службе при помощи метода bi ndService(Intent,ServiceConnection,int)
. Привязка к службе — механизм подклю- чения к службе и непосредственного вызова ее методов. Привязка осуществляется

490
1   ...   41   42   43   44   45   46   47   48   ...   55

перейти в каталог файлов

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

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