akademia androida Http i AsyncTask część VII
agenda 1. 2. 3. 4. URLConnection i HttpURLConnection UI Thread i ANR AsyncTask Zadanie
1. URLConnection i HttpURLConnection Jest to klasa, dzięki której możemy wysyłać i odbierać dane poprzez sieć. Nie musimy znać rozmiaru przesyłanych danych, co jest sporą zaletą. Klasa HttpURLConnection dziedziczy z klasy URLConnection (instancje tej klasy nie nadają się do ponownego użycia: dla każdego połączenia musi zostać stworzony nowy obiekt). Klasa URLConnection obsługuje 4 rodzaje protokołów: URI (jako łącze lokalne) FTP HTTP i HTTPS Jar Używanie klasy HttpURLConnection opiera się o taki schemat: 1. Stworzenie obiektu klasy HttpURLConnection i uzyskanie połączenia dzięki metodzie openconnection(). Otrzymany wynik rzutujemy. 2. Przygotowujemy żądanie. Podstawowym parametrem żądania jest URI (w naszym przypadku URL). Nagłówek żądania może również zawierać metadane takie jak np. pliki cookies. 3. Odczytać odpowiedź. Nagłówek otrzymany w odpowiedzi zwykle zawiera metadane takie jak typ i długość zwróconego ciała żądania, datę, czy pliki cookies. Dzięki metodzie getinputstream(), która zwraca strumień z wiadomością możemy odczytać odpowiedź. W przypadku, gdy wynik naszego żądania jest pusty, metoda zwraca pusty strumień. 4. Rozłączyć się. Kiedy odczytamy ciało żądania połączenie należy zakończyć. Służy do tego metoda disconnect(). Dzięki niej zwalniamy zasoby wykorzystywane przy połączeniu (można je użyć ponownie). Przykład nawiązania połączenia ze stroną http:/// : URL url = new URL("http:///"); HttpURLConnection urlconnection = (HttpURLConnection) url.openconnection(); try { InputStream in = new BufferedInputStream(urlConnection.getInputStream()); readstream(in); finally { urlconnection.disconnect();
Metoda readstream(): private void readstream(inputstream in) { BufferedReader reader = null; try { reader = new BufferedReader(new InputStreamReader(in)); String line = ""; while ((line = reader.readline())!= null) { System.out.println(line); catch (IOException e) { e.printstacktrace(); finally { if (reader!= null) { try { reader.close(); catch (IOException e) { e.printstacktrace(); Ważne: nie możemy wykonywać połączenia w wątku głównym aplikacji (UI Thread) w podany sposób. Eleganckim, i zarazem praktycznym wyjściem z tej sytuacji jest użycie mechanizmu udostępnianego przez API Androida zwanego AsyncTask. 2. UI Thread i ANR Android, jak większość dzisiejszych systemów operacyjnych wspiera wielowątkowość. Nie każdy jednak zdaje sobie sprawę z tego, że aby z niej skorzystać, musimy jawnie określić jakie zadania naszej aplikacji mają być wykonywane asynchronicznie, tj. poza głównym wątkiem aplikacji. Należy bowiem pamiętać, że wszystkie komponenty naszej aplikacji zarówno te widoczne (Aktywności), jak i te, które teoretycznie pracują w tle (Broadcast Receivers, Usługi) uruchomione są w tym samym wątku głównym (UI Thread). Gdy w wątku głównym wykonuje się jakaś czasochłonna czynność, użytkownik ma wrażenie, że aplikacja się zawiesiła, ponieważ żaden element interfejsu nie reaguje na jego akcje. Aby zapobiec zawieszeniom, w systemie funkcjonuje mechanizm dbający o płynność działania interfejsu. Jego zadaniem jest sprawdzanie czy aplikacja nie łamie
jednej z dwóch zasad: Czas reakcji interfejsu na zdarzenie (dotknięcie ekranu, naciśnięcie przycisku itp.) musi być mniejszy niż 5 sekund. Czas wykonania zadania przez BroadcastReceiver musi być mniejszy niż 10 sekund. Kiedy któraś z powyższych zasad jest łamana, wyświetlane zostaje okno dialogowe ANR (Application Not Responding). Daje ono użytkownikowi możliwość zatrzymania naszej aplikacji, co skutkuje jej awaryjnym wyłączeniem. 3. AsyncTask Abstrakcyjna klasa AsyncTask jest prostym mechanizmem, który pozwala na przeniesienie czasochłonnych zadań do nowego wątku. Oprócz tego umożliwia ona wykonywanie zadań w głównym wątku aplikacji (UI Thread), dzięki czemu w łatwy sposób możemy sterować interfejsem użytkownika. Przykład deklaracji klasy wykorzystującej AsyncTask: private class LongTask extends AsyncTask<Integer, Integer, Long> { //class code Klasy rozszerzające AsyncTask są deklarowane jako prywatne i wykorzystywane wewnątrz danej aktywności. Parametry klasy (typy generyczne, nie można używać typów prymitywnych, jak np. : int, boolean, itd.) AsyncTask<Params, Progress, Result> : Params typ danych wejściowych Progress typ danych przedstawiających postęp w działaniu Result typ zwracany przez zadanie Gdy któregoś nie potrzebujemy wstawiamy Void. Metody, które możemy przeciążyć dziedzicząc po AsyncTask to: onpreexecute() wywoływana zaraz przed uruchomieniem właściwego zadania. Metoda ta wykonywana jest w wątku głównym aplikacji, dzięki czemu
możemy w niej skonfigurować interfejs aplikacji (np. wyświetlić ProgressBar albo zablokować przyciski). doinbackground(params params) jedyna metoda, która uruchamiana jest w oddzielnym wątku. To tutaj powinniśmy umieścić wszystkie czasochłonne czynności. Nie ma dostępu do wątku głównego aplikacji, w związku z czym jedynym sposobem na zaktualizowanie stanu interfejsu jest wywołanie metody publishprogress(progress values). onprogressupdate(progress onpostexecute(result oncancelled() - values) metoda wywoływana jest w momencie wywołania publishprogress( ) wspomnianego powyżej. Również wykonywana jest w głównym wątku aplikacji, w związku z czym służy ona do aktualizacji informacji o postępie wykonywanej czynności. result) wywoływana w momencie zakończenia pracy doinbackground( ). Argumentem metody jest wynik zwrócony przez doinbackground( ). Również i tutaj mamy dostęp do komponentów interfejsu, w związku z czym z tego miejsca konfigurujemy wszystkie widoki po wykonaniu pracy. w związku z tym, że możemy w każdej chwili anulować wykonywanie zadania (metoda cancel( )), możemy zaimplementować obsługę takiego stanu. W takim wypadku jednak metoda doinbackground( ) będzie nadal wykonywana do końca. Natomiast po jej zakończeniu zamiast wykonać onpostexecute( ) wykonana będzie metoda oncancelled(). Typy argumentów z metod doinbackground( ), onpostexecute( ) są parametrami klasy AsyncTask. onprogressupdate( ) By uruchomić zadanie, wystarczy wywołać metodę klasy LongTask: execute(). oraz Przykład dla private void launchmytask(){ new LongTask().execute();
Prosta aplikacja odświeżająca ProgressBar: package pl.edu.zut.wi.mad; android.app.activity; android.os.asynctask; android.os.bundle; android.view.view; android.widget.button; android.widget.progressbar; public class MainActivity extends Activity { final private int START_PROGRESS = 0; final private int STOP_PROGRESS = 100; private Button button; private ProgressBar progressbar; /** Called when the activity is first created. */ public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.main); progressbar = (ProgressBar)findViewById(R.id.progressbar_Horizontal); button = (Button)findViewById(R.id.button); button.setonclicklistener(new View.OnClickListener() { public void onclick(view v) { new WymagajacyWatek().execute(""); ); private class WymagajacyWatek extends AsyncTask<String, Integer, String>{ protected String doinbackground(string... arg0) { int i,a; for(i = 0; i < 100; i++){ publishprogress(i); a = 0; while(a<1500){ a++;
return null; protected void onpostexecute(string result) { setprogressbar(stop_progress); button.setenabled(true); protected void onpreexecute() { //"wyzerujemy" progress bar setprogressbar(start_progress); //zablokujmy przycisk na czas dzialania watku button.setenabled(false); protected void onprogressupdate(integer... progress) { setprogressbar(progress[0]); private void setprogressbar(int progress){ progressbar.setprogress(progress); 4. Zadanie Stworzyć layout składający się z 3 elementów: Buttona, ProgressBara (poziomego) i ImageView Po wciśnięciu Buttona ma odpalać się zdarzenie obsługiwane przez AsyncTask Na czas działania zdarzenia AsyncTask wyłączamy obsługę Buttona W klasie dziedziczącej z AsyncTask musimy obsłużyć: połączenie ze stroną pobranie obrazka i zapisanie go na kartę pamięci urządzenia odświeżanie stanu ProgressBara (uzależnić postęp na pasku od rozmiaru pliku) wyświetlenie pobranego obrazka z pamięci telefonu w ImageView
Dziękuję za uwagę kontakt: dglinski@wi.zut.edu.pl mad@zut.edu.pl