Slide 1

Slide 1 text

Android School Warsztat 05 Adam Jodłowski

Slide 2

Slide 2 text

2 Wątek UI ● podobnie do innych środowisk wizualnych, Android renderuje interfejs i reaguje na akcje użytkownika w tzw. wątku interfejsu użytkownika ● domyślnie całe przetwarzanie aplikacji odbywa się w tym wątku, jeśli nie postanowimy inaczej ● długotrwałe obliczenia musimy wykonywać poza wątkiem UI, w przeciwnym przypadku system wyświetli użytkownikowi zachętę do natychmiastowego przerwania działania naszej aplikacji (ANR a.k.a. FC)

Slide 3

Slide 3 text

3 Thread ● klasyczny wątek znany z Javy (mamy też dostęp do klas z java.util.concurrent), pamiętajmy, że nie możemy manipulować interfejsem z wątków tła ● Android jest oparty o tzw. single thread model, wątek UI nie zachowuje bezpieczeństwa przy dostępie współbieżnym i nie możemy odwoływać się do niego z innego wątku Thread t = new Thread() { public void run() { // przetwarzanie w tle } }; t.start();

Slide 4

Slide 4 text

4 Handler ● najlepiej utworzyć go jako pole w klasie aktywności i odwołać się do niego z naszej implementacji Thread ● jest on automatycznie wiązany z kolejką wiadomości głównego wątku i może aktualizować interfejs private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { // odbior wiadomosci, aktualizacja UI } }; ● proces wysyłania komunikatów do handlera: Message message = new Message(); // mozna lepiej Bundle bundle = new Bundle(); bundle.putString("status", "COMPLETED"); message.setData(bundle); handler.sendMessage(message); // handler.sendEmptyMessage(int); .

Slide 5

Slide 5 text

5 AsyncTask ● API Androida może zająć się zarządzaniem wątkami, pozostawiając nam implementację logiki i jednokrotne wykonanie metody execute([1]) zadania ● musimy stworzyć własną klasę rozszerzającą AsyncTask<[1], [2], [3]> i przesłonić potrzebne nam metody onPreExecute() // inicjalizacja, UI [3] doInBackground([1]) // glowne przetwarzanie, metoda wymagana onProgressUpdate([2]) // aktualizacja postepu, UI onPostExecute([3]) // przekazanie wynikow, UI ● AsyncTask może być uruchomiony tylko jeden raz, aktualizacji postępu dokonujemy dzięki metodzie publishProgress([2]) ● możemy też anulować zadanie wywołując metodę cancel(boolean) i sprawdzać cyklicznie czy nie nastąpiło anulowanie za pomocą metody isCancelled() ● w przypadku anulowania, zamiast onPostExecute(Object) zostanie wywołana metoda onCancelled(Object)

Slide 6

Slide 6 text

6 Timer ● pozwala na zlecanie zadań TimerTask do sekwencyjnego wykonania we własnym wątku o określonym czasie i opcjonalnie z powtarzaniem Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { // wykonanie dzialania z watku tla } }, 2000); ● anulowanie wszystkich zadań wymaga wywołania metody cancel()

Slide 7

Slide 7 text

7 Przetestuj mechanizmy wykonywania długotrwałych zadań w tle.

Slide 8

Slide 8 text

8 Zmiany konfiguracji ● są to wszystkie zmiany stanu urządzenia, prowadzące do zniszczenia (wywołując kolejne metody cyklu życia) i restartu widocznej aktywności ● może to być zmiana orientacji ekranu, podłączenie stacji dokującej, zestawu słuchawkowego, wysunięcie klawiatury sprzętowej i inne ● zapobiegamy restartom deklarując obsługę zmian w manifeście android:configChanges="keyboardHidden|orientation|screenSize" ● otrzymamy dzięki temu wywołanie zwrotne przy zmianie konfiguracji @Override public void onConfigurationChanged(Configuration newConfig) { if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { ... } } ● wszystkie zasoby (getResources()) zostaną na tym etapie przeładowane i możemy skorzystać z nich w reakcji na zmianę konfiguracji ● wymuszamy stałą orientację ekranu aktywności android:screenOrientation="portrait" .

Slide 9

Slide 9 text

9 Save/RestoreInstanceState ● w przypadku nagłego przerwania działania naszej aktywności, możemy zapisać i odtworzyć ulotne dane @Override public void onSaveInstanceState(Bundle savedInstanceState) { super.onSaveInstanceState(savedInstanceState); savedInstanceState.putInt("count", 666); savedInstanceState.putString("name", "Android"); } ● tak zapisany obiekt Bundle trafi do onCreate() oraz do onRestoreInstanceState() po restarcie aktywności . . . . .

Slide 10

Slide 10 text

10 NonConfigurationInstance ● jeśli zrestartowanie aplikacji spowoduje potrzebę długotrwałego odtwarzania poprzedniego stanu danych, możemy skorzystać z możliwości utrwalenia własnego obiektu i odebrania go w niezmienionej formie po ponownym uruchomieniu aktywności @Override public Object onRetainNonConfigurationInstance() { final MyData data = collectMyData(); return data; } ● jeśli aktywność została zrestartowana, otrzymamy obiekt z powrotem @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); final MyData data = (MyData) getLastNonConfigurationInstance(); if (data == null) { data = loadMyData(); } } ● pamiętajmy, aby nie zwracać w ten sposób obiektów ściśle związanych z aktywnością, kontekstem, komponentami wizualnymi i innymi (dlaczego?)

Slide 11

Slide 11 text

11 Globalny obiekt aplikacji ● za jego pomocą możemy przechowywać globalny stan powiązany z kontekstem ważnym przez cały czas życia aplikacji w pamięci public class MyApplication extends Application { public void metodaNaszejAplikacji() { ... } @Override public void onCreate() { super.onCreate(); // inicjalizacja skladowych } } ● wskazanie klasy aplikacji w manifeście ● użycie w komponencie składowym naszej aplikacji ((MyApplication) getApplication()).metodaNaszejAplikacji();

Slide 12

Slide 12 text

12 Przetestuj sposoby reakcji na zmiany konfiguracji urządzenia i zachowaj w odpowiedni sposób ulotne dane instancji.

Slide 13

Slide 13 text

13 Łączenie z Internetem ● Android pozwala na standardowe tworzenie gniazd TCP oraz UDP, zawiera klasę (Http)URLConnection oraz Apache HttpClient URL url = new URL("http://www.android.com/"); HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); try { InputStream in = new BufferedInputStream(urlConnection.getInputStream ()); // czytamy stream... } finally { urlConnection.disconnect(); }

Slide 14

Slide 14 text

14 Apache HttpClient ● utworzenie klienta HttpClient httpClient = new DefaultHttpClient(); ● przykład użycia GET HttpGet httpGet = new HttpGet("http://host/resource"); httpGet.addHeader("Authorization", "Basic c2NvdHQ6dGlnZXI="); ResponseHandler responseHandler = new BasicResponseHandler(); String response = httpClient.execute(httpGet, responseHandler); ● przykład użycia POST List postParameters = new ArrayList(); postParameters.add(new BasicNameValuePair("body", "body content")); postParameters.add(new BasicNameValuePair("token", "auth token")); HttpPost httpPost = new HttpPost("http://host/resource"); httpPost.setEntity(new UrlEncodedFormEntity(postParameters)); String response = httpClient.execute(httpPost, responseHandler);

Slide 15

Slide 15 text

15 Połącz się z Internetem. Zapoznaj się z możliwościami Android Asynchronous Http Client (http://loopj.com/android-async-http).

Slide 16

Slide 16 text

16 JSON ● Android zawiera bilbioteki pakietu json.org do pracy z formatem JavaScript Object Notation JSONObject json = new JSONObject(); try { json.put("name", "scott"); json.put("surname", "tiger"); json.put("age", 666); } catch (JSONException e) { e.printStackTrace(); } Log.d("AS", json); // {"name": "scott", "surname": "tiger", "age": 666} ● w formacie JSON rozróżniamy obiekty i tablice obiektów, możemy pobierać z nich wartości proste i łańcuchy znaków JSONObject json = new JSONObject(response); JSONArray elements = json.getJSONArray("elements"); JSONObject obj = elements.getJSONObject(0); long id = obj.getLong("id"); ● biblioteki takie jak Google GSON i Jackson pozwalają uprościć pracę z modelami obiektów reprezentowanymi jako dane JSON i stosować silne typowanie

Slide 17

Slide 17 text

17 XML ● Android wspiera obsługę XML Pull Parser, DOM w wersji 2 oraz SAX // tworzymy fabryke parserow i nowy parser SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser sp = spf.newSAXParser(); // pobieramy uchwyt do elementu przetwarzajacego XML XMLReader xr = sp.getXMLReader(); // tworzymy i przekazujemy wlasny mechanizm przetwarzania MyXmlHandler myXmlHandler = new MyXmlHandler(); xr.setContentHandler(myXmlHandler); // parsujemy xr.parse(new InputSource(url.openStream())); ● do parsowania XML, musimy dostarczyć implementację własnego handlera parsującego treść dokumentu

Slide 18

Slide 18 text

18 XmlHandler ● przykładowa implementacja private class MyXmlHandler extends DefaultHandler { private boolean inName = false; private boolean inSurname = false; @Override public void startDocument() throws SAXException {...} // oraz endDocument() @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { if (localName.equals("name")) { this.inName = true; } else if (localName.equals("surname")) { this.inSurname = true; } } @Override public void endElement(String namespaceURI, String localName, String qName) throws SAXException {...} @Override public void characters(char ch[], int start, int length) { String content = new String(ch, start, length); } }

Slide 19

Slide 19 text

19 Przetestuj techniki parsowania danych.

Slide 20

Slide 20 text

20 Zadanie domowe: Stwórz aplikację pobierającą dane z wybranej usługi sieciowej odporną na zmiany konfiguracji urządzenia.