name="AppTheme"
parent="
Theme.AppCompat.Light.DarkActionBar
">
name="colorPrimary">@color/colorPrimary
name="colorPrimaryDark">@color/colorPrimaryDark
name="colorAccent">@color/colorAccent
name="AppTheme"
parent="Theme.AppCompat.Light.DarkActionBar">
name="StrongBeatBoxButton"
parent="@style/BeatBoxButton">
- bold
name="AppTheme"
parent="Theme.AppCompat.Light.DarkActionBar">
...
name="AppTheme"
parent="Theme.AppCompat
.Light.DarkActionBar
">
...
name="AppTheme"
parent="Theme.AppCompat">
name="colorPrimary">@color/
colorPrimaryred<
/item>
name="colorPrimaryDark">@color/
colorPrimary
Darkdark_red
name="colorAccent">@color/
colorAccentgray
item>
name="Theme.AppCompat"
parent="Base.Theme.AppCompat" />
Тема
Theme.AppCompat
наследует
атрибуты
от
Base.Theme.AppCompat
. Интересно, что
Theme.AppCompat
не
переопределяет никакие атрибуты, а только содержит ссылку
на своего родителя.
Щелкните мышью по
Base.Theme.AppCompat
с нажатой
клавишей (
Ctrl
). Android Studio сообщит, что тема уточняется
по ресурсам. Существует несколько разных версий этой темы в
зависимости от используемой версии Android.
name="Base.Theme.AppCompat"
parent="Base.V7.Theme.AppCompat">
name="Base.V7.Theme.AppCompat"
parent="Platform.AppCompat">
-
androidx.appcompat.app.AppCompatVie
wInflater
- false
- true
name="windowActionBarOverlay">false
name="Platform.AppCompat"
parent="android:Theme.Holo">
name="android:windowNoTitle">true
name="android:windowActionBar">false
- ?
attr/buttonBarStyle
- ?
attr/buttonBarButtonStyle
name="android:borderlessButtonStyle">?
attr/borderlessButtonStyle
...
name="Platform.AppCompat"
parent="android:Theme.Holo">
...
...
name="android:windowBackground">@color/backgrou
nd_material_dark
name="AppTheme"
parent="Theme.AppCompat">
- @color/red
name="colorPrimaryDark">@color/dark_red
- @color/gray
-
name="android:windowBackground">@color/soothing
_blue
name="Base.V7.Theme.AppCompat"
parent="Platform.AppCompat">
...
name="buttonStyle">@style/Widget.AppCompat.Butt
on
name="buttonStyleSmall">@style/Widget.AppCompat
.Button.Small
...
name="Widget.AppCompat.Button"
parent="Base.Widget.AppCompat.Button"/>
Стиль
Widget.AppCompat.Button
самостоятельно никакие
атрибуты не определяет. Чтобы просмотреть его содержимое,
перейдите к его родителю. Вы увидите, что базовый стиль
существует
в
двух
версиях.
Выберите
версию
values/values.xml
.
name="Base.Widget.AppCompat.Button"
parent="android:Widget">
name="android:background">@drawable/abc_btn_def
ault_mtrl_shape
- ?
android:attr/textAppearanceButton
- 48dip
- 88dip
- true
- true
name="android:gravity">center_vertical|center_h
orizontal
name="AppTheme"
parent="Theme.AppCompat">
name="colorPrimary">@color/red
name="AppTheme"
parent="Theme.AppCompat">
name="colorPrimary">@color/red
name="colorPrimaryDark">@color/dark_red
name="colorAccent">@color/gray
name="android:windowBackground">@color/soothing
_blue
name="BeatBoxButton"
parent="Widget.AppCompat.Button">
name="android:background">@color/dark_bluem>
name="Platform.AppCompat"
parent="android:Theme.Holo">
...
name="AppTheme"
parent="Theme.AppCompat">
...
name="BeatBoxButton"
parent="Widget.AppCompat.Button">
name="AppTheme"
parent="Theme.AppCompat">
...
name="BeatBoxButton"
parent="Widget.AppCompat.Button">
name="android:background">@drawable/button_beat
_box_normal
-
name="android:background">@drawable/button_beat
_box
(PhotoGalleryFragment.kt)
private const val TAG = "PhotoGalleryFragment"
private const val POLL_WORK = "POLL_WORK"
class PhotoGalleryFragment : Fragment() {
...
override fun onOptionsItemSelected(item:
MenuItem): Boolean {
return when (item.itemId) {
R.id.menu_item_clear -> {
photoGalleryViewModel.fetchPhot
os("")
true
}
val isPolling =
QueryPreferences.isPolling(requireContext())
if (isPolling) {
WorkManager.getInstance().c
ancelUniqueWork(POLL_WORK)
QueryPreferences.setPolling
(requireContext(), false)
} else {
val constraints =
Constraints.Builder()
.setRequiredNetworkType
(NetworkType.UNMETERED)
.build()
val periodicRequest =
PeriodicWorkRequest
.Builder(PollWorker::cl
ass.java, 15, TimeUnit.MINUTES)
.setConstraints(constra
ints)
.build()
WorkManager.getInstance().e
nqueueUniquePeriodicWork(POLL_WORK,
ExistingPeriodicWorkPol
icy.KEEP,
periodicRequest)
QueryPreferences.setPolling
(requireContext(), true)
}
activity?.invalidateOptionsMenu
()
return true
}
else
->
super.onOptionsItemSelected(item)
}
}
...
}
Обратите внимание на блок
else
, который вы добавили
здесь. Если работник в данный момент не запущен, то мы
назначим новый запрос на работу с
WorkManager
. В этом
случае вы используете
PeriodicWorkRequest
, чтобы
заставить вашего работника самого запланировать себя через
некоторый интервал. В запросе используется конструктор,
такой как
OneTimeWorkRequest
, который вы использовали
ранее. Конструктору нужен класс
Worker
, а также интервал
выполнения.
Если вы считаете, что 15 минут — это слишком долго, то вы
правы. Однако если вы установите меньшее значение
интервала, то обнаружите, что работник все равно выполняется
с 15-минутным интервалом. Это минимальный интервал,
допустимый для
PeriodicWorkRequest
, чтобы система не
была постоянно привязана к выполнению одного и того же
рабочего запроса. Это экономит системные ресурсы и ресурс
батареи.
Конструктор
PeriodicWorkRequest
принимает
ограничения, как и одноразовый запрос, поэтому вы можете
добавить ограничение на работу с сетью с измерением
трафика. Когда вы хотите спланировать запрос на работу, вы
используете класс
WorkManager
, но на этот раз вы используете
функцию
enqueueUniquePeriodicWork(...)
. Эта функция
принимает имя типа
String
, политику и ваш рабочий запрос.
Имя позволяет однозначно идентифицировать запрос, что
полезно, если вы захотите его отменить.
Политика работы подсказывает менеджеру, что делать, если
вы уже запланировали запрос на работу с определенным
именем. В этом случае вы используете опцию
KEEP
, которая
отказывается от нового запроса в пользу уже существующего.
Другая опция —
REPLACE
, которая, как следует из названия,
заменяет существующий запрос на новый.
Если работник уже запущен, то вам необходимо сообщить
WorkManager
об отмене запроса на работу. В этом случае для
удаления периодического запроса на работу вызывается
функция
cancelUniqueWork(...)
с именем
POLL_WORK
.
Запустите приложение. Вы увидите новый пункт меню для
переключения опроса. Если вы не хотите ждать 15 минут
интервала, вы можете отключить опрос, подождать несколько
секунд, а затем включить его снова.
Теперь PhotoGallery может автоматически держать
пользователя в курсе последних изображений, даже если
приложение не запущено. Но есть одна проблема: пользователь
будет получать уведомления в любое время, когда приходят
новые изображения, — даже если приложение уже запущено.
Это нежелательно, потому что отвлекает внимание
пользователя от вашего приложения. Кроме того, если
пользователь нажмет на уведомление, запустится новый
экземпляр
PhotoGalleryActivity
, который будет добавлен в
обратный стек вашего приложения.
Вы исправите это в следующей главе, предотвратив
появление уведомлений во время работы PhotoGallery. При
создании этих обновлений вы узнаете, как прослушивать
сообщения от интентов трансляции и как работать с такими
интентами с помощью приемника трансляции.
Do'stlaringiz bilan baham: |