Professional Documents
Culture Documents
Vjezba5-T Odt PDF
Vjezba5-T Odt PDF
Vjezba5-T Odt PDF
U prošlim vježbama smo vidjeli na koji način je moguće koristiti resurse sa Interneta u
Android aplikaciji i kako akciju dobavljanja podataka prebaciti u posebnu nit kako ne bi
blokirala glavnu nit zaduženu za ažuriranje korisničkog interfejsa.
U ovim vježbama vidjet ćemo kako popraviti propust koji imamo ukoliko koristimo
AsyncTask za odvajanje akcije u novu nit. Mana AsyncTask-a je da ukoliko aktivnost koja
sadrži navedeni AsyncTask prestane biti vidljiva ili ako se uređaj rotira taj AsyncTask se
ponovo pokreće. Odnosno ako aktivnost nije vidljiva AsyncTask ne zna gdje da vrati
razultat jer se metoda onPostExecute() izvršava u glavnoj niti aktivnosti. U nastavku teksta
biće opisano jedno rješenje, a prije toga ponovit ćemo značenje termina Servis u okviru
android aplikacije.
Servisi su komponente koje omogućavaju izvršavanje dugih zadataka i oni ostaju aktivni i
u slučajevima kada njihove aplikacije nisu vidljive. Servisi imaju veći prioritet od aktivnosti i
ukoliko se desi da Android nema dovoljno resursa servis će imati manje šanse da bude
ugašen od aktivnosti. Aplikacije koje imaju neki posao koji se izvršava u pozadini imaju
veći prioritet od aplikacija koje to nemaju.
Servisi koriste glavnu nit pa zbog toga zadatke koji su vremenski zahtjevni treba prebaciti
u posebni thread. Ukoliko stavimo AsyncTask unutar servisa osigurali smo se od
restartovanja AsyncTaska jer se servis neće prekidati čak i kada je aplikacijia
minimizovana.
AsyncTask-ove koji imaju dugotrajno izvršavanje treba stavljati u Android servise koji će
se izvršavati bez prekida i kada se aplikacija minimizuje ili ako se uređaj rotira.
Razlika između servisa i aktivnosti je ta što servisi nemaju svoj odgovarajući korisnički
interfejs. Aktivnosti su dizajnirane da se pokreću, restartuju i zatvaraju tokom svog
životnog ciklusa, dok su servisi dizajnirani da duže traju. Servisi zavise od aktivnosti,
broadcast receivera i drugih servisa. Oni kontrolišu njihovo kreiranje, kontrolisanje i
zaustavljanje.
Pregled mehanizama za izvršavanje zadataka:
Service Thread IntentService AsyncTask
Kada koristiti? Zadatak bez Za dugotrajne Dugotrajni zadaci, Za zadatke koji bi
korisničkog zadatke. Za periodični zadaci. inače blokirali glavni
interfejsa. Ne bi paralelno thread.
trebao biti dug jer izvršavanje
se izvršava na zadataka.
glavnom threadu.
Pokretanje onStartService() start() Putem Intenta execute()
Pozvan iz ... Bilo kojeg threada Bilo kojeg threada Glavne niti Glavne niti
Izvršava se na ... Glavnom threadu U zasebnom U zasebnom U zasebnom
threadu threadu threadu
Ograničenja Blokira glavni Ručno upravljanje Pojedinačne Jedna instanca se
thread izvršavanjem zadatke ne izvršava može pozvati samo
threada, kod paralelno, višestruki jednom.
postaje kompleksan pozivi se dodaju na Vezan za životni
vrlo brzo. message queue. ciklus glavne niti.
Kreiranje servisa
Da bi kreirali servis potrebno je implementirati klasu koja je nasljeđena iz Service klase.
Nakon kreiranja klase obavezni smo implementirati dvije metode onCreate i onBind.
Kako je servis komponenta aplikacije potrebno ju je registrovati u manifest fajlu. Za
registrovanje servisa koristimo <service> tag, njega smještamo unutar <application> tag-a.
Unutar <service> taga potrebno je navesti dva atributa android:enabled="true" i naziv sa
atributom android:name.
<service android:enabled=”true” android:name=”.MyService”/>
Ukoliko želimo da osiguramo naš servis, da druge aplikacije ne mogu pristupati njemu bez
dobijanja odgovarajući dozvola koristimo još jedan atribut unutar <service> tag-a. Atribut
android:permission postavljamo na vrijednost naziva permisije koju zahtjevate da druga
aplikacija ima kako bi koristila servis vaše aplikacije.
Pokretanje servisa
Da bi mogli pokrenuti servis potrebno je dodati još jednu metodu u klasu serisa. Metoda
onStartCommand priprema servis za početak njegovog izvršavanja. Kako se sve što se
nalazi u onStartCommand metodi pokreće u glavnoj niti praksa je da se na početku
onStartCommand metode pokrene nova nit, a da se prilikom zatvaranja servisa zatvori i
ova nit. Na ovaj način smo osigurali da servis ne blokira glavnu nit aplikacije.
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
startBackgroundTask(intent, startId);
return Service.START_STICKY;
}
Način zaustavljanja servisa zavisi od toga kako ste ga pokrenuli. Ako se radi o
eksplicitnom pokretanju potrebno je samo pozvati metodu stopService i kao parametar
prosljediti novi (anonimni) intent koji ima klasu servisa kao parametar.
U slučaju servisa koji je pokrenut implicitno potrebno je kreirati novi impcitni intent u kojem
je navedena akcija koja je bila i prilokom pokretanja servisa, nakon čega je potrebno
pozvati stopService i prosljediti novokreirani intent.
Jedno pozivanje stopService metode će zatvoriti odgovarajući servis bez obzira koliko
puta je pozvan startService nad tim servisom.
Intent servisi
Android posjeduje klasu koja implementira najbolje prakse za izvršavanje servisa u
pozadini koji se pozivaju povremeno na zahtjev aplikacije, poput update-a sa interneta i sl.
Klasa koja implementira opisane funkcionalnosti je IntentService. IntentService reda
zahtjeve za servisom u redu kako su došli i izvršava ih sekvencijalno, jedne za drugim.
Izvršavanje pojedinog zadatka je asinhrono, u odvojenom threadu od glavnog thread-a.
IntentService interno koristi HandlerThread. HandlerThread je baziran na dvije
komponente Looper i MessageQueue. MessageQueue je red poruka koje dolaze na
izvršavanje u niti. Poruku ovdje možete smatrati kao posao koji nit treba da obavi. Pošto
jedna nit može samo da radi jedan zadatak u jednom trenutku ostali zadaci se dodaju u
red. Looper prolazi kroz red poruka i osigurava da će nit dobiti sljedeći posao. Svaki
sljedeći zadatak se dodaje u red, a kada nit bude slobodna looper će ga isporučiti nit.
IntentService zaprima zahtjeve putem intentova koji sadrže sve potrebne parametre za
izvršavanje jednog zahtjeva i vrši raspoređivanje njihovog izvršavanja umjesto vas.
Pored raspoređivanja reda izvršavanja zahtjeva IntentService vrši kreiranje niti koja će se
izvršavati u pozadini i vrši sinhronizaciju sa UI niti.
Da bi se implementirao IntentService potrebno je kreirati klasu koja je izvedena iz klase
IntentService i koja sadrži implementiranu metodu onHandleIntent. Metoda onHandleIntent
treba da sadrži posao koji je vremenski zahtjevan, odnosno većinu procesiranja koje servis
radi. Ova metoda će se izvršavati u posebnoj niti, a sav posao oko kreiranja,
sinhronizovanja i uništavanje te niti obavlja IntentService umjesto vas.
Potrebno je implementirati i metodu onCreate pomoću koje postavljate početne uslove za
izvršavanje servisa, kao i konstruktor koji prima parametar tipa string.
lass MyIntentService extends IntentService {
public c
public M yIntentService() {
super(null);
}
public MyIntentService(String name) {
super(name);
// Sav posao koji treba da obavi konstruktor treba da se
// nalazi ovdje
}
@Override
public void onCreate() {
super.onCreate();
// Akcije koje se trebaju obaviti pri kreiranju servisa
}
@Override
protected void onHandleIntent(Intent intent) {
// Kod koji se nalazi ovdje će se izvršavati u posebnoj niti
// Ovdje treba da se nalazi funkcionalnost servisa koja je
// vremenski zahtjevna
}
}
Nakon što smo kreirali intent potrebno je specificirati način kako da vratimo podatke iz
intenta. Jedan od mnogobrojnih načina je da koristimo klasu ResultReceiver. Ova klasa je
ustvari omotač oko klase Binder. Binder je mehanizam za komunikaciju između procesa.
Implementaciju navedene klase ćemo opisati poslije.
Kao i za sve intentove koje smo dosada pozivali potrebno je proslijediti neke dodatne
podatke putem putExtra metode.
intent.putExtra("parametar1", vrijednost);
intent.putExtra("parametar2", vrijednost);
intent.putExtra("receiver", mReceiver);
Kada smo postavili sve vrijednosti potrebne za pozivanje intenta potrebno je da pozovemo
navedeni intent:
startService(intent);
// Ukoliko startate iz fragmenta pozivajte kao getActivity().startService(intent);
Implementiranje ResultReceiver-a
Potrebno je implementirati ResultReceiver klasu:
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (mReceiver != null) {
mReceiver.onReceiveResult(resultCode, resultData);
}
}
}
Kada smo kreirali klasu za ResultReceiver potrebno je dodati i u aktivnost implementaciju
interfejsa i njegove metode koja će se pozivati prilikom dobivanja obavještenja od
IntentService-a.
Tako u aktivnosti dodajemo da ona implementira interfejs MojResultReceiver.Receiver:
/* Primjer: */
public class MyActivity extends Activity implements MojResultReceiver.Receiver {
@Override
public void onReceiveResult(int resultCode, Bundle resultData) {
switch (resultCode) {
case MyIntentService.STATUS_RUNNING:
/* Ovdje ide kod koji obavještava korisnika da je poziv upućen */
break;
case MyIntentService.STATUS_FINISHED:
/* Dohvatanje rezultata i update UI */
String[] results = resultData.getStringArray("result");
break;
case MyIntentService.STATUS_ERROR:
/* Slučaj kada je došlo do greške */
String error = resultData.getString(Intent.EXTRA_TEXT);
Toast.makeText(this, error, Toast.LENGTH_LONG).show();
break;
}
}
Zadaci: Implementirati sve pozive web servisa preko IntentService-a sa prošle vježbe. Na
kraju vježbe trebate imati bar jedan poziv web servisa realizovan koristeći IntentService.
Da li bi bio bolji AsyncTask ili IntentService za zadatak koji zahtijeva osvježavanje
vremenske prognoze svakih pet minuta? Kako biste implementirali ovu funkcionalnost?