Service ● Ziel – – ● – – – – ● ● 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 – – – – ● 1 bindService ● Anlass zum Erzeugen – – ● Binden mit bindService Starten mit startService – – 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 ● onCreate Stoppen eines Service Selbst mit stopSelf() onStartCommand – Clients an Services gebunden Service gestartet und läuft onUnbind onDestroy Service beendet onDestroy Service beendet Nachrichten schicken und erwarten (asynchron) Binden und Remoting (synchron) aber trotzdem Serialisierung (RPC) 2 bindService Service – Lebenszyklus ● onBind Anwendung Prof. Dr. Peter Barth, Mobile Computing – Effekt – ● onCreate startService Service aktiv ● Von außen mit stopService und passendem Intent Nur falls keine Bindungen existieren ● – 3 onCreate Machen, wenn der Dienst nicht mehr gebraucht wird Effekt – Service stoppt (selbst oder fremd) Remote Service Service in anderem Prozess, damit potenziell in anderer Anwendung Instanziieren remote, für Kommunikation Serialisierung notwendig ● Prof. Dr. Peter Barth, Mobile Computing Service – Lebenszyklus Service im gleichen Prozess, damit gleiche Anwendung Instanziieren lokal Auf Instanz kann nach Binden direkt zugegriffen werden Komp./ Activity Remote Service Remote Service – Nebenläufigkeit und Service unabhängig voneinander Aufruf im (lokalen) Service immer noch im Main-Thread Falls Hintergrundoperation gewünscht, dann selbst Threading verwalten Interagiert mit anderen Komponenten Andere Komponenten im selben Prozess (Lokal) oder in anderem Prozess (Remote) Local Service – ● Local Service Service – Lösungsansatz: Service – Anwendung Service – Local und Remote 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 – – – ● ● Als Lokaler Service [Als Remote Service] Als Remote Messaging Service – Localer Service – – ● Service Funktionalität – ● LocalService – Binden, Realisieren Binden – Zwei Zahlen als Eingabe Ergebnis ist deren Addition – Service Nutzen, Optionen – – Über Binden Über Starten mit Parameter ● – Und potenziell Empfangen eines Ergebnisses über Messaging Lebt in derselben Java-VM Direkter Zugriff möglich 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 5 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 – – AndroidManifest.xml 6 LocalService – Verwenden, Aufruf 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 Antipattern: in onClickBindLocal bei true direkt Service verwenden – geht schief! ● Service verwenden – – – – ● 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 Prof. Dr. Peter Barth, Mobile Computing Service – Lebenszyklus – – 7 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 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 – – – ● ● public void onClickUnbindLocal() { if (localServiceBound) { localServiceBound = false; localService = null; unbindService(localServiceConnection); } } – – ● onServiceDisconnected wird nicht aufgerufen (nur bei ungeplanmäßigem...) – ● Bindung wird irgendwann gelöst Service kann weiter existieren, muss aber nicht ● – ● 9 LocalService – Ansage der Ergebnisse – ● ● Ansage der Ergebnisse Kein Blockieren des UIs Mehrere Ansagen hintereinander möglich – – ● „Siebzehn plus Zweiundvierzig ist gleich Neunundfünzig“ – – 11 Initialisierung der Sprach-Engine in onCreate Shutdown der Sprach-Engine in onDestroy Teure Operationen onStartCommand – Prof. Dr. Peter Barth, Mobile Computing 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 Einfach und leicht umzusetzen Lebenszyklus – 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() { … } } ServiceApp.onClickStartParams Prof. Dr. Peter Barth, Mobile Computing Voraussetzung – Audio/Video player Fernbedienung, … Intent intent = new Intent(this, LocalService.class); intent.setAction("compute"); intent.putExtra("number1", getNumber1()); intent.putExtra("number2", getNumber2()); startService(intent); 10 LocalService – Realisieren, Aussprache Gewünschtes Verhalten – Falls keine Rückgabe notwendig ● Falls Service nur gebunden wurde, dann wird Service zerstört, sobald letzte Bindung gelöst wurde Prof. Dr. Peter Barth, Mobile Computing – Nicht möglich Verwendung trotzdem sinnvoll – ● Anfrage Service zu starten Übergabe Parameter per Intent möglich Funktioniert auch remote Rückgabe Ergebnis – Effekt – Start/StartParams – Achtung: – ● Merken, dass Bindung gelöst wurde/wird Referenz lösen, wird nicht mehr genutzt unbindService: Anfragen beim System, wieder über localServiceConnection LocalService – Start, StartParams 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 LocalService – Beenden Aussprache public void onClickStartParamsLocal(...) { Intent intent = new Intent(this, LocalService.class); intent.setAction("say"); intent.putExtra("number1", getNumber1()); intent.putExtra("number2", getNumber2()); startService(intent); } – – – Aussprache auch als RemoteService sinnvoll Entweder stopService oder laufen lassen – 13 Anwendung – Remote Service Erzwingen eines Remote Service – – – – 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 Naiver Ansatz – Komp./ Activity – 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 – „Siebzehn plus Zweiundvierzig ist gleich Neunundfünzig“ ● Klar, getService kann nicht ein Objekt aus einem anderen Prozess zurückgeben 14 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) – – 15 ServiceApp.java Prof. Dr. Peter Barth, Mobile Computing ● Service Instanz in anderem Prozess Eventuell in anderer Anwendung 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 RemoteService – Naiv Remote Service – Bei Ende Aufgabe: Service beendet sich selbst TTS-Engine starten teuer, Ende nur falls mind. 5 Sekunden keine Nachricht gesprochen Umsetzung – ServiceApp.java Remote Service ● – ● Nach Übergabe auszusprechender Text soll Service weiterlaufen Bisher läuft Service immer weiter... Ziel – public void onClickStopLocal(...) { Intent intent = new Intent(this, LocalService.class); stopService(intent); } Prof. Dr. Peter Barth, Mobile Computing ● – ● public void onClickStartLocal(...) { Intent intent = new Intent(this, LocalService.class); intent.setAction("init"); startService(intent); } Service läuft weiter Kann/muss? manuell angehalten werden Service läuft weiter – ProgrammEnde? – ● ● 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 – – IPC ● – – Geht schon, wie mit StartParams im Intent Alternativ: Nachtrichten erwarten zahl1 zahl2 replyTo Daten zurücksenden ● ● ● Message ● Remote Service ● ● – Message – res Prof. Dr. Peter Barth, Mobile Computing 17 RemoteService – Realisieren ● Konstanten – ● Nachrichten-Typen, im what der Message Messenger – – Fertige Android-Komponente Handler übergeben, zuständig für Abarbeiten einer Nachricht ● – ● ● ● in handleMessage (gleich) WeakReference Falls keine static class, dann leak der Service-Reference Service Reference wird aber gebraucht, also... (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 ● handleMessage – 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 19 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 – onBind: einfach, messenger kann notwendige IBinder-Instanz erzeugen Prof. Dr. Peter Barth, Mobile Computing Zwei ints: arg1, arg2 Ein Object obj Ein int: what (ID) Ein Messenger: replyTo 18 RemoteService – Realisieren handleMessage public static final int MSG_ADD = 1; public static final int MSG_ADD_RESULT = 2; http://stackoverflow.com/questions/11407943/this-handler-class-should-be-static-or-leaks-might-occur-incominghandler int: arg1, arg2 Object: obj int: what Messenger: replyTo Vordefinierte Attribute ● Komp./ Activity Auch für asynchron genutzten LocalService zu verwenden 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 – Rückkanal beim Übermitteln erhalten Auch Rückkanal Antwort-Nachricht schicken Message Nachrichtenbasierte Kommunikation – Remote Service Daten übermitteln ● – ● Komp./ Activity Lösung: Asynchrones Messaging – ● Nachrichten – Message @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 RemoteService – Nutzen, Anfrage-Nachricht Schicken 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, 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 – GUI aktualisieren – ● ● Prüfen Im UI-Thread – – 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 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"); } Prof. Dr. Peter Barth, Mobile Computing 22 Alternative: RemoteService mit IPC 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 ● 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 – – – ● ● 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) … – – – – – – ● 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 – – Beispiel – Aktualisieren TextFeld ● – ● – ● ● https://developer.android.com/guide/components/processes-and-threads.html IndeterminateOnly, drehender Kreis Dreht sich nicht, wenn UI-Thread belegt – ● Flexible Dauer der Berechnung Dauer mit Slider einstellen – – Mit aktueller Uhrzeit, Millisekundenauflösung – Buttons für unterschiedliche Strategien Main, ist im UI-Thread Prof. Dr. Peter Barth, Mobile Computing ● public void onClickMain(View view) { compute(); böse: Keine update(); } Berechnung im 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(); 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 – 27 26 Neben – Basis für alle Demonstrationen – Main – Böse – update: Aktualisieren Textfeld – 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...) Prof. Dr. Peter Barth, Mobile Computing ● compute: Berechnung – 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. UI-Thread ProgressBar – Alles was potenziell lange dauert Alles mit Netzwerk Alles mit Remoting (Remote Service) Alles mit File I/O (Fast) alles mit I/O Rahmen – 25 Benutzungsoberfläche reagiert nie träge Wann separater Thread – Remote oder Local Service mit Messaging – Ziel: Schwupdizität – ● RemoteService – ● Wiederholung – Warum Threading? 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 update – mit Handler, post 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(); } ● Handler – – – ● – ● Prof. Dr. Peter Barth, Mobile Computing 29 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 – – Es gibt ein postDelayed, das vorher ein bisschen wartet Wird in die EventQueue des UI-Threads eingestellt und dann irgendwann abgearbeitet Prof. Dr. Peter Barth, Mobile Computing ● private Runnable updater = new Runnable() { @Override public void run() { update(); } }; AsyncTask – Android-Klasse – – ● public void onClickNebenHandler(View view) { new Thread() { @Override public void run() { compute(); handler.post(updater); } }.start(); } ● ● ● UI-Thread Background-Thread onPreExecute Handler und Threading (im Main-Thread anlegen) Callback-Methoden doInBackground onProgressUpdate rufe doProgressUpdate: Teile Fortschritt mit onProgressUpdate onProgressUpdate: Update-Info, Main-Thread onPostExecute: Nach Task, Main-Thread Drei Typ-Paramter: <T1, T2, T3> – – – 31 30 onPreExecute: Vor Task, Main-Thread doInBackground: Der Task, neuer BG-Thread – ● Etwas angenehmer Auch nicht so viel besser. Effekt – post AsyncTask identisch lesbarer Prof. Dr. Peter Barth, Mobile Computing Wiederverwenden post um Runnable-Events einzustellen ● Syntax nicht so toll für häufiges Verwenden – ● 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 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(); } 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 update – Looper 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); } Prof. Dr. Peter Barth, Mobile Computing 33 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 ● 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
© Copyright 2025 ExpyDoc