Gode praksisser og arkitekturer i Android-programmering 1
Om oplægsholderen Jacob Nordfalk nordfalk@lundogbendsen.dk Instruktør for Lund&Bendsen Ekstern lektor på IHK Underviser i Java SE, Java EE, Webserverprogrammering, OOAD, Android Forfatter af http://javabog.dk Udvikler til J2SE, J2EE, Android Har et halvt dusin apps på Android Market Teoriprøven, AndroidElementer, DR Radio, Esperanto-radio Muzaiko og 3-4 andre Har lige sendt de første patches til Androids kildekode! Android 2
Hvorfor nødvendigt med praksisser? Brugerens oplevelse er i centrum Alt er 'åbent og klar til brug' - altid Programmer til mobile enheder har Kun lidt kontrol over hvordan de lukkkes Ingen kontrol over hvordan de startes Stærkt svingende adgang til netværket Brugerens oplevelse forringes væsentligt hvis en app ikke tager højde for dette 3
DR Radio Kildekoden er på http://code.google.com/p/dr-radio-android/source/ 4
5
DR Radio Levende Levende ikon ikon (widget) (widget) Opdatering Opdatering hvert hvert 20. 20. sekund sekund Udsendelse Udsendelse og og næste næste udsendelse udsendelse Spiller Spiller nu nu og og 33 forrige forrige Stamdata Stamdata opdateres opdateres hver hver 3. 3. time time (kanaler, (kanaler, stream stream URLer, URLer, driftmeddelser driftmeddelser etc) etc) 6
Stærkt svingende adgang til netværket Android er enkelttrådet Lav aldrig aldrig aldrig netværkskommunikation i GUI-tråden Opdatér aldrig aldrig aldrig GUI fra baggrundstråd Brug AsyncTask (eller Thread) Har indbygget trådpulje Har indbygget trådsikker kommunikation mellem baggrundstråd og GUI-tråd 7
Stærkt svingende adgang til netværket Data der udløber Cache så længe det giver mening, giv så bruger meddelse om utilgængelighed Stamdata - der sjældent udløber Bundt dem med app'en! Undgå "indlæser, vent venligst"-dialoger der irriterer og blokerer for brug af andre funktioner Start op med en gammel kopi Hent ny udgave i baggrunden Opdater brugerflade hvis stamdata har ændret sig 8
DR Radio 9
Programmatisk brugerflade package dk.firma.projektnavn; import android.app.activity; import android.os.bundle; import android.widget.textview; public class MitProjektAkt extends Activity { /** Called when the activity is first created. */ @Override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); TextView tv = new TextView(this); tv.settext("velkommen"); setcontentview(tv); } } 10
Deklarativ brugerflade i XML package dk.firma.projektnavn; import android.app.activity; import android.os.bundle; public class MitProjektAkt extends Activity { /** Called when the activity is first created. */ @Override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.main); } } <?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="velkommen" /> 11
Aktivitets-stakken Intent Intent Intent 12
Androids applikationsarkitektur Ansvarlige Ansvarlige for for ét ét skærmbillede skærmbillede Opretter Opretter views views og og layout layout (typisk (typisk fra fra xml) xml) registrerer registrerer lyttere lyttere MVC MVC "View-Controller" "View-Controller" Android Framework instantierer AndroidManifest.xml Applikationens navn Tilladelser Aktiviteter, services,... Intent filtre Aktivitet Intent Aktivitet Intent Aktivitet viser layout.xml JVM Kommunikation Kommunikation mellem mellem aktiviteter aktiviteter URI URI 'på 'på steroider' steroider' Har Har et et rigt rigt antal antal felter felter til til forskellige forskellige data data skaffes skaffes med med getintent() getintent() 13
Sende data med intent Nøgle-værdipar kan sendes med intent i.setextra(string nøgle, værdi) Aflæses i modtageraktivitet med getintent().getextras().getstring(nøgle) AfsenderAktivitet Intent i = new Intent(this, ModtagerAktivitet.class) i.putextra("navn", "Jacob") i.putextra("alder", 40) this.startactivity(i); ModtagerAktivitet Intent i = getintent(); String navn = i.getextras().getstring("navn"); int alder = i.getextras().getint("alder"); 14
Eksempler på intents Åbne opkaldsvinduet new Intent(Intent.ACTION_DIAL, Uri.parse("tel:26206512")) Ringe op direkte new Intent(Intent.CALL, Uri.parse("tel:26206512")) Åbne browseren new Intent(Intent.ACTION_VIEW, Uri.parse("http://javabog.dk")) Åbne vindue til at skrive epost-meddelese intent = new Intent(android.content.Intent.ACTION_SEND); intent.settype("plain/text"); intent.putextra(intent.extra_email, new String[] {"jacob.nordfalk@gmail.com"}); intent.putextra(intent.extra_subject, emne); intent.putextra(intent.extra_text, txt); Åbne vindue til at skrive SMS intent=new Intent(Intent.ACTION_VIEW); intent.settype("vnd.android-dir/mms-sms"); intent.putextra("sms_body", besked); intent.putextra("address", nummer); 15
Hvor skal vi lægge modellen? Afhænger af koblingsgrad Tæt koblede aktiviteter har nok fælles model Data kun nødvendige i én aktivitet lægges Der er kun én bruger! en Singleton (eller lignende) rækker Hvor stor bliver kodebasen? Næppe mere end 30 (ydre) klasser... Sjældent arbejde til mere end 3 udviklere 16
instantierer Jam, så er strukturen jo oplagt... Android Framework Aktivitet Intent Aktivitet Intent Aktivitet viser layout.xml instantierer ved opstart Levende ikon Én Én JVM JVM pr. pr. app app :-) :-) JVM <<Singleton>> Model 17
Men men men... Bruger oplever at alt er 'åbent og klar til brug' Bruger kan skifter tilbage til de sidst 8 brugte programmer - og fortsætte hvor han slap Kun lidt kontrol over lukning Framework må smide JVMs ud af hukommelsen - uden varsel - og senere genskabe dem Services og notifikationer kan mindske risikoen for at det sker, men ikke afværge det med sikkerhed Ingen kontrol over hvordan app (gen)startes App kan blive genstartet midt i en sekvens af skærmbilleder - i en frisk proces/jvm! 18
Aktiviteters livscyklus Aktiviteter genstartes ofte, f.eks ved vending af skærmen fysisk tastatur skydes ud/ind hvis klaptelefon åbnes/lukkes hvis telefonsprog sprog ændres kan slås fra, men... Så snart en app ikke er synlig kan den risikere at blive slået ihjel Framework kalder onsaveinstancestate(bundle b) gemmer View's tilstand 19
Genskabning af aktiviteter 20
Anbefalet arkitektur Er Er flygtige: flygtige: (gen)instantieres (gen)instantieres af af framework framework efter efter behov behov Framework Framework persisterer persisterer views' views' tilstand tilstand onsaveinstancestate(bundle b) b) oncreate(bundle oncreate(bundle savedinstancestate) savedinstancestate) Android Framework instantierer Framework Framework husker husker indhold indhold Aktivitet Intent Aktivitet Intent Aktivitet Framework Framework instantierer, instantierer, før før noget noget andet andet JVM MinApplication Application singleon anvender instantierer <<Singleton>> Model Levende ikon 21