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.
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)
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();
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); .
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)
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()
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" .
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 . . . . .
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?)
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 <application android:name=".MyApplication" ... > • użycie w komponencie składowym naszej aplikacji ((MyApplication) getApplication()).metodaNaszejAplikacji();
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
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