Programmieren für mobile Endgeräte€¦ · Dozenten: Patrick Förster, Michael Hasseler...
Transcript of Programmieren für mobile Endgeräte€¦ · Dozenten: Patrick Förster, Michael Hasseler...
Dozenten: Patrick Förster, Michael Hasseler
Programmieren für mobile Endgeräte SS 2013/2014
Dozenten: Patrick Förster, Michael Hasseler
Wiederholung (Datenverarbeitung)
• Bisher:
• Activity: Steuereinheit zwischen View und Model
• BroadcastReceiver: „Zuhörer“, der auf bestimmte Intents wartet
• Beide verarbeiten Daten
• Nutzereingaben
• Intent-Argumente
• Beide blockieren dabei die Applikation
• Daher strenges Zeitlimit für die „akzeptierte“ Verarbeitungszeit
• Activity: Maximal fünf Sekunden
• BroadcastReceiver: Maximal zehn Sekunden
Programmieren für mobile Endgeräte 2
Dozenten: Patrick Förster, Michael Hasseler
Services
• Dritte Möglichkeit zur Datenverarbietung
• Werden von Activies, Receivern oder anderen Services gestartet
• General ohne grafisches Interface
• Keine Zeiteinschränkung
• Höher priorisiert als inaktive oder gestoppte Activities
• „Foreground Services“: Services mit hoher Priorität, die nur in Extremfällen vom System gestoppt werden dürfen
• Achtung: Laufen per se im Haupt-Thread, d.h. auch Services blockieren die Applikation
Programmieren für mobile Endgeräte 3
Dozenten: Patrick Förster, Michael Hasseler
Services (Lebenszyklus)
• Es gibt Varianten eines Services mit unterschiedlichen Lebenszyklen:
• „Started“:
• Der Service lebt solange bis er gestoppt wird/sich selbst stoppt
• „Bound“:
• Andere Komponenten (bspw. Activities) können sich an einen Services „Binden“
• Der Service lebt solange wie eine andere Komponente eine Verbindung aufrecht erhält
Programmieren für mobile Endgeräte 4
create start stop destroy
create bind unbind destroy
Dozenten: Patrick Förster, Michael Hasseler
Services („Started“)
• Ein Service wird durch erben von android.app.Service definiert
• Die Methode onBind ist abstract und muss implementiert werden
• Callback-Methoden zu allen Lebenszyklus-Stati (bis auf „stop“)
• Services im Manifest registrieren:
• Service starten/stoppen (explizit/implizit)
Programmieren für mobile Endgeräte 5
Intent intent = new Intent(this, MyService.class); startService(intent); stopService(intent)
public class MyService extends Service { public static final String ACTION_SERVICE = "de.wuu.ziv.aufgabenapp.ACTION_SERVICE"; @Override public IBinder onBind(Intent intent) { return null; } }
<service android:name=".MyService" android:enabled="true"> <intent-filter> <action android:name="de.wuu.ziv.aufgabenapp.ACTION_SERVICE" /> </intent-filter> </service>
Intent intent = new Intent(MyService.ACTION_SERVICE); startService(intent); stopService(intent)
Dozenten: Patrick Förster, Michael Hasseler
Services („Bound“)
• Wenn bspw. eine Activity direkt mit einem Service kommunizieren soll, benötigt es einen „bounded“ Service:
• Implementierung von onBind
• IBinder werden zur Umsetzung von Remote-Aufrufen benötigt
• Im Beispiel ist der Service „lokal“ zur aufrufenden Activity
• Mit android.os.Binder existiert bereits eine vollständige
Implementierung der IBinder-Schnittstelle für diesen Fall
Programmieren für mobile Endgeräte 6
public class MyService extends Service { public class Binder extends android.os.Binder { public MyService getService() { return MyService.this; } } private final Binder binder = new Binder(); public IBinder onBind(Intent intent) { return binder; } }
Dozenten: Patrick Förster, Michael Hasseler
Services („Bound“ II)
• Auf aufrufender Seite benötigt es eine ServiceConnection:
• Callback für das Binding
• Zugriff auf den Service
• BIND_AUTO_CREATE: Service automatisch erstellen, solange das
Binding besteht
Programmieren für mobile Endgeräte 7
public class MainActivity extends Activity { … private MyService service; private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { service = ((MyService.Binder)service).getService(); } @Override public void onServiceDisconnected(ComponentName name) { service = null; } }; … }
Intent intent = new Intent(this, MyService.class); bindService(intent, connection, Context.BIND_AUTO_CREATE); unbindService(intent)
Dozenten: Patrick Förster, Michael Hasseler
Services (Restart)
• Falls ein Service vom System gestoppt wurde, wird er per default erneut gestartet, sobald genügen Ressourcen zur Verfügung stehen
• Bei jedem Start wird der Callback onStartCommand aufgerufen
• Sein Rückgabewert definiert das „Restart“-Verhalten
• START_STICKY: Service wird solange neugestartet, bis er per stopService explizit angehalten wird
• START_NOT_STICKY: Service wird nur neugestartet, wenn während seiner Auszeit ein startService erfolgte
• START_REDELIVER_INTENT: Service wird neugestartet, falls ein startService erfolgte oder der Service abgebrochen wurde, bevor er sich per stopSelf selbst stoppen konnte; in diesem Fall wird der initiale Intent erneut mitgeliefert (ansonsten null)
Programmieren für mobile Endgeräte 8
public int onStartCommand(Intent intent, int flags, int startId) { ... // arbeit starten return Service.START_NOT_STICKY; }
Dozenten: Patrick Förster, Michael Hasseler
Services (Beispiel)
• Service, der eine Internetverbindung herstellt und eine URL einliest
Programmieren für mobile Endgeräte 9
public int onStartCommand(Intent intent, int flags, int startId) { try { read(new URL(intent.getStringExtra("URL"))); } catch (java.lang.Exception e) {… } return Service.START_NOT_STICKY; } private String read(URL url) { try { URLConnection connection = url.openConnection(); int responseCode = ((HttpURLConnection)connection).getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { String inputLine; StringBuilder sb = new StringBuilder(""); BufferedReader inBuff = new BufferedReader(new InputStreamReader(connection.getInputStream())); while ((inputLine = inBuff.readLine()) != null) { sb.append(inputLine).append("\n"); } inBuff.close(); return sb.toString(); } } catch (java.lang.Exception e) {… } return null; }
Dozenten: Patrick Förster, Michael Hasseler
Services (Beispiel II)
• Idee gut, aber:
• „Network on main thread exception“
• Der Service könnte die Applikation zu lange blockieren
• Lösung: Das Einlesen muss ausgelagert werden
• Klasse android.os.Async übernimmt das Thread-Management
Programmieren für mobile Endgeräte 10
private class ScanTask extends AsyncTask<URL, String, List<String>> { @Override protected List<String> doInBackground(URL... params) { List<String> contents = new ArrayList<String>(); String content; for (URL url : params) { content = read(url); if (content != null) { contents.add(content); publishProgress(content); } } return contents; } private String read(URL url) {…} }
public int onStartCommand(Intent intent, int flags, int startId) { try { task = new ScanTask(); task.execute(new URL(intent.getStringExtra("URL")); } catch (java.lang.Exception e) {… } return Service.START_NOT_STICKY; }
Dozenten: Patrick Förster, Michael Hasseler
Services (Threads + UI)
• Achtung: Aus einem anderen als den Main-Thread heraus, kann die UI nicht geändert werden!
• Insbesondere also nicht aus AsyncTask.doInBackground
• Folgende AsyncTask-Methoden werden im Main-Thread ausgeführt
• doProgressUpdate: Wird aufgerufen nachdem per publishProgress ein Fortschritt signalisiert wurde
• onPostExecute: Wird aufgerufen sobald doInBackground
durchgelaufen ist
Programmieren für mobile Endgeräte 11
Dozenten: Patrick Förster, Michael Hasseler
Services (Timer)
• Der Service soll statt einmal periodisch laufen
• Statt AsyncTask verwendet man TimerTask
• Im Gegensatz zu AsyncTask gibt es keine mit dem Main-Thread
synchronisierten Methoden!
• Mögliche Lösungen:
• Eigenständiges Thread-Handling implementieren
• Aus der run-Methode einen AsyncTask erstellen und aufrufen
• Handler benutzten um Code in anderen Thread auszuführen
Programmieren für mobile Endgeräte 12
private class RefreshTask extends TimerTask { private List<URL> url; public RefreshTask(URL urls) { this.url = url; } @Override public void run() { read(url); } }
public int onStartCommand(Intent intent, int flags, int startId) { try { URL url = new URL(intent.getStringExtra("URL„); Timer timer = new Timer(); timer.scheduleAtFixedRate(new RefreshTask(urls), 0, refreshRate); } catch (java.lang.Exception e) {… } return Service.START_NOT_STICKY; }
Dozenten: Patrick Förster, Michael Hasseler
Services (Handler)
• Handler bieten die Möglichkeit java.lang.Runnable Objekte zur
Ausführung an einen bestimmten Thread zu überreichen
• Der Thread ist dabei immer der, in das Handler-Objekt erzeugt wurde
• Der RefreshTask wird aus dem Main-Thread heraus erzeugt
Per post wird veranlasst, dass UpdateContentRunnable im
Main-Thread ausgeführt werden soll
Programmieren für mobile Endgeräte 13
private class RefreshTask extends TimerTask { private List<URL> url; private Handler handler = new Handler(); private View view; Public RefreshTask(URL url, View view) { this.url = url; this.view = view; } @Override public void run() { String content = read(url); handler.post(new UpdateContentRunnable(view, content)); } }
private class UpdateContentRunnable implements Runnable { private String message; private View view; UpdateContentRunnable(View view, String message) { this.message = message; this.view = view; } public void run() { TextView messageView = (TextView) view.findViewById(R.id.text_scan_message); messageView.setText(message); } }
Dozenten: Patrick Förster, Michael Hasseler
Services (Alarm)
• Timer laufen im Scope der Applikation
• Wird die Applikation beendet, so auch der Timer
• Soll ein Service eine Applikation überleben, sind Timer ungeeignet
• Ein Alarm überlebt eine Applikation
• Alarm wird über den AlarmManager ausgelöst
• Es wird ein Broadcast mit dem angegebenen Intent gesendet
• Über den PendingIntent kann der Alarm wieder abgebrochen werden
Programmieren für mobile Endgeräte 14
public class MyActivity extends Activity { private AlarmManager alarmManager; private PendingIntent pending; @Override public void onCreate() { super.onCreate(); alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); pending= PendingIntent.getBroadcast(this, 0, new Intent(…), 0); .. }
Dozenten: Patrick Förster, Michael Hasseler
Services (Alarm)
• Auslösen des Alarms:
• ELAPSED_REALTIME: Alarm starten nach der angegebenen Zeit, seit
Starten des Gerätes
• setInexactRepeating wird genutzt, wenn es nicht wichtig, das der
Alarm nach eine exakten Zeit ausgelöst wird
Programmieren für mobile Endgeräte 15
public class MyActivity extends Acitivty { … @Override public void onCreate() { … // einfach alarmManager.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime(), scanIntent); alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime(), 5000, pending); // oder: alarmManager.setInexactRepeating( AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime(), AlarmManager.INTERVAL_FIFTEEN_MINUTES, pending ); }
Dozenten: Patrick Förster, Michael Hasseler
Services (Alarm)
• Der Alarm löst einen Broadcast aus
• Eigenen BroadcastReceiver anmelden
• Der Receiver wiederum startet den Service:
Programmieren für mobile Endgeräte 16
public class MyReceiver extends BroadcastReceiver { public static final String ACTION_SCAN = "de.wwu.ziv.aufgabenapp.ACTION_SCAN"; @Override public void onReceive(Context context, Intent intent) { Intent scanIntent = new Intent(context, AufgabenScanner.class); context.startService(scanIntent); } }
public class MyActivity extends Acitivty { @Override protected void onResume() { super.onResume(); receiver.setListener(this); registerReceiver(receiver, new IntentFilter(MyReceiver .ACTION_SCAN)); } }