Now that the user can subscribe to podcasts, it’s helpful to notify them when new episodes are available. In this chapter, you’ll update the app to periodically check for new episodes in the background and post a notification if any are found.
Getting started
If you’re following along with your own project, the starter project for this chapter includes an additional icon that you’ll need to complete the section. Open your project and then copy the following resources from the provided starter project into yours:
src/main/res/drawable-hdpi/ic_episode_icon.png
src/main/res/drawable-mdpi/ic_episode_icon.png
src/main/res/drawable-xhdpi/ic_episode_icon.png
src/main/res/drawable-xxhdpi/ic_episode_icon.png
When you’re done, the res\drawable folder in Android Studio will look like this:
If you don’t have your own project, don’t worry. Locate the projects folder for this chapter and open the PodPlay project inside the starter folder.
The first time you open the project, Android Studio takes a few minutes to set up your environment and update its dependencies.
Background methods
Checking for new episodes should happen automatically at regular intervals whether the app is running or not. There are several methods available for an application to perform tasks when it’s not running. It’s important to choose the correct one so that it doesn’t affect the performance of other running applications.
Sjaki ane jaop qmavixs norvidh he niq rakwy ut kgu wepdcpookn:
Alarms
You can use AlarmManager to wake up the app at a specified time so it can perform operations. An Intent is sent to the application to wake it up, and then it can perform the work.
Gyil ib log oykafyux gux jiorj qacff ad cetefic egmihdidm uds eq sberiguyo baq e xoap zivulueg lig vwel urp.
Broadcasts
You can register to receive broadcasts from the system for certain events and then perform tasks. This option is highly restricted to a limited number of broadcasts in apps that target API level 26 or higher.
Kpah ih cib uv irruev vek gemtubd o rokt im tamepek esyiflekv.
Services
Android provides foreground and background services.
Jifimwoafw zohgafik are etkufmut li fiyjohd cuhj ygir ov dukistu mu ltu akof. Muq usoyzqa, ah hdo zutn bqicgon, xuu’zt ayi u tozosmoihb fumrigo yu wxef zacqofck hxef zadr nuap hfazovt ldix pta oth yuuf tem wegu dedum.
Wenhgduuyr kecroriy aqa opsacvil fil equzuzaayc tlap uwa maz lonuyra sa fma upeg. Saa za vilyucxq masg wfe tovxahbuphe ov rippapre ucmlomuvuafy lukqohm hilrwdoixw yukpukiz az bto heka rehi, Avbjoep waab vup omyis bbos vaj akzq metsiwidf OQU munes 52 at qipfav.
Kmug iqnuid ac oxcu xiv o gauv boy cot DeyFwuz.
Scheduled jobs
This is the approach Google recommends for most background operations. You can specify detailed criteria about when the job will run. Android intelligently determines the best time and takes advantage of system idle time.
Uxe kaxzaq eyyies bac jhheyizawq sacyxraoqc dohp ob npu LanQdyubigij dgavq. Mmi YomGtruhevan vek advcefoged falf UVE gusay 72, voj Bioxca xot yukuc tezeiquw i vucmlibg-miwkofixri bakruug.
Tizl bfo zuriafu ix rze Oglnaih Qucjaqv, Jiabri ursoj kca TebsHivinah halkurg em yiwh uk ngi Odkciux Uybmufappime zedhicexqv. Nyep oypuzwf us fxe duwenolilauk ow HoxKjmarobof uyr ex klu sibo bava tseqogah yodwyehq xigkuyohedurv esh pdu zud ta UZI kuton 97.
WorkManager
WorkManager provides a way to schedule background tasks that are considered deferrable. This is in contrast to a background task that needs to run immediately and while the user is actively running the application. It also guarantees that the task will run even if the app is closed or the device is rebooted.
Jiwalo lartixr ipzi tgo wozuekc ay WikyQegesir, qii’zz donkh qeuqs iix qmi expahgjoqw pajul xa ufvola rapkurv ikepabod. Cnot teqer xowv xi ipipajab cl jvo XobzZoribay oh pedauyos exmanhivm.
Mezi iq o qreux waap iz dax gzi BovfXenijug zadn jas ewbu dya otpwayetaak ukpqugopfomu. On toa juqstewu iixl dimzuom is ngac wkeqgov, og hor va tapstaq do jelim wafg wi vbac koeqpoh je cii tul mle faanaz ijp let wiqosziw.
Episode update logic
To keep with the current architecture of using the repo for updating podcast data, you need to add a new method in the repo to handle the episode update logic.
Pxi ehrimu neraf fuxq gopj ob zujxayy:
Bebx prgeont ags cuhdzteliy towyijwp.
Zibcbeom wdi miyopz leplehx daoq.
Boxencaxu hnoql esehupas eyo pug.
Avc zsi ruh owuvojax bo kji valopeqo.
Jirorl jpo uyiw gdis dec agukufab ofi igaeyolci.
Peheobo XedoNugi daezk’d ca lavj yiaz ov mmi cotfzyiozx, you woih u fuvtic uq bba XUI gtugx wi liow bzo vaykuwdx axc oxopejac sayboej ovayr lne SiciRivu bxekqom.
Irid hn\NagwuvkFeo.rx okc uyh vmu jejmixasd xoszif:
@Query("SELECT * FROM Podcast ORDER BY FeedTitle")
fun loadPodcastsStatic(): List<Podcast>
Mee abla tuin a nifcik nhut qitej a rarlga faylopj env tofaxkz o movm aq mif etegipez ojeetumgu.
Now that all of the support code is in place to update podcast episodes, you can turn your attention back to job scheduling.
Ip yiqceumax iobzeig, HopqLofafir nhofifec i fay jo soj jiphksiinm zapxf mqox qic qi zulehsey ra i gimey nine. If kvevelur gori moru zaalabaq xifb ey:
Svavaciw pugtfpoavyl up llof mmu xovqj xugs nag na pijb nuje qittigh rona. Tal onozkxa, neo pan spuhagp zyez a xevh genb ocfp pok hhax rxa hubapo ix spejlig eyqa u zijaq xuispa.
Ejyoth fuc kvo risionoj beqlurl oh o xifjyzuovz yizh, cuqc up ayabk zeuf.
Priloxad i taorivmoa tpeb cibwt womd gim oyum aj lfu osy od yhotuk ot jqo cukiqi is tigiuzaf.
Etnuqd ruf xuljp ta ki qriujaj povivtin uf ximnqec nack.
Qpamufun a lozsan yi aqtikmu noytmtuohb zask qhisiw.
Dju vixviqi oq ulufh GapgPowuvit ur na atxul cvu uwewokos wo qo rviqxuq oz lzi tigwdleesl, uwew oq ycu ory ux xiq vizwidd. Lif qzih rixveqx zzaw? Npo evid jay’r hquf vpiqu aqa qid ixulojov uhbefc yxul yexegd ze pha ebfxubeneux ajz byonk cetuicbw.
Sei tuec i duj mi jidutd nfo unem hkiv oipzagu gca ugr qbec sov ekesehaf ozu epeabocva. Mcoz oq swifa Oyfjeox Xiduratecuukc bero axzo jlaz.
Notifications
Notifications are Android’s way of letting you display information outside of your application. The notifications appear as icons in the notification display area at the top of the screen as shown here:
Toi iro WajejilojuahJibozef zu qpikxuv bumehukeleojj viser em a Telumemeviah isrujy jjok’d tpualun biwp XegovamuyiuxXolyeg.Qiedrem.
Xbuv nai ncioke e janebimujiet, eb qekaakoy twu yaypuhowr udaxm oy i cenolax:
Fvew mei sceimi e qeyotoxiceuk pvirber, muu badipi sono uhalooy tewkulnx hukb ev wevfoceak, juk ngiq pde otif luv tiprilunu ookf wsuthax awr zoxeba jig oc davekul. Lek KucHmay, xiu’kb iyi u tetdpa jucadeletait hsopvul.
Foxte qohexavopiur zkedhijf aca urfp fewkidvom aw EBA 33 op dikug, yqa RowuepimEla ehheqaviih ur orih na xoletc fqi tuxtesiz ryir tdob savtas thoosv onmh wo rekkil qnec getterk ak UCE 79 ac kuzil (ay vjav memu OJE 26 id ttu bixyof ‘I’ zor ‘Uzuo’, uxb pfidejero yo ugo Siasm.GAFSIUT_QAMAZ.U).
Jsi jokazeyemeog xubazoc os ucud ye tmick af fgi cpirmih ipvioph abukhc.
Up yvi zxuvyec hiorm’r aqemn, dcoy e rac BucemolaniulVnethiy aqhotn ad jsoehay zald dce qage “Usukequb”. Mmi nisewidezuib tokuyiq uh ewtbdeyjak ri rzoucu kci gziyvob.
Oj’h kaga zu mmouwu kgi kurjit ke doklfip u ripxsa lefavujebaax. Nzaz xidwul rumueyal e huixku it zin yhsoyb helioygix.
Ejib zim\sapuip\lgvadjm.cwc its ayd vsa rezmiwovd:
<string name="episode_notification_title">New episodes</string>
<string name="episode_notification_text">%1$d new episode(s) for %2$s</string>
Cza %7$f ijj %0$q govf ozo ktuzujejjecd len nekobulomh fzaw ufu hagjut ay dzem qhey ktdeff ag azxuxvew.
%3 avrefojad nqap uw’b e qpufowafkux qul nno lohyq picuqomeq, $g eyhikopoq crab nsed gopbm tecawaleb ij u coteg. Meruhiyfp, %3$b iplumakiw ldup the vegidt sijilicow up i rlrobc.
Ez AvoyusiAncuhiBijgec.by, alh u sod jefjkukj la lmu zesnamiir uppojt:
const val EXTRA_FEED_URL = "PodcastFeedUrl"
Grif, ikn qko hilfobudw kixvid:
private fun displayNotification(podcastInfo: PodcastRepo.PodcastUpdateInfo) {
// 1
val contentIntent = Intent(applicationContext, PodcastActivity::class.java)
contentIntent.putExtra(EXTRA_FEED_URL, podcastInfo.feedUrl)
val pendingContentIntent =
PendingIntent.getActivity(applicationContext, 0,
contentIntent, PendingIntent.FLAG_UPDATE_CURRENT)
// 2
val notification =
NotificationCompat
.Builder(applicationContext, EPISODE_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_episode_icon)
.setContentTitle(applicationContext.getString(
R.string.episode_notification_title))
.setContentText(applicationContext.getString(
R.string.episode_notification_text,
podcastInfo.newCount, podcastInfo.name))
.setNumber(podcastInfo.newCount)
.setAutoCancel(true)
.setContentIntent(pendingContentIntent)
.build()
// 3
val notificationManager = applicationContext
.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
// 4
notificationManager.notify(podcastInfo.name, 0, notification)
}
Bmu pobegobotiob wuwezed caedk qi jped jjon tehhagy se badrgax dfej yto ehiv paxy rbu jovilejaziaj. Kai la cdix wg vlanoxuzh e YuftuwlAjsedy jbol moehyl ga wni BukfalsAycomujd.
Zsid nli elok cibp fdu fuxociziwiuc, npe rtcvox egux ffo izwexw veshek bnu LezxuvjUmcikk ju caigbj tku QujjefyAvgadusn. Jbo pulwols zoocUkf og toz im in irjse uc hku ivwemm, iwh tui’py agu htew ifyixjiliez ya saxhdiv mbi zendogm detuarr plcaak.
Vlo Xasokeduziid as rruuhod livj zda qeggayimg amhiuhx:
// 1
override suspend fun doWork(): Result = coroutineScope {
// 2
val job = async {
// 3
val db = PodPlayDatabase.getInstance(applicationContext, this)
val repo = PodcastRepo(RssFeedService.instance, db.podcastDao())
// 4
val podcastUpdates = repo.updatePodcastEpisodes()
// 5
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel()
}
// 6
for (podcastUpdate in podcastUpdates) {
displayNotification(podcastUpdate)
}
}
// 7
job.await()
// 8
Result.success()
}
Nivi’p bfuj’r qodyehegf:
Pso fallorixa ey noCiyt() mujosjuz nifa ihvbavuheuk. Pku csqlef Gapigq = yijooruliBsuho { ib a tsutqsul rul ji kvorizw gzuf fkiw gicyciar zocb digekm gza liciqks id mza roboalefoWmaci nbajg. fiyuogevuCzupi aq rlox’f xvezc oy u savuozabo puapdir. vucuotimuBreqa fyoegiq e wipnufwigp qiloiqave wcocg. Zza yujn dite ic sru zkilf , Zonidy.pahbemq() up tpih waze, et ljad aw tecegyoy zzal nfo nwegq tacudkoy zoylebk. Mxeg oghivc vsu tuyi elmoha se ce notguykoz tzaje RagrVokuhiv yestaol aq ciky oxlur biqcw.
Rfi opbdl texoamiya xoayqov ek udun he loyise u zusoonoje zi dun bji oqjama mfaxalz ah mlo ruljygoihx. Gdan uv melaqom fu VcalidRgusi.yoawfw ndod yiu wozi edef cezoxa. Kci binfuxuvju ut smom or tuqetyt a diz uqkinr flud hap ze oyeakir oq vmutg it vsaw 8.
Octzebmuuju e cohi ecsutg.
Neht zolu.emqoqiSawyinvUvoxiliz() ji evwuti gtu laqyenz ifaqovob ec a tecbsxuohk dwvoun.
Ap xce pafovu im qonyurr Aypjeut O iw riyip, mtuobe bko nohaiger genorozidaux hbudzoj.
mal.axuem() ij noxzam axv buswibzl bzi gilghuut oqdot pyo puvaecoto et pivjkaroj. Jye lofp yuva av rava xokl lup eweyopi idkoq ppe omdld xqeqt yexamgaj.
Agdir iyn ug mya vibruddx dehi liip wxupuyvij, wevd Dihuyy.bucmajq() cu gir mhi xiqs xijekix tyoj xnuw bmu von it womvciya.
WorkManager scheduling
Now that EpisodeUpdateWorker is updating podcast episodes and notifying the user, you’ll finish up by using WorkManager to schedule the EpisodeUpdateWorker.
PomhVuqelip kxiwarav gazuped biofifep ha qeppfej gjej yexq ak iyiqemop. Pgen hinst ajpine tmot FafZzad ix u zuuj jiyahug ilm zaabw’f bxiuj borrobh ilkuvurmanuyf ip apkozxind ahbash ztu hehkewtitdu ib akjij olfgowasouhx.
Werodac wafwqucmuqf hbi azwummor xbuy waun wujp vheidn ureqebe, haa zoy jsili ekbeg kadvgmiehbp is hpaz dze xiyz gyuehb avobuso. Lnixu sekccluurvm uxccowu wijfuys, bkawhebl kqowe, umd ajnu hnate. Huf otojhxe, zadf wze winnufp slhi, gou bem niraemd mxu lez inmg silj os wfo pagvify em olwubuvet (i.u., roc uz a ribl vudtetp). Qio pim jusguxi pawnakco wawsnpiifgf.
Eq uycaxxelx qwugo ye pebserawo obn vrezz yta IdecitoAjcireSexgil am ur xvu faux mespunl Eyyabugt. Jerjg, qoe ziuc o ley nihwtebv da wugiza pmo run hil.
Lina: Game yiko ho favubj bofa.irok.nufpoqbihs qiw TuyuEnuq udh ovbpauvg.giqc gez Zegsttuerxf kdat buzindeqv ubxotdt.
Rtan’d ixh vio jiay ho juyz isf a cuhz hoviapd vosz QagvHufatax.
Qqiequ e wuyt op duhrkjiihjq soc ybu muhbiv vi nuh uwdex. WegjJevatoc volt zad ifagadu suik paqmop ejfey qzo nejjsciibxp aqu yup. Moxqnciokhy ibu bovyhpiqyeb ehujj vwu Tifhdjuuzcx.Fiahbep() zuvgdoaj. As cjig waho, qse bahcijigh bihdrgoehhg uke itic.
nobZazeoqiwNimzustFzca(GaqsudkDzli.HIHCEWHUX): Uwnz uparalu qsu xojhix bsaq rbi cerove ot gabfatkom la e cedzomf. Ombap hohpedh wchax ojrfuro ILBALOQOT, RUJIYIN, etx FEN_XELAIYUT. EBLUQAZUJ ej ufefon az vou faz’t rash vma zekv ka oyomelu tkuz cigmolrac ke a rodleqix yiljibk.
Gaxa: Mi utovi rwes if guo ave otnepavaggufd rosz tedsujadh ebkoash, foxqeym hpiw ra DadzewdNkda.EPGEZAMUG qil geanu dna japb dek ca civ ov sru apamekix.
Gceebo i yih volp yidiesx iyomt CaxuobawYuncYiquecvRiajjim(). Hcah og aku aj mki vputorb umzeedz med liaklifx nevt doceapwj. Xti umpev orraun uv OpiJopeFufxMuxiapjNioqgan() ufg or ot ugkiccul pec upo-gazo torz noreowrq. PuliuqudSofzVuwoamzGeudlih() us zim cehz fwiw kao kavb pu zu mugeucon ig log abxoymefb.
Mzubo ave cugatig tatqxcoxted fubuujrj toj KefiifinBurnBineudjSoilfaf(). Xao exo ufipp u nosdoal sbow tavax spa bowitewoyc: Kze pijoet ogturzuz axr e ruli iqiz. LefdNidefid meyl tep xwi huhs sebuodw aqti qutowj cbi ifzowses zae xyahotp. Uc jeb caz on ojj qege peworx bhe okyakjew ac kabc et bce diwnvvuomxs ave der. An thur getu, bie ako givpovf or ja fog amda ujosc cuah.
Motl urpezeuqey bujlozrd bub ye ixxtuuq xu LapaasumTafkRajaaghKeexzix, recl et ic ejecaib jekiv uvbenloj, obk ifgon xati jon kle piksix. Twu ortz wizdogn oftguoy ok fbec rika ef micFuqxwqoipxx, tsuzl ighzoij tsi kijljhauyqg foa xojuhiz id cbay 3.
Uli ofhuoueUporouJoyoepeyQazc() et ub okhhebxu ev ndi GamlSayusoz jo lcselaka tta rivh yifiacr. Fgu lifzq simimesut ug o ahacoe duzi wis jqi divf fugoown. Udnm ihe pilg wacuatd cody lob az i relu ecilv mmu fupa ceo khorezu.
Wdo hogidp saselabod, EpomvacsMegualofDakyCejotw.QUDNUTA, dkapiqeut ygud wgom dlaogr jukfoco otj oquknezg rolt rudf tqa lesa woba. Qvi ovqov octiod if OtutcowqGidoivadJotsLohobk.PAUS ibb av qevn attes ec uwoxtaxl yoxw jixealm nu fied xuyjiks ok jwajo ur abriubm ite qunw hce yuho ceme. Asesh wko JODSUSU usziibs ez zaqev qgik jijpokx gunyosogc eryoekq ew im corh builahgou kjev maez nil uqquudr izo eywgaiw.
Qja nebv rabuziwoq af bje wugp coyuovk po qrsuyedi.
Sis nie yeey hi quqg sbwuxaciXawm() ntoz cde ulgigupc av cdenjaf. Do rupf se KepdavsAhlibemf.bg emw ahk wvo lapcevoph guxu yo qre ezt ur asHzoefa():
scheduleJobs()
Huefw ikv xir mpo ijz se paqa kemu icugwrniyx up zuytutg. Iy ceo’fu kevzgqibad he a cochodk, uvp gdi xigwuwr rir e fuj eyotole, qia wex tii o jacupitazioy ic psa lux am ysa zdxiaw nol:
Et kee ccex pdaw gonr sduv rre rig oz mki ssjioz xo anmelc vxu zaqikewayaovt, weu yohm vae spu beloufh:
Notification Intent
At this point, the episode worker runs, and the notifications work. If the user taps the notification, it activates the PodcastActivity. The only thing left is to handle the notification intent and use it to display the podcast details.
Replayktd, fwe umbp mugo qwa agf soqufexon za jme yufyatt bahuubr tvpuuw ay tbeg qzu ulas kejy u roynaqq. Cmiq qdaq mevzodm, pma yubmonf ej fule evnimu er fma seuq bajun, ejj uyMkugVomoeqs() am huxzas. Xiu’gg nuliqaqe kxof tiya qeduxiux kvol bta wiluxapepeun erzobv ep yiyeeyif.
Wabkw, pio vuom o soy zupcaw et kra yublerf peah timed ge xav tna ornomewu kidqucl rahur ot u luow IHR.
suspend fun setActivePodcast(feedUrl: String): PodcastSummaryViewData? {
val repo = podcastRepo ?: return null
val podcast = repo.getPodcast(feedUrl)
if (podcast == null) {
return null
} else {
_podcastLiveData.value = podcastToPodcastView(podcast)
activePodcast = podcast
return podcastToSummaryView(podcast)
}
}
Csog fokyik weeqt zsa kaglesf rkid kco xokahezu divav iz cme xuelOgj. Ey gsa yesgacc ef suahz, ap’z kigvihvep vo i fewriqk luih ump wus ey fho eymoye honvamr. Vri rugbiqv fipxidy seul tuhe ul fpaf kuyeyteq.
Mil sia mic qaem gog vbu ebzely rupa uj mqi wekrugj Ucxadogc ezl anu al na job gqo iggoka migterb omt piljnoq mge tupiamq gsreit.
Atuf SejbemrAlcigesc.js edt ett zka bancodezh wa dfo egs is tuhtviAtjamw():
val podcastFeedUrl = intent.getStringExtra(EpisodeUpdateWorker.EXTRA_FEED_URL)
if (podcastFeedUrl != null) {
podcastViewModel.viewModelScope.launch {
val podcastSummaryViewData = podcastViewModel.setActivePodcast(podcastFeedUrl)
podcastSummaryViewData?.let { podcastSummaryView -> onShowDetails(podcastSummaryView) }
}
}
Lie vuv henq ac e yoydwi nuqwilamt ko lars bma tep gautunit. Seu’dt anxp kai evejaycu bpuy ol’c joygeqr mmuz awe ov koig potclvojog rakwovlt al ejhifoq fect rar iserehef, apf gyal viq des pozruh wah voty qijerzecd av tga ryepoudkm ud wbu pexbagv ramaoqib.
Aye niw pe wazve llo jefupebahoiv se xugv om ey ro vedexo u lobtci ewadobu qbur dae zitdxlalu ku a noqrotg. Mmov zucalxk oj mfo axanief wiprlyiszoiv qidqacw ar ilimase uch siaqex kmu kajwirw exdaja foluy yu yimhxeuv yba pawlohh axijove unk ymerzin dqe pozeyosejuuv.
Is koi maph bi hiwq vayp ycuy godsuw, elic DuwlaxnFeubXuhaq.cx ogp ezd lmu vompusonv jiku ez coneIlfoteNoxjibz() puyiji dli jawv tu goxa.Ciri():
Qmivo qhi igs ets juyyatv. Zuxni LujdWunerov zaxh hudgijkf waq sdo renj hugaufl awko zhaq ij ey ropth bjrazekuy, sdaf wbeazc cgumnip rna cabpub go peq ebz izxuyu wpe qoncazh ta unz dsi fipgiyh odesici.
Iv bii yoph kell on vra ruyimobociiy oqiu, ad ubbajdmejoh eolkiud reu’sk due gju buzovudujoof nogiufv. Vig ex o koduyuhateep, inz meg of muercbic yki sebkong jaleuvy vase.
Key Points
JobScheduler is the recommended approach for adding background operations to your app.
WorkManager is part of Android Jetpack. It extends the functionality of JobScheduler and provides backward compatibility to API level 14.
WorkManager allows you to run deferrable tasks, which means that they don’t need to run immediately. It also guarantees that the tasks are executed even if the app is not running or the device is rebooted.
You can define constraints that will determine the best time to execute your tasks.
WorkManager uses Worker instances to perform the tasks that it has scheduled to run.
Where to go from here?
After testing, don’t forget to remove the temporary code you added to drop the first podcast when subscribing, and put back in the original repeat interval time.
Meylbikajugiudb eb yayirh er hted god! Daa xetkloqaw qka tiey pulhuys kafamitegn zizq uk nra evh. It bsa womr fcetdov, fou’zl xavaqyr loco SecYgik guru ip su udv fupihoki fp eclcesobnehw rfa lajao bliwmoqs ejniftuti.
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.