Service ● Ziel – – ● Lösungsansatz: Service – – – – – ● Komponente mit Lebenszyklus unabhängig von der Activity/UI Dienste, die ohne UI laufen (im Hintergrund) Android Komponente, ohne Benutzungsoberfläche Nutzen von Diensten durch andere Komponenten Ablauf: Binden an Dienst, „Methodenaufruf“ oder Nachrichten Anderer Lebenszyklus als UI/Activity ContentProvider können als speziellen Service gesehen werden Nebenläufigkeit – – – Nebenläufigkeit und Service unabhängig voneinander Aufruf im (lokalen) Service immer noch im Main-Thread Falls Hintergrundoperation gewünscht, dann selbst Threading verwalten Prof. Dr. Peter Barth, Mobile Computing 1 Service – Local und Remote ● Service – – ● Local Service – – – ● Interagiert mit anderen Komponenten Andere Komponenten im selben Prozess (Lokal) oder in anderem Prozess (Remote) Service im gleichen Prozess, damit gleiche Anwendung Instanziieren lokal Auf Instanz kann nach Binden direkt zugegriffen werden Anwendung Local Service Remote Service Remote Service Remote Service – – Komp./ Activity Anwendung Service in anderem Prozess, damit potenziell in anderer Anwendung Instanziieren remote, für Kommunikation Serialisierung notwendig ● ● Nachrichten schicken und erwarten (asynchron) Binden und Remoting (synchron) aber trotzdem Serialisierung (RPC) Prof. Dr. Peter Barth, Mobile Computing 2 bindService Service – Lebenszyklus ● Anlass zum Erzeugen – – ● Binden mit bindService Starten mit startService onBind Effekt – – – ● onCreate Instanziieren und onCreate Je nach Anlass Methode onBind/onStartCommand Gestarteter Service im Main-Thread Letzer onStartCommand – Client – unbindService – abgemeldet Rückgabe ob Service weiterlaufen soll (sticky) START_[NOT_]STICKY Prof. Dr. Peter Barth, Mobile Computing Clients an Services gebunden startService onCreate onStartCommand Service gestartet und läuft onUnbind onDestroy Service beendet onDestroy Service beendet Service aktiv Service stoppt (selbst oder fremd) 3 bindService Service – Lebenszyklus ● Stoppen eines Service – Selbst mit stopSelf() ● – ● Machen, wenn der Dienst nicht mehr gebraucht wird Von außen mit stopService und passendem Intent Effekt – Nur falls keine Bindungen existieren ● – onCreate Bindungen werden aufgehoben Dann onDestroy und irgendwann GC Prof. Dr. Peter Barth, Mobile Computing onBind Clients an Services gebunden Letzer Client – unbindService – abgemeldet startService onCreate onStartCommand Service gestartet und läuft onUnbind onDestroy Service beendet onDestroy Service beendet Service aktiv Service stoppt (selbst oder fremd) 4 Service – Beispiel Addierer ● Addieren, Optionen – – – ● Service Funktionalität – – ● Als Lokaler Service [Als Remote Service] Als Remote Messaging Service Zwei Zahlen als Eingabe Ergebnis ist deren Addition Service Nutzen, Optionen – – Über Binden Über Starten mit Parameter ● Und potenziell Empfangen eines Ergebnisses über Messaging Prof. Dr. Peter Barth, Mobile Computing 5 LocalService – Binden, Realisieren ● Localer Service – – ● Lebt in derselben Java-VM Direkter Zugriff möglich Binden – – – Bei onBind muss ein Binder zurückgegeben werden Ein Binder ist etwas was IBinder implementiert, von Binder erben Ok Nur eine Dienstleistung, LocalService selbst zurückgeben! ● ● Darin dann Funktionalität doAdd Lebenszyklus unspannend public class LocalService extends Service { public class LocalBinder extends Binder { public LocalService getService() { return LocalService.this; } } private IBinder binder = new LocalBinder(); @Override public IBinder onBind(Intent intent) { return binder; } public Integer doAdd(Integer zahl1, Integer zahl2) { int ret = 0; if (zahl1 != null) ret += zahl1.intValue(); if (zahl2 != null) ret += zahl2.intValue(); return Integer.valueOf(ret); } @Override public void onCreate() { super.onCreate(); } @Override public void onDestroy() { super.onDestroy(); } } LocalService.java <service android:name=".LocalService" android:exported="false" /> Prof. Dr. Peter Barth, Mobile Computing AndroidManifest.xml 6 LocalService – Verwenden, Bindung ● ServiceConnection – – Zum Binden benötigt, Callbacks onServiceConnected sobald Service gebunden wurde ● – ● LocalService-Instanz merken geht, da lokaler Service ...Disconnected, falls Verbindung unplanmäßig beendet wurde Binden, mit explizitem Intent – – private boolean localServiceBound = false; private LocalService localService; private ServiceConnection localServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName n, IBinder service) { localService = ((LocalService.LocalBinder) service).getService(); } @Override public void onServiceDisconnected(ComponentName n) { localServiceBound = false; localService = null; } }; ServiceApp.java public void onClickBindLocal(… bindService: intent, Intent intent = new Intent(this, LocalService.class); ServiceConnection, localServiceBound = bindService(intent, automatisch erstellen localServiceConnection, Context.BIND_AUTO_CREATE); true, falls Bindungsanfrage erfolgreich war – ist aber noch nicht gebunden, erst bei onServiceConnected Prof. Dr. Peter Barth, Mobile Computing Antipattern: in onClickBindLocal bei true direkt Service verwenden – geht schief! 7 LocalService – Verwenden, Aufruf ● Service verwenden – – – – ● Muss gebunden sein Direkter Aufruf von doAdd nur möglich, da es in derselben VM läuft (lokaler Service) Immer noch alles im UI-Thread Falls Asynchronität beim Service ausführen notwendig ist, selbst Threading realisieren Service – Lebenszyklus – – Lebenszyklus ist zum Erstellen und Zerstören des Objekts, nicht automatisch separater Thread Im Beispiel kein separater Thread notwendig Prof. Dr. Peter Barth, Mobile Computing public void onClickUseLocal(… if (localService == null) { Log.e(TAG, "local service not yet bound"); return; } setResult(localService.doAdd(getNumber1(), getNumber2())); Achtung, darf erst NACH erfolgreichem Durchlauf von onServiceConnected verwendet werden AntiPattern: Binden in onClickUseLocal erzwingen wollen/darauf warten. Das ist der UI-Thread – NIE WARTEN. Das Leben ist asynchron – gewöhnen Sie sich dran... 8 LocalService – Verwenden, Bindung Lösen ● Bindung lösen – – – ● Achtung: – ● Merken, dass Bindung gelöst wurde/wird Referenz lösen, wird nicht mehr genutzt unbindService: Anfragen beim System, wieder über localServiceConnection public void onClickUnbindLocal() { if (localServiceBound) { localServiceBound = false; localService = null; unbindService(localServiceConnection); } } onServiceDisconnected wird nicht aufgerufen (nur bei ungeplanmäßigem...) Effekt – – Bindung wird irgendwann gelöst Service kann weiter existieren, muss aber nicht ● Falls Service nur gebunden wurde, dann wird Service zerstört, sobald letzte Bindung gelöst wurde Prof. Dr. Peter Barth, Mobile Computing 9 LocalService – Start, StartParams ● Start/StartParams – – – ● Rückgabe Ergebnis – ● Anfrage Service zu starten Übergabe Parameter per Intent möglich Funktioniert auch remote Nicht möglich Verwendung trotzdem sinnvoll – Falls keine Rückgabe notwendig ● ● – Audio/Video player Fernbedienung, … Einfach und leicht umzusetzen Prof. Dr. Peter Barth, Mobile Computing Intent intent = new Intent(this, LocalService.class); intent.setAction("compute"); intent.putExtra("number1", getNumber1()); intent.putExtra("number2", getNumber2()); startService(intent); ServiceApp.onClickStartParams public int onStartCommand(Intent intent, int flags, int startId) { if (intent.getAction().equals("compute")) { Bundle extras = intent.getExtras(); Integer number1 = extras.getInt("number1"); Integer number2 = extras.getInt("number2"); Integer res = doAdd(number1, number2); Log.v(TAG, String.format("onStartCommand: %d+%d=%d", number1, number2, res)); // ???? WIE ZURÜCK ??? return START_NOT_STICKY; LocalService.java Rückgabe bestimmt Lebenszyklus: START_STICKY: Service soll weiterlaufen START_NOT_STICKY: Serivce kann von Android direkt beendet werden 10 LocalService – Ansage der Ergebnisse ● Gewünschtes Verhalten – – – ● Ansage der Ergebnisse Kein Blockieren des UIs Mehrere Ansagen hintereinander möglich Voraussetzung – Speaker (mit eigenem Threading) kümmert sich um eigentliche Aussprache public class Speaker { … public Speaker(Context context) { … } public void add(String message) { … } public void shutdown() { … } } Prof. Dr. Peter Barth, Mobile Computing „Siebzehn plus Zweiundvierzig ist gleich Neunundfünzig“ 11 LocalService – Realisieren, Aussprache ● Lebenszyklus – – – ● Initialisierung der Sprach-Engine in onCreate Shutdown der Sprach-Engine in onDestroy Teure Operationen onStartCommand – – – private Speaker speaker; @Override public void onCreate() { super.onCreate(); speaker = new Speaker(this); } public int onStartCommand(Intent intent, int flags, int startId) { if (intent.getAction().equals("say")) { Bundle extras = intent.getExtras(); Integer number1 = extras.getInt("number1"); Integer number2 = extras.getInt("number2"); Integer res = doAdd(number1, number2); speaker.add(String.format("%d + %d = %d", number1, number2, res)); return START_STICKY; Asynchron, } queue public void onDestroy() { Message speaker.shutdown(); } Aussprache Sicherstellen, das Service weiterläuft Sonst würde onDestroy ja Sprach-Engine herunterfahren obwohl Aussprache noch nicht fertig ist Prof. Dr. Peter Barth, Mobile Computing LocalService.java 12 LocalService – Verwenden, Aussprache ● Berechnen, Aussprache – – – ● Nur Starten sinnvoll – – ● Initialisierung der Speech-Engine kann dauern Vorher machen Nur Stoppen sinnvoll – – ● Service starten Einfach Action „say“ Für jede Anfrage wird einmal onStartCommand gerufen public void onClickStartParamsLocal(...) { Intent intent = new Intent(this, LocalService.class); intent.setAction("say"); intent.putExtra("number1", getNumber1()); intent.putExtra("number2", getNumber2()); startService(intent); } public void onClickStartLocal(...) { Intent intent = new Intent(this, LocalService.class); intent.setAction("init"); startService(intent); } public void onClickStopLocal(...) { Intent intent = new Intent(this, LocalService.class); stopService(intent); } Service läuft weiter Kann/muss? manuell angehalten werden ServiceApp.java ProgrammEnde? – Entweder stopService oder laufen lassen Prof. Dr. Peter Barth, Mobile Computing Aussprache auch als RemoteService sinnvoll 13 LocalService – Beenden Aussprache ● Service läuft weiter – – ● Ziel – – ● Nach Übergabe auszusprechender Text soll Service weiterlaufen Bisher läuft Service immer weiter... Bei Ende Aufgabe: Service beendet sich selbst TTS-Engine starten teuer, Ende nur falls mind. 5 Sekunden keine Nachricht gesprochen Umsetzung – – – – – private Handler handler; @Override public void onCreate() { super.onCreate(); handler = new Handler(getMainLooper()); speaker = new Speaker(this); Runnable onIdle = new Runnable() { @Override public void run() { handler.post(new Runnable() { @Override public void run() { stopSelf(); }}); } }; speaker.setOnIdle(onIdle); } stopSelf: Aufruf veranlasst Service sich selbst zu stoppen Dann wird irgendwann onDestroy aufgerufen stopSelf muss in Main-Thread (des Service) gerufen werden Speaker bietet Timeout Callback (in seinem Thread, TimerTask) an Handler-Magic zur Umsetzung Prof. Dr. Peter Barth, Mobile Computing ServiceApp.java 14 Anwendung Remote Service ● Remote Service – – ● Remote Service Erzwingen eines Remote Service – – – – ● Service Instanz in anderem Prozess Eventuell in anderer Anwendung Attribut setzen android:process=“<name>“ und <name> beginnt mit Doppelpunkt : Service Objekt wird dann in separatem Prozess angelegt Aufrufe nur remote möglich Automatisch Ausführen der Dienste in anderem Thread (ist ja anderer Prozess) Interprozess-Kommunikation notwendig – – IPC mit Messaging IPC mit Remoting (IDL) Prof. Dr. Peter Barth, Mobile Computing StartParams mit Speaker geht sofort ohne Änderung Komp./ Activity „Siebzehn plus Zweiundvierzig ist gleich Neunundfünzig“ 15 RemoteService – Naiv ● Naiver Ansatz – – Genauso implementieren Schlägt Fehl 03-12 20:29:46.977 9298-9298/de.medieninf.mobcomp.service E/AndroidRuntime﹕ FATAL EXCEPTION: main Process: de.medieninf.mobcomp.service, PID: 9298 java.lang.ClassCastException: android.os.BinderProxy cannot be cast to de.medieninf.mobcomp.service .RemoteService$RemoteBinder – ● Klar, getService kann nicht ein Objekt aus einem anderen Prozess zurückgeben private boolean remoteServiceBound = false; private RemoteService remoteService; private ServiceConnection remoteServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName n, IBinder service) { remoteService = ((RemoteService.RemoteBinder) service).getService(); } @Override public void onServiceDisconnected(ComponentName n) { remoteServiceBound = false; remoteService = null; } }; ServiceApp.java public void onClickBindLocal(… Intent intent = new Intent(this, RemoteService.class); remoteServiceBound = bindService(intent, remoteServiceConnection, Context.BIND_AUTO_CREATE); Andere Ansätze: NUR WENN NOTWENIG (wenn lokaler Service reicht, OK) – – Messaging, Serialisierung, nachrichtenbasiert Remoting (RPC), entfernte Methodenaufrufe Prof. Dr. Peter Barth, Mobile Computing 16 RemoteService – Ziel, Nachrichtenübertragung ● Ziel: Dienst darf in anderem Prozess laufen – – – ● Daten an Prozess übermitteln Berechnen Ergebnis zurück Lösung: Asynchrones Messaging – ● Geht schon, wie mit StartParams im Intent Alternativ: Nachtrichten erwarten Message zahl1 zahl2 replyTo Daten zurücksenden ● ● ● IPC Daten übermitteln ● – Komp./ Activity Remote Service Rückkanal beim Übermitteln erhalten Auch Rückkanal Antwort-Nachricht schicken Auch für asynchron genutzten LocalService zu verwenden Remote Service Komp./ Activity Message res Prof. Dr. Peter Barth, Mobile Computing 17 Nachrichten – Message ● Nachrichtenbasierte Kommunikation – – – – – ● Message Nachrichten aufbauen Absender: Vor Verschicken serialisieren (Objekt->Dokument, marshalling) Empfänger: Vor Verwenden deserialisieren (Dokument->Objekt, unmarshalling) Nachricht auslesen und reagieren Für Rückantwort wieder Nachricht mit vertauschten Rollen Message Android Message-Objekt, vordefiniert – Vordefinierte Attribute ● ● ● ● – – Zwei ints: arg1, arg2 Ein Object obj Ein int: what (ID) Ein Messenger: replyTo int: arg1, arg2 Object: obj int: what Messenger: replyTo (Un)Marshalling vordefiniert für Grundtypen, Wrapper, String Parcelable (und nicht Serializable, wird auch für Nicht-Java-Kommunikation verwendet) Prof. Dr. Peter Barth, Mobile Computing 18 RemoteService – Realisieren ● Konstanten – ● Nachrichten-Typen, im what der Message Messenger – – Fertige Android-Komponente Handler übergeben, zuständig für Abarbeiten einer Nachricht ● – WeakReference ● ● ● in handleMessage (gleich) Falls keine static class, dann leak der Service-Reference Service Reference wird aber gebraucht, also... public static final int MSG_ADD = 1; public static final int MSG_ADD_RESULT = 2; private final Messenger messenger = new Messenger(new IncomingHandler(this)); private static class IncomingHandler extends Handler { private WeakReference<RemoteService> wservice; public IncomingHandler(RemoteService service) { wservice = new WeakReference<RemoteService>(service); } @Override public void handleMessage(Message msg) { … } } @Override public IBinder onBind(Intent intent) { messenger.getBinder(); } RemoteService.java onBind: einfach, messenger kann notwendige IBinder-Instanz erzeugen http://stackoverflow.com/questions/11407943/this-handler-class-should-be-static-or-leaks-might-occur-incominghandler Prof. Dr. Peter Barth, Mobile Computing 19 RemoteService – Realisieren handleMessage ● handleMessage – – – – – ● An msg.what diskriminieren, nur MSG_ADD erlaubt Argumente holen Checken ob RemoteService noch da ist (kann wegen WeakRef schief gehen) währen shutdown Aufruf (kann schief gehen, remote) Rücknachricht schicken Im Main-Thread – @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_ADD: Integer number1 = Integer.valueOf(msg.arg1); Integer number2 = Integer.valueOf(msg.arg2); RemoteService service = wservice.get(); if (service == null) { Log.e(TAG, "already finishing service, ignore message "); return; } Integer res = service.doAdd(number1, number2); Message message = Message.obtain(null, MSG_ADD_RESULT, res, 0); if (msg.replyTo == null) { Log.e(TAG, "cannot reply to add message "); return; } try { msg.replyTo.send(message); } catch (RemoteException e) { Log.e(TAG, "caught RemoteException while replying"); return; } break; default: Super.handleMessage(msg); }} RemoteService.IncomingHandler Nicht schlimm wenn separater Prozess, bei LocalService in separatem Thread nutzen Prof. Dr. Peter Barth, Mobile Computing 20 RemoteService – Nutzen, Binden ● Binden – – – ● Wie lokal mit ServiceConnection und Boolescher Variable Nur messenger auf Basis des Service erzeugen und merken Rest genauso UnBind – Genau wie Lokal Prof. Dr. Peter Barth, Mobile Computing private boolean remoteServiceBound = false; private Messenger messengerSend; private ServiceConnection remoteServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { messengerSend = new Messenger(service); } @Override public void onServiceDisconnected(ComponentName name) { remoteServiceBound = false; messengerSend = null; } }; public void bindRemote() { Intent intent = new Intent(this, RemoteService.class); remoteServiceBound = bindService(intent, remoteServiceConnection, Context.BIND_AUTO_CREATE); } public void unbindRemote() { if (remoteServiceBound) { unbindService(remoteServiceConnection); remoteServiceBound = false; messengerSend = null; } 21 } ServiceApp.java RemoteService – Nutzen, Anfrage-Nachricht Schicken ● Nachricht erzeugen – – Static-Factory-Method um passende Nachrichten zu erzeugen Wir brauchen nur die drei Ints (what, arg1, arg2) ● – – – – Parcelable-Objekte in Object-Argument Antwort Messenger (replyTo) mitgeben (gleich) Checken ob noch aktiv Nachricht senden Bei „remote“ immer Fehler prüfen Prof. Dr. Peter Barth, Mobile Computing public void useRemote() { Message message = Message.obtain(null, RemoteService.MSG_ADD, getNumber1(), getNumber2()); message.replyTo = messengerReceive; try { Messenger sender = messengerSend; if (sender != null && remoteServiceBound) { messengerSend.send(message); } } catch (RemoteException e) { Log.e(TAG, "cannot send message, got RemoteException"); } Log.e(TAG,"ignoring message, RemoteService not available"); } 22 RemoteService – Nutzen, Antwort-Nachricht Empfangen ● Antwort-Messenger replyTo – – – ● Wieder Messenger mit Incoming-Handler Wieder WeakReference-Trick Wieder IncomingHandler handleMessage – – Muss Ergebnis-Nachricht sein Kann am Ende des Lebenszyklus passieren ● – ● Prüfen GUI aktualisieren Im UI-Thread – – private Messenger messengerReceive = new Messenger(new IncomingHandler(this)); private static class IncomingHandler extends Handler { private WeakReference<ServiceApp> wserviceapp; public IncomingHandler(ServiceApp serviceapp){ wserviceapp = new WeakReference<ServiceApp>(serviceapp); } @Override public void handleMessage(Message msg) { switch (msg.what) { case RemoteService.MSG_ADD_RESULT: ServiceApp serviceapp = wserviceapp.get(); if (serviceapp == null) { Log.e(TAG, "ignoring reply, shutting down"); return; } serviceapp.setResult(Integer.valueOf(msg.arg1)); break; default: super.handleMessage(msg); } } } Gut, da GUI aktualisiert wird Wenn es lange Berechnung gäbe, dann wieder mit Threading selbst auslagern Prof. Dr. Peter Barth, Mobile Computing 23 Alternative: RemoteService mit IPC ● Interprozess Kommunikation (IPC) – – – ● Schnittstellen, Android Interface Description Language (AIDL) Stubs (werden generiert) Verstecktes Messaging X Y Client Stub Schnittstelle X Y Unsichtbar Server Stub X Y Stubs Generierter Code auf Client und auf Server Seite – Stellt API (implementierte Schnittstellen) zur Verfügung – Agieren als Proxy für entfernten Methodenaufruf (RPC) AIDL vermeiden ● Meist reicht LocalService Methodenaufruf ● Wenn remote, dann – Kommunikation Parameter (mit Einschränkungen) messaging (versteckt nichts) ● Wenn es muss, dann AIDL – Messaging mit automatisiertem (Un)-Marshalling – ● Prof. Dr. Peter Barth, Mobile Computing https://developer.android.com/guide/components/aidl.html 24 RemoteService – Messaging Einsatzgebiete ● Empfohlen – – – ● RemoteService – – – ● LocalService, wenn notwendig mit selbst realisiertem Threading und Messaging wie eben Wenn nur ein (Worker-)Thread notwendig, dann IntentService Nur wenn das alles nicht passt, dann RemoteService Nur wenn Prozessgrenzen überschritten werden müssen Wenn Service nach außen angeboten werden muß Wenn Service sporadisch abbrechen kann (VM zerstört, native libs) … Remote oder Local Service mit Messaging – – – – Nachrichtenbasiert ist einfach! (auch wenn es zuerst nicht so aussieht) (Un)Marshalling geschenkt für viele einfache Typen, automatisch asynchron, Separater Worker-Thread nutzen (automatisch in neuem Prozess) Keine komplizierte? synchrone RPC/IPC notwendig Nachteile: Nur asynchron, nur für einfach strukturierte Daten einfach (sonst Parcelable) Prof. Dr. Peter Barth, Mobile Computing 25 Wiederholung – Warum Threading? ● Ziel: Schwupdizität – ● Wann separater Thread – – – – – ● Benutzungsoberfläche reagiert nie träge Alles was potenziell lange dauert Alles mit Netzwerk Alles mit Remoting (Remote Service) Alles mit File I/O (Fast) alles mit I/O Rahmen – – – 03-13 09:55:17.386 13018-13018/de.medieninf.mobcomp.nebenlaeufigkeit V/MainActivity﹕ compute: started for 4630 ms 03-13 09:55:22.036 13018-13018/de.medieninf.mobcomp.nebenlaeufigkeit V/MainActivity﹕ compute: done after 4645 03-13 09:55:22.036 13018-13018/de.medieninf.mobcomp.nebenlaeufigkeit I/Choreographer﹕ Skipped 3021 frames! The application may be doing too much work on its main thread. Unakzeptabel ab 1 Sekunde Störend für Benutzer ab 0,1 Sekunden (ruckeln ist BÄH!) Ziel: Nie! länger als 0,1 Sekunden im UI-thread! (Nehmen Sie sich 0,01 vor...) https://developer.android.com/guide/components/processes-and-threads.html Prof. Dr. Peter Barth, Mobile Computing 26 Beispiel – Aktualisieren TextFeld ● ProgressBar – – ● compute: Berechnung – – ● Flexible Dauer der Berechnung Dauer mit Slider einstellen update: Aktualisieren Textfeld – ● IndeterminateOnly, drehender Kreis Dreht sich nicht, wenn UI-Thread belegt Mit aktueller Uhrzeit, Millisekundenauflösung Basis für alle Demonstrationen – – Buttons für unterschiedliche Strategien Main, ist im UI-Thread Prof. Dr. Peter Barth, Mobile Computing 27 UI-Thread ● Main – Böse – – ● UI-Thread Berechnung im UI-Thread UI-friert ein public void onClickNeben(View view) { new Thread() { böse: UI-update @Override darf nur im public void run() { compute(); UI-Thread update(); erfolgen } }.start(); Neben – – – ● public void onClickMain(View view) { compute(); böse: Keine update(); } Berechnung im Separater Thread für Berechnung Problem: update auch in separatem Thread, Crash Single-Threaded UI! (wie alle UIs) } update hier sinnlos, Berechnung noch nicht fertig Fix – update muss wieder im UI-Thread laufen Prof. Dr. Peter Barth, Mobile Computing 28 update – UI-Thread ● runOnUIThread – – ● Methode der Activity Kann mit Runnable aufgerufen werden Effekt – – Im UI-Thread: Sofort ausführen In anderem Thread: Wird in die EventQueue des UI-Threads eingestellt und dann irgendwann abgearbeitet Prof. Dr. Peter Barth, Mobile Computing public void onClickNebenUIThread(View view) { Log.v(TAG, "onClickNebenUIThread"); new Thread() { @Override public void run() { compute(); MainActivity.this.runOnUiThread(new Runnable() { @Override public void run() { update(); } }); } }.start(); } Syntax nicht so toll für häufiges Verwenden 29 update – mit Handler, post ● Handler – – – UI-Thread hat eine Message-Queue für Events Explizit machen mit Handler Wird im Main-Thread eine Handler Instanz erzeugt, dann hat man darüber Zugriff auf die Message-Queue ● – post um Runnable-Events einzustellen ● ● Wiederverwenden private Handler handler = new Handler(); public void onClickNebenHandler(View view) { new Thread() { @Override public void run() { compute(); handler.post(new Runnable() { @Override public void run() { update(); } }); } }.start(); } Es gibt ein postDelayed, das vorher ein bisschen wartet Auch nicht so viel besser. Effekt – post – Wird in die EventQueue des UI-Threads eingestellt und dann irgendwann abgearbeitet Prof. Dr. Peter Barth, Mobile Computing 30 update – mit Handler, post, updater ● Updater – – – – ● Aktionen für UI-Thread in Runnables vorpaketieren Runnables direkt mit dem einen Handler immer wiederverwenden Runnable kann auch direkt mit .run() ausgeführt werden, wenn man sich sicher ist, dass man im UI-Thread ist [Wird mal schöner mit Java 8] Effekt – – private Runnable updater = new Runnable() { @Override public void run() { update(); } }; public void onClickNebenHandler(View view) { new Thread() { @Override public void run() { compute(); handler.post(updater); } }.start(); } Etwas angenehmer identisch lesbarer Prof. Dr. Peter Barth, Mobile Computing 31 AsyncTask ● AsyncTask – Android-Klasse – – ● ● ● ● Background-Thread onPreExecute Handler und Threading (im Main-Thread anlegen) Callback-Methoden doInBackground onPreExecute: Vor Task, Main-Thread doInBackground: Der Task, neuer BG-Thread onProgressUpdate rufe doProgressUpdate: Teile Fortschritt mit onProgressUpdate – ● UI-Thread onProgressUpdate: Update-Info, Main-Thread onPostExecute: Nach Task, Main-Thread Drei Typ-Paramter: <T1, T2, T3> – – – T1: Was geht in Background rein (beliebig viele) T2: Was geht in Progress rein T3: Was geht aus Background raus Prof. Dr. Peter Barth, Mobile Computing doProgressUpdate doProgressUpdate onProgressUpdate doProgressUpdate onPostExecute 32 update – mit AsyncTask ● n Mal update – – ● n, default 3 Im Button 7 Fortschritt anzeigen – Bei jedem update onPostExecute done 7 times Prof. Dr. Peter Barth, Mobile Computing public class AsycnUpdater extends AsyncTask<Integer, Short, String> { private int howOften = 3; @Override protected String doInBackground(Integer... params) { if (params.length > 0) { howOften = params[0]; } for (int i=0; i < howOften; i+=1) { compute(); publishProgress(Short.valueOf((short) i), Short.valueOf((short) howOften)); } return "done " + howOften + " times"; } @Override protected void onProgressUpdate(Short... values) { update(String.format("%d/%d", values[0], values[1])); } @Override protected void onPostExecute(String s) { Log.v(TAG, "onPostExecute " + s); update(); } } public void onClickNebenAsyncTask(View view) { new AsycnUpdater().execute(7); } 33 update – Looper ● Problem – – – ● Ein neuer Thread je Task Aufwendig, besser wiederverwenden von Threads, ThreadPools Häufig reicht ein Worker-Thread Lösungsansätze – java.util.concurrent.*: ExecutorService ● – Looper mit interner Message-Queue ● – ● ● Ok, zu verwenden Auch ok, ein Worker-Thread Ein Worker bei Services: IntentService Selbst bei Bedarf Details nachlesen Beispiel: Je Click eine Anfrage, Hintereinander Abarbeiten Prof. Dr. Peter Barth, Mobile Computing private LooperThread looperThread; public void onCreate(… looperThread = new LooperThread(); looperThread.start(); } Neuer Background public void onDestroy(… Thread looperThread.shutdown(); } private class LooperThread extends Thread { public volatile Handler handler; public void run() { Looper.prepare(); handler = new Handler(); Interne Queue Looper.loop(); abarbeiten } public void shutdown() { handler.getLooper().quit(); } } public void onClickNebenLooper(View view) { looperThread.handler.post(new Runnable() { @Override zu Background public void run() { Thread compute(); handler.post(updater); } Wieder UI-Thread, }); der alte handler } 34 Ende ● Android – – – – ● Weitere Themen – Vorträge (?) ... – – – – – – – ● Lebenszyklus, Komponenten Activities, Intents, Broadcasts SQLite, ContentProvider Services, Nebenläufigkeit Netzwerkkommunikation Standardbezogene Dienste, Position Bluetooth Kommunikation Lage/Beschleunigungssensor, Sensoren Signieren Automatisiertes Testen … Viel Spaß und Erfolg im Projekt Prof. Dr. Peter Barth, Mobile Computing 35
© Copyright 2025 ExpyDoc