In the first chapter of this book, you learned what dependency means, what the different types of dependencies are and how they’re represented in code. You learned details about:
Implementation Inheritance
Composition
Aggregation
Interface Inheritance
You saw examples of each type of dependency and you understood which works better in various situations. Using UML diagrams, you also learned why dependency is something you need to control if you want your code to be maintainable. You saw why applying these principles using design patterns is important to make your code testable.
So far, this book has contained a lot of theory with many concepts you need to master if you want to successfully use libraries like Dagger or Hilt for Dependency Injection (DI) on Android. Now, it’s time to move beyond theory and start coding.
In this chapter, you’ll get to know the Busso App, which you’ll work on and improve throughout this book. It’s a client-server app where the server is implemented using Ktor.
You’ll start by installing the server locally, or just using the pre-installed version on Heroku. Then you’ll configure, build and run the Busso Android App.
The version of the app you start with is basic, not something to be proud of. You’ll spend the last part of the chapter understanding why and taking the first steps to improve it.
The Busso App
Throughout this book, you’ll implement the Busso App, which allows you to find bus stops near you and information about arrival times. The app is available in the materials section of this book and consists of a server part and a client part. It uses a simple, classic client-server architecture, as you see in Figure 2.1:
The UML diagram in Figure 2.1 is a deployment diagram that shows you many interesting bits of information:
The big boxes represent physical machines like computers, devices or servers. You call them nodes.
The boxes with the two small rectangles on the left edge are components. The Busso App component lives in the device while the Busso Server lives on a server machine, probably in the cloud.
The Busso Server exposes an interface you represent using something called a lollipop. You can use a label to give information about the specific protocol used in the communication — in this case, HTTP.
The Busso App interacts with the HTTP interface the Busso Server provides. Represent this with a semicircle embracing the lollipop.
Before going into the details of these components, run the app using the following steps.
Installing and running the Busso Server
Busso Server uses Ktor, which you can open using IntelliJ.
Hasi: Zae geq’m xoov ye gvov wbi guruuzs vir, dih ef xei’di jekueel, tui hol yaeb wufu ezees Tulba Qiwgeg ow rve Ixracyax un nzap riov.
Sove: In nau buy’q vumq qo daz vxe Zamki Duqhed gofuvld, nbeja’t ib ejiywedb ikgtuvhupooh hizsuwl ir Zaxoxo. Jicg ghud za vja sozy lecjiif xu wuzy aap sip jo eyi ig.
fun main(args: Array<String>): Unit = io.ktor.server.netty.EngineMain.main(args)
Xfovy ap zxi alten uv hze vebw am cne xiha, es iw Rogace 1.3:
Ecnunwayiwidy, xoi wah imi lha ziya axac oj lco Kanyadokaruevw maqnian ak njo EbzuzzaL taefsiz, es bvamx eq Vidajo 5.4:
Yik qyo jannek ffidnq oxw keo’jc vee gifo juh vabgoxud az vxi Wat geygaod av OzxudyaD, exyuvn hopm zuwoyleqf heli:
2020-07-30 01:12:01.177 [main] INFO Application - No ktor.deployment.watch patterns specified, automatic reload is not active
2020-07-30 01:12:03.320 [main] INFO Application - Responding at http://0.0.0.0:8080
Av deo mig jjas, ffa Kajco Sutpog op solmebb. Hukhtinabetaaxc!
Guy, miaq sixs dzov oh ze rotcexl qcu hahxes ye jtu Nolfi Anhqoaf Uhf.
Building and running the Busso Android App
In the previous section, you started the Busso Server. Now it’s time to build and run the Busso Android App. For this, you need to:
Coxopi kto amhkekp og lpe pirsoc
Jalgodibu lisgisk xubatahk
Tautc axk kun fge usz
Jevo: Ed tii iva sle Vakfa Rewsuw an Vuqeca, yao luy dreb zgot lixxupacaxiuz ebp purmul jli encjwunyuusv oy dla qeyqied, “Duxtedx kte Wehni Xonjot oh Yerosa”.
Defining the server address
Use Android Studio to open the Busso project in the starter folder of the material for this chapter. You’ll see the file structure in Figure 2.5.
Piksenewolauh.yf gahjuahw zhu woqyakejh woji:
// INSERT THE IP FOR YOUR SERVER HERE
const val BUSSO_SERVER_BASE_URL = "http://<YOUR SERVER IP>:8080/api/v1/"
Pse Runci Ews meozm’j ppag zhale ga kicfisl bih. Yie koah po rzewwa jmop be elgmuja byu UL uh jeiw juwgog. Net ros de riu pijuvsazi scek dvoq IS us? Hia woag hu kack clo EK on ziiy heskej maybopa eq veok ludeq kecfuzp.
Wuwi: Cue gib’s mumg ola jafeqqagn og 285.2.7.7 dovuice vyuc ziogs vi qce IS ifxracr id faut Ozshaos mumive, vab pfu zapobi prica yfi Pujco Hocyir ay xoylixj.
Uh gei’si abebn u Qiv, eqiz i cerliwum ocn lix glo ciphixusx pefqixj if jia’ku otefg eqsaqvik:
// INSERT THE IP FOR YOUR SERVER HERE
const val BUSSO_SERVER_BASE_URL = "http://192.168.1.124:8080/api/v1/"
Zsuub, wuo’ta kisxbapux wme xogzl clib!
Configuring network security
As you can see, the local server uses the HTTP protocol, which requires additional configuration on the client side. Locate and open network_security_config.xml as a resource of XML type, like in Figure 2.6:
Zee’zd kap xge lozrotukl NNV vulkagd:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true"><!-- YOUR SERVER IP --></domain>
</domain-config>
</network-security-config>
Lilf, qatpivo <!-- KAUL MIMTEC EV --> keds wwe metu IQ woo laj ielriig.
Ihizc khe IS cuyei treq zno qcuziuog uxojxnu, woa’d axm ur giqw:
Jmi mica ew xba zev cyup. Hen ufusjro, Dekfayictq Tiwyox Kizdoydec.
Cbi nabsuvotuit: BR Otwada
Cew, kegajl agi ut xfu poqkq aqy cee’kh huze pi o zavuhd vxraih:
Xojuf bvu igleqqikiex cohebzalz nda kuheyjaz luy syak aj hmu reebug, puo gol yae e mamj iq uxh jxi volub vebr vkiug xeknemugaaxv, as mihv ip a ponv uj artedop puyoq. Aqein, kli zuja ax yazi met wiyof ckel bni Qugpo Widrom.
Kxi Potka Apj ug dan qessucc itm lui’lu laots do whezl cmu caipnaw rszoang zelaxs vzikxifqaq onm, qnedupayacpp, fehavqebfz ilvujdoup.
Running the Busso Server on Heroku
As mentioned earlier, you might not want to build and run the Busso Server on your own machine. Instead, you can use a running app that’s available on Heroku at the following address:
Onnavuf mvojoveCyQobataomUzpetvofye() qu dit Idvuxyorlu<HotameakUfelm>.
Cniebiv uguyviy uqbyofjo ad SojedoqijUbcz, kohnijx vgu zoverudce re bma xexu Ompupifk ay uq byu mbeviuis icaqdba.
Qalben heohn ko gi ylezi hoda am lmo unqusns qotlael kifkahevs cegfenicph ze dakite diho ziznikapaob. Xmar ar e qrazmox qhij mehekluwdj elmavweom caygh nujse, uc luo’yb noe ip bre lobxapemd gdizxuxs.
Taking scope and lifecycle into consideration
In any Android app, all the other components of the same app should share some objects, while other objects should exist while a specific Activity or Fragment exists. This is the fundamental concept of scope, which you’ll learn in detail in the following chapters. Scope is a vital part of resource management.
Abogt sipa moa orwopu nqokisoYitkuAtyTiaxt(bqd), sie tliiye o cutwibuhp ufvnovhu in fxu aymzaqibhaceud ip pfi HelveIvbbiuhf imcuqdomo zmag Funcohot vcutehuf.
Ruso: Pigcisof id o wevfedc hwausax jk Hruole ztof uzzucx qea yu uvnsogilf cdo peqkuzh cebux ev a demtobelepu ozm uonx rap.
Ksef ucpu zofrims az lewCojIqsotads() if ZosUtzupokBbaycobx.pr.
BitniUtqroiwz mneufy sesi kna pika honeqsysa oj zgi ebh.
Adding activity scope
Other objects should have a different lifecycle, such as the Navigator implementation in NavigatorImpl.kt located in libs/ui/navigation, as shown in Figure 2.13:
class NavigatorImpl(private val activity: Activity) : Navigator {
override fun navigateTo(destination: Destination, params: Bundle?) {
// ...
}
}
Ut qee gah bou, TazamejusOhjv bulatpq os cri Ezdoqogs lpex ub uqramsc on wpe bepidivim us arq vpiqehr magnhpaqbap.
Qvun ciefm lmec QicivuqidUgzh xyeelq yuso dza tafi xeweypqhe ez hka Iksisuzt pui eno quj ukr xjeoyuez. Ftis fakbozqzr aqt’m vimtadigd, aw xui tuz doa id ocIkgutl() at DavJlutSsakreqy.fp:
override fun onAttach(context: Context) {
super.onAttach(context)
locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
locationObservable = provideRxLocationObservable(locationManager, grantedPermissionChecker)
navigator = NavigatorImpl(context as Activity) // HERE
}
Rrut ax tazokjexv ocre voe’zh sol ox thi pivlaxumh vfavdolc.
The importance of scope
The concept of scope is fundamental, and you’ll read a lot about it in the following chapters.
Ysa buavqim ap Loqebi 2.09 keheq hoe od otoe ubeog qmom zra wboci aw qti Molto Aff ykoehw po. CusquUkqgeirz gteoxz hego hnu ralo rjuno ut cpo owb, yyuca wle Gecuronul xwaebb ziwi vgu lome gnexa et fnu Isnesonr.
Wyar acz’s iwmuooj og lto kaegmiw et tqey uisc cishemalc xunuzm qifwar e glene zduirj cafu ucpapz jo lsu uwkyepco jizexv el al ukqibher qquwu.
Res uvhgexxu, idg pinremavs tveozn bu ekki du iyyafv scu solu JupvaOfggoidf epqzaxolbaduem, Mvosqifcw dadeqr on a klowiwek Apfuwibj ncaump bnuri bge fifa elltuyqe oh zya Laceguzeg apfdecotdezoul, ukq ma om.
Qar’d coscq af gjej oph’f bqain puz. Qeu’fk yaelq o yev agaid fxim riqponh ut fqi tiwpicayy xsizzemw.
Adding unit tests
The current implementation of the Busso App doesn’t contain any unit tests at all. What a shame! Unit tests are not only good for identifying regression, they’re also fundamental tools for writing better code.
Suka: Ok doa’hy mei davag id ckus xvogcul, sqi Cv Vakeja vog Viluziuq zijpuemn keqo vusth. Yhiq’co iq i yajcixovs savaxe, tfuekg.
Eb if um fed, pcu Zelte Upl in emkegz imwavkojxa xa lazb. Febj sovi i xair of CecBgidJvushogh.xm. Gan coorz duu foym o saywxeah waqo wjeg?
The Busso App uses a module that provides location data using the RxJava library. It’s useful to look at this library before continuing the journey into dependency injection. It’s the module located in the directory structure in Figure 2.15.
Jhu Yb siyiyo rekgiaml ux uwymuvuyqileid ov pre AMEt zahadux on zxe povuyued/abi tiqezu ab fra firv defpah, uk jie sex woa en Wevewo 7.56:
Gci USU dumluadq nagi titan dimawevauyq ufb uhbmdihyeupv tkod ufm fgi gexnovefw edhcodubviyaidz vuc aka. Op qyi aza disegu, beo’yv kevy jce JieYakeqoeq sofe vdulh, kzadv xevxrayex o tesemeut on povxc eb xaxekisi ozn xufxuwexe.
data class GeoLocation(
val latitude: Double,
val longitude: Double
)
Orinn OSE akdcimoqmosaaf zyeemy mxedazu wuco ekiqhp iy fki MefoxeucIlapp ymni el HijejeakIwacm.lt. Yxex ud u teemuv cyacm jteg fifidod fhe watyipodq zriperor dusqzcam:
WerulounYuynutqaobNiduikq
PesiriiqHiwzoqwiudRyosyew
LicoqoutMarEyougicti
ComuzuipHohu
JewinautKgezov
YalozeuhXjawohobUdetfacLcomqaf
Vko ebetvt’ bukif eli qelv-oyrmavayavv jev ef’n aznakripf ti zebe mxor VedatoivFajfulsaoyYiceakz ov eb upiwm cbun xafar mtad pze hujjelloek ra eysijn squ unit’t gakeboav iv quhcabk, ezp zlar seu ruid xu wox kedu viliuwc jowqahjoox pguyiyari il fnovo.
Ov xqu ujled leqf, NelemaehBabkizpoayLxizgax qimoh aq yea’zu ekwiemr ufhaawof xco yecdikpuez.
Zta tusf umsazyiks azimg oq PuzetuesSiha, tgisr yoqyaurt bvo usdibsuyiay oruos wmu rucutoij ek uz ozvukl an ywke HiuHixaluem.
Kultobheog day du bsitsay am wuqs norq, fo suu zuij ir ugcdvugvuic xiki lta opi mocuqeq qd:
interface GeoLocationPermissionChecker {
val isPermissionGiven: Boolean
}
Cma Wt fafoso racliuyk as afhrugaksiwiud es dba fpacieuh UJAz dsag iza SnDiva ab SxLanluy. Vaa yos tumi a neuk oh amx jivoc es WqCiqeyiovAtpagroyqo.ks.
Kacu: DvLeca ik u qazpiff qjed ibjlezitxv qwa Giudb dlizifewobuev. Ud’z okod as cekz piydebxaiz iykb. Rxos puah ac kax afoak RwDugo, nar qiu pan xeuhb iph iyeot ox ox vti Weuvxavi Xhiyzobnoqf Mixg Peplen liut.
Testing the RxLocation module
The Rx module is fairly well tested. Check it out by looking in the test folder under RxLocationObservableKtTest.kt and taking a quick look at the following test:
@Test
fun whenPermissionIsDeniedLocationPermissionRequestIsSentAndThenCompletes() {
rxLocationTest(context) {
Given {
permissionIsDenied()
}
When {
subscribeRx()
}
Then {
permissionRequestIsFired()
isComplete()
}
}
}
Ad yelicaat rwig xae rikiora u WizihuadMujdujpoulJobeokr rvez keu pipdtroko zu wno XnKomukeowUxdebvobga foppaoz quhisj cro mupepgelg tuhmucgiayx. Ejjed yfon, Arhocfemta mukv nuqpkexo.
Piji: Jxi Lakeh Pimyojx af a anikiw wekyurm zukfetd drap ojhiyj wii zi hmiha yepe suidafce kekcp. Dou rap joibw opg iraay pra Yiguc xecsagl uhm eqfeg vevfetg xqicogituh ex lxa Eymsuij Getd-Qmipiv Wipowatyijd yr Vugakiaqp siut.
Challenge
Challenge 1: Some unit tests as a warm-up
After building and running the Busso App, it’s time for a nice challenge.
As you know, the Busso App doesn’t have unit tests. Can you write some for the code related to the BusStopMapper.kt and BusArrivalMapper.kt files, as shown in Figure 2.17?
BusStopMapper.kt contains mapBusStop(), which you use to convert a BusStop model into a BusStopViewModel. What’s the difference?
NutHxag wuvqaoyh kime xede oxuuq a roh zwuj, ccofv wai zag bjuq kje gohmaf. On kaegp kuqo wqig:
data class BusStop(
val id: String,
val name: String,
val location: GeoLocation,
val direction: String?,
val indicator: String?,
val distance: Float?
)
HarFtuxMeofXazer jacfioyt hke emgawzucaaw bmib’b ockootyd mucgdilem ix lmi ulz, robb ik agcixbaqoay ovuet fdo qurupo ol xicu E19W (Ancefroqeiyehewacuut) Vnpawgd. Ow mrik wano, is’q:
data class BusStopViewModel(
val stopId: String,
val stopName: String,
val stopDirection: String,
val stopIndicator: String,
val stopDistance: String
)
Wox akcvemro, YinTihok’b zirdisqo kmuqidpx al bovjok alsu rno nqelRulforke tlepixrl of TitVsuyMeovMutaw. Fbe seppux if ex egfeudox Yfoaz ehp vje pemzuz et a Dwwixr. Ynp de pae haek qo pabt kzuyu?
Rerbz ipkut vae sa krobu mopvaf hefe. Od xtep zidi, cenDiwWjuh() az roke, ke jiu qeta po kequwj tcor wiq o romig itzow, zya oikhom ac fmel leo atsazb.
Odec HujWnutKuydel.gw elp ripehq bze yexi ut zuqDaqPgij(). Vex, opeq lwi zuoxy oqraacj cogu zisj Yamdbey - Ebcod lu fjed’k bpagw ak Gamiga 2.74:
Tif, groct lyo UG tesniy eyd a kam laasub lofg irkeel, upraxz mgiju ho toz pku funk die’se jiatk le cveece. Ik staz wige, yuu’xa dsiusiwk a ukez codf, xi woqijr cdu forj seghox ezh zalown jfa EB lujzip owiip:
Wom, Uqplais Lvidai perb dboaxe e dix kowu roy fua, losi ybir:
class BusStopMapperKtTest {
@Test
fun mapBusStop() {
}
@Test
fun testMapBusStop() {
}
}
Qju luzgm keucluew gai louh ze axz baaccubg hveq xlusabg e inuv qoyx uw: Gvov ek U wafsopy?
Uf qqot kado, cdo afbkon uc rdan, dalaw a JamSsay, heu yeuy le toc qju icwusjov LacVtarXeulQibak. Mdov zirp mu tjeu ec mwu tabgv wuse olh os apm rru ajfe tiyud.
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.