Operacijski sistem Android se na vse pretege trudi ustreči tako uporabniku kot tudi razvijalcu.

Sestava vsake aplikacije

Operacijski sistem Android je sestavljen modularno, kar pomeni, da je vsaka aplikacija skupek posameznih komponent, vizualnih ali pa takšnih, ki tečejo v ozadju. Komponente lahko poljubno kombiniramo med seboj, iz ene odpremo drugo ali pa isto in pri tem nismo omejeni le na komponente svoje aplikacije, temveč si lahko pomagamo tudi z uporabnimi komponentami drugih aplikacij. Poglejmo si, kaj so te komponente.

Aktivnosti

Vizualne komponente se imenujejo aktivnosti (orig. Activity) in predstavljajo vse, kar uporabnik vidi. Vsaka aktivnost ima svoj uporabniški vmesnik in je namenjena natančno določeni nalogi oziroma skupini podobnih nalog. Če vzamemo za primer preprosto aplikacijo za upravljanje s (splošnim) socialnim omrežjem, bi ta imela vsaj tri različne aktivnosti:

1. prijava v socialno omrežje (na primer LoginActivity),
2. prikaz objav drugih uporabnikov (na primer PostsListActivity),
3. vnos nove objave (na primer NewPostActivity).

To bi bila res najbolj osnovna aplikacija, v praksi pa imajo takšne aplikacije še podroben prikaz posamezne objave, morebiti prikaz slike oziroma galerije, nastavitve, pomoč itd. In vsaka od teh aktivnosti bi imela svoj podrazred razreda Activity.

Če pogledamo še iz druge strani – vsakič ko opazimo, da se spremeni videz aplikacije (včasih celo vsakič ob posodobitvi podatkov), se v ozadju zamenja tudi aktivnost. To je resnično pomembna komponenta in pri razvijanju aplikacije veliko časa porabimo za konfiguriranje in sestavljanje aktivnosti. O samih aktivnostih bomo več povedali drugič.

Storitve

Storitev (orig. Service) je komponenta, ki teče v ozadju, kar pomeni, da nima grafičnega vmesnika in lahko teče, ne glede na to, ali je aplikacija, ki si lasti to storitev, aktivna ali ne. Storitev je namenjena konstantnemu, periodičnemu opravljanju določenih nalog. V prejšnjem primeru aplikacije za upravljanje s socialnim omrežjem bi to bil ponavljajoč se proces, ki na primer vsakih 15 minut preveri, ali so na strežniku kakšna nova sporočila, in jih prenese na napravo ter morebiti tudi opozori uporabnika o novicah. Takšen proces mora teči, ne glede na to, ali uporabnik trenutno uporablja aplikacijo ali ne, in se ne sme končati. Prav tako ne sme imeti vpliva na trenutno aktivno aplikacijo – če uporabnik trenutno kliče koga po telefonu, posodobitev podatkov socialnega omrežja ne sme upočasniti ali pa celo ustaviti tega. In z uporabo storitve za to preprosto poskrbimo. Tudi storitve bomo pustili za naslednjič.

Nameni

Aktivnosti in storitve so osnovne komponente vsake androidne aplikacije, namen (orig. Intent) pa je vezivo med njimi. Vsako od teh komponent namreč poženemo tako, da ustvarimo (izrazimo) nov namen in ga posredujemo sistemu Android, ki nam tako požene ustrezno aktivnost ali storitev. Namen predstavlja uporabnikovo željo po izvedbi določene naloge in operacijski sistem se bo potrudil po svojih najboljših močeh, da uporabniku to omogoči.
V prejšnjem primeru bi uporabnik s klikom na gumb »Vnesi objavo« izrazil namen »želim deliti novo objavo z drugimi uporabniki«, operacijski sistem bi pogledal, kako lahko uporabniku to omogoči, in mu ponudil aktivnost št. 3 – NewPostActivity (vnos nove objave). In če bi uporabnik v nastavitvah izrazil še namen »želim, da vsakih 5 minut preveriš, ali je na strežniku kakšna nova objava«, bi sistem poiskal storitev, ki bi počela natančno to.

Eksplicitni ali implicitni

Nameni so lahko natančno določeni (eksplicitni) ali pa bolj ohlapni (implicitni). Eksplicitne namene uporabljamo večinoma znotraj aplikacije in odprejo natančno določeno aktivnost, saj mu podamo kar razred le-tega. Na primer, če uporabnik v primeru naše socialne aplikacije pritisne na gumb »Vnesi objavo«, natančno vemo, da mu moramo prikazati aktivnost NewPostActivity.

Implicitni tip namenov pa uporabljamo predvsem za izbiro med različnimi aplikacijami, ki jih imamo nameščene na napravi in so zmožne opraviti določeno nalogo. Na primer, če uporabnik želi odpreti sliko, ki jo je nekdo objavil na našem socialnem omrežju, izrazi ohlapno željo »želim odpreti sliko Android-Minion.jpg«, za kar mu lahko Android ponudi več možnosti: privzeto galerijo Android, pregledovalnik fotografij Google+, Adobe Photoshop Touch itd. Skratka, vse nameščene aplikacije, ki znajo odpreti slike. Če je takšna aplikacija ena sama, jo Android kar požene, sicer pa uporabnika pozove, da izbere želeno.

Androidna inteligenca

Kako pa sistem Android sploh ve, katero aktivnost oziroma storitev naj izbere? Ne gre za kakšno posebno inteligenco, ampak le za to, da se vsaka aktivnost vnaprej prijavi, da se zna odzvati na določene namene. Da to omogočimo, v datoteki AndroidManifest.xml, znotraj značke <activity>, dodamo eno ali več značk <intent-filter>, znotraj katere določimo, na katere parametre se bo naša aktivnost odzvala. Poglejmo si preprost primer:

<manifest …>
<aplication …>
<activity …>
<intent-filter android:label="@string/view_image"
android:icon="@drawable/ic_view_image">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="image/*"/>
</intent-filter>
</activity>
</aplication>
</manifest>

Android bo ponudil to aktivnost, če bo na primer uporabnik kliknil na sliko v brskalniku datotek, kot je vidno na sliki.

Med ponujenimi aplikacijami se pojavi tudi naša.

Lastnosti android:label in android:icon značke <intent-filter> določata napis in ikono, ki se bosta prikazala ob kliku na sliko. @string/view_image kaže na napis »Poglej sliko« v strings.xml, @drawable/ic_view_image pa na ustrezno ikono v mapah res/drawable-*.
Podznačke <action>, <category> in <data> pa določajo, kdaj se bo aktivnost prikazala.

Akcija

Značka <action> je obvezna in določa osnovno zvrst akcije, na primer poglej, uredi itd. Zvrst določimo v lastnosti značke android:name. Nekaj osnovnih lastnosti je:

● android.intent.action.MAIN – to označuje osnovno aktivnost, namenjeno zagonu aplikacije in se bo prikazala v zagonskem predalu sistema (App drawer),
● android.intent.action.VIEW – odpri datoteko, kontakt, podatek ipd.,
● android.intent.action.EDIT – odpri datoteko, kontakt, podatek ipd. in omogoči uporabniku urejanje tega,
● android.intent.action.SEND – pošlji datoteko, kontakt, podatek ipd. prek drugih programov (na primer deli na Facebooku, pošlji po e-pošti itd.),
● android.intent.action.SEND_MULTIPLE – podobno kot SEND, le da pošlje več podatkov hkrati,
● android.intent.action.PICK – v drugi aktivnosti izberi datoteko, kontakt, podatek ipd. in ga vrni nazaj (na primer izberi svojo profilno sliko iz galerije),
● android.intent.action.SEARCH – določa aktivnost, ki omogoča iskanje znotraj aplikacije.

Vnaprej definiranih akcij je še veliko več, lahko pa definiramo tudi svoje, na primer com.comtrade.intent.BE_AWESOME, vendar je to uporabno zgolj znotraj naše aplikacije ali pa znotraj skupine aplikacij, saj tuje aplikacije ne bodo vedele, katero akcijo klicati. Za povezovanje z drugimi aplikacijami torej uporabljajmo vnaprej definirane androidne akcije, za interno uporabo pa lahko uporabimo tudi lastne.

Podatek

Značka <data> predstavlja podatek, ki ga želimo odpreti z aplikacijo. Ta značka ni obvezna, je pa zelo pogosta. Z njo lahko na primer določim, da naša aplikacija odpira le slike, le kontakte ali pa zgolj datoteke iz določene mape.

Znački lahko nastavimo:
● mimeType – tip datoteke, na primer »text/plain« (navaden tekst), »text/html« (html datoteke), »image/*« (vse slike), »*/*« (vse datoteke) itd.,
● scheme – vir podatka, na primer »file« (datoteka), »mailto« (e-poštno sporočilo), »content« (vsebina), »geo« (geografske koordinate) itd.
● path in pathPattern – natančno določena pot do datoteke oziroma vzorec poti, na primer »/DCIM/*« (zgolj slike s kamere),
● host in port – na primer www.google.com za host in 8080 za vrata.

Kategorija

Značka <category> predstavlja kategorijo in dodatno omeji zvrst aktivnosti. Ta značka ni obvezna in dostikrat tudi ni podana. Najbolj uporabne so:
● android.intent.category.DEFAULT – privzeta aktivnost za določeno vrsto namena; če ima vsaj ena od ustreznih aktivnosti nastavljeno to kategorijo, potem tiste brez nje niso prikazane,
● android.intent.category.LAUNCHER – ta kategorija določa aktivnosti, ki se bodo prikazale v androidovem osnovnem meniju za zagon aplikacij,
● android.intent.category.PREFERENCE – določa aktivnost, ki vsebuje nastavitve aplikacije,
● android.intent.category.BROWSABLE – aktivnosti, ki bodo pognane iz spletnega brskalnika, bodo imele to kategorijo.

Zagon aktivnosti

Aktivnosti zaženemo tako, da ustvarimo nov namen in ga posredujemo. Kot smo že omenili, poznamo dve vrsti namena: eksplicitni in implicitni. Z drugega vidika pa obstajajo aktivnosti, ki zgolj sprejmejo podatke, in takšne, ki kaj tudi vrnejo. Poglejmo si, kako poganjamo vse te tipe aktivnosti.

Eksplicitni namen

Pri eksplicitnem namenu natančno vemo, katero aktivnost ali storitev želimo odpreti (in ji morda posredovati podatke). Za to moramo ustvariti nov namen (Intent) in ga posredovati v metodo startActivity(Intent intent) ali pa startService(Intent intent). To sta metodi v razredu Context, katerega podrazred je Activity, od koder bomo večinoma tudi izvajali klice teh dveh metod.

Primeri eksplicitnih namenov:

1. Intent intent = new Intent(context, SettingsActivity.class);
startActivity(intent);
To bo odprlo aktivnost z nastavitvami aplikacije (SettingsActivity).
2. Intent intent = new Intent(context, OpenImageActivity.class);
intent.setData(Uri.parse("content://media/external/images/media/5374"));
startActivity(intent);
Ta koda bo zagnala aktivnost za prikaz slik (OpenImageActivity), v kateri bo vidna slika na podanem naslovu »content://media/…«.
3. Intent intent = new Intent(context, UploadDataService.class);
startService(intent);
Ta koda pa bo pognala storitev za nalaganje podatkov na strežnik (UploadDataService).

Implicitni namen

Implicitni namen pomeni, da nas ne zanima, s katero aplikacijo ali aktivnostjo bo sistem oziroma uporabnik odprl naš podatek. Hočemo le, da ga zna. Prav tako kot pri eksplicitnih namenih tudi tu ustvarimo nov namen, le da mu namesto razreda podamo zgolj opis aktivnosti in podatkov. Tudi tu namen posredujemo v metodo startActivity(Intent intent). Storitve ni priporočljivo zaganjati implicitno, zato metode startService(Intent intent) tu ne uporabljamo.

Poglejmo si nekaj primerov implicitnih namenov:
1. Intent intent = new Intent(Intent.ACTION_VIEW,
Uri.parse("content://media/external/images/media/5374"));
startActivity(intent);

Ta koda odpre sliko, ki je na napravi zapisana na naslovu »content://media/…«. Android sam prepozna, da gre za sliko, ki jo želimo samo videti (ne pa nujno tudi urejati), in nam ponudi vse aplikacije, ki znajo prikazati sliko.

2. Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addCategory(Intent.CATEGORY_BROWSABLE);
intent.setData(Uri.parse("http://www.comtrade.si"));
startActivity(intent);

To je namen, ki odpre izbrano spletno stran v brskalniku.

Izbira brskalnika za odpiranje naslova url

3. Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_STREAM,
Uri.parse("content://media/external/images/media/5374"));
intent.setType("image/jpeg");
startActivity(intent);

Ta namen pa omogoča pošiljanje slike. Tokrat se bodo odprle aplikacije, ki znajo to sliko posredovati naprej, na primer Gmail, Facebook, Dropbox itd. Tu moramo paziti, saj podatka ne dodamo prek metode setData, ampak prek putExtra.

Vračanje podatkov

Včasih želimo odpreti aktivnost zgolj zato, da dobimo iz nje nekakšen odgovor, kot je recimo iskanje določenega kontakta, slike, zvoka, videa ali drugih datotek. Načelo delovanja je tako, da ustvarimo nov namen in ga posredujemo v metodo startActivityForResult(Intent intent, int requestCode) – prvi parameter je naš namen, drugi pa unikatna številka, s katero ločimo rezultate. Primer:

1. private static final int REQUEST_SELECT_FILE = 1;

Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("file/*");
startActivityForResult(intent, REQUEST_SELECT_FILE);
Ta namen nam bo pokazal aktivnost za izbiro datoteke s pomnilnika.
2. private static final int REQUEST_SELECT_CONTACT = 2;

Intent intent = new Intent(Intent.ACTION_PICK);
intent.setDataAndType(Uri.parse("content://contacts"),
ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE);
startActivityForResult(intent, REQUEST_SELECT_CONTACT);
Ta namen pa bo pokazal aktivnost, v kateri bomo lahko izbrali kontakt od vseh, ki vsebujejo telefonske številke.

Izberimo kontakt.

Ko se aktivnost, ki smo jo klicali, zaključi, nam vrne odgovor prek metode onActivityResult(int requestCode, int resultCode, Intent intent) – prvi parameter je številka, ki smo jo podali v metodo startActivityForResult, drugi parameter nam pove, ali je uporabnik izbral podatek ali preklical izbiranje, tretji pa vsebuje vrnjen podatek. Metodo onActivityResult moramo v našem razredu *Activity povoziti in se odločiti, kaj napraviti z rezultatom:

@Override
protected void onActivityResult(int requestCode,
int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
if (requestCode == REQUEST_SELECT_FILE) {
String file = data.getData().toString();
Toast.makeText(this, "Datoteka: " + file,
Toast.LENGTH_LONG).show();
} else if (requestCode == REQUEST_SELECT_CONTACT) {
String phone = getPhoneNumber(data.getData());
Toast.makeText(this, "Telefon: " + phone,
Toast.LENGTH_LONG).show();
}
}
}
Če je izbral datoteko, mu izpišemo tekst »Datoteka: <ime datoteke>«, če je izbiral kontakt, pa mu izpišemo »Telefon: <tel. št.>«.

Zaključek

Aktivnosti in nameni so temelji vsake androidne aplikacije. Z dobro zastavljenim zaporedjem aktivnosti preprečimo marsikatero nevšečnost in uporabniku ponudimo znani delovni tok (workflow), ki je prisoten v vseh drugih aplikacijah. Zato si je že pred začetkom razvijanja dobro vzeti čas in narediti natančen načrt aktivnosti ter si s tem olajšati nadaljnji razvoj.

Kodo, uporabljeno v tem članku, najdete na GitHubu: http://goo.gl/6UFjgJ. Če pa imate še kakšno vprašanje, mi lahko pošljete e-pošto.

In na koncu ne pozabite, da je dobra aktivnost zdrava tako za telo kot tudi za aplikacijo.

Moj mikro, marec - april 2014 | Rok Končina