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