$30 off During Our Annual Pro Sale. View Details »

Android School 05

Android School 05

Przetwarzanie długotrwałych zadań w wątkach tła, użycie klasy AsyncTask i handlerów. HTTP — Apache HttpClient, HttpURLConnection, parsowanie XML i JSON.

Adam Jodłowski

April 15, 2013
Tweet

More Decks by Adam Jodłowski

Other Decks in Programming

Transcript

  1. Android School
    Warsztat 05
    Adam Jodłowski

    View Slide

  2. 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)

    View Slide

  3. 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();

    View Slide

  4. 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);
    .

    View Slide

  5. 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)

    View Slide

  6. 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()

    View Slide

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

    View Slide

  8. 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"
    .

    View Slide

  9. 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
    .
    .
    .
    .
    .

    View Slide

  10. 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?)

    View Slide

  11. 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();

    View Slide

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

    View Slide

  13. 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();
    }

    View Slide

  14. 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);

    View Slide

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

    View Slide

  16. 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

    View Slide

  17. 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

    View Slide

  18. 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);
    }
    }

    View Slide

  19. 19
    Przetestuj techniki parsowania danych.

    View Slide

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

    View Slide