Upload
open-knowledge-gmbh
View
370
Download
4
Embed Size (px)
DESCRIPTION
Android Architecture Design Speaker: Lars Röwekamp Wie heißt es doch so schön: "Es könnte alles so einfach sein - ist es aber nicht!". Dieses Gefühl kennt wahrscheinlich jeder Android-Entwickler, der schon einmal versucht hat, eine etwas komplexere Android-App zu bauen, die sowohl auf Smartphones als auch auf Tablets gleichermaßen gut funktioniert und dabei noch mit einer ansprechenden Ergonomie aufwartet. Ganz zu schweigen von den Herausforderungen, unterschiedlichste OS-Versionen zu unterstützen oder aber auch im Tunnel bzw. im Flugzeug noch sinnvolle Funktionen zu bieten. Die Session zeigt, dass all dies mit einer gut durchdachten Architektur durchaus möglich ist.
Citation preview
Pho
to c
redi
t: S
antiM
B .
/ Fot
er.c
om /
CC
BY-
NC
-ND
Real-Life Architecture
@mobileLarson @_openKnowledge
Lars Röwekamp | CIO New Technologies
Real-Life Architecture Android 4+
MTC2014
Eigentlich ist ja alles ganz einfach ...
Real-Life Architecture Android 4+
MTC2014
Eigentlich ist ja alles ganz einfach ...
Real-Life Architecture Android 4+
MTC2014
... auch wenn es kompliziert(er) wird.
Real-Life Architecture Android 4+
MTC2014
... auch wenn es kompliziert(er) wird.
Real-Life ArchitectureMTC2014
Splash Overview Share
Preferences
Map
Real-Life Architecture
Android 4+
Real-Life Architecture Android 4+
MTC2014
Real-Life Architecture Android 4+
MTC2014
Wo liegt das ...
Problem?
Es war einmal eine App ...Real-Life Architecture
MTC2014
Splash Overview Share
Preferences
Map
Es war einmal eine App ...Real-Life Architecture
MTC2014
Es war einmal eine App ...Real-Life Architecture
MTC2014
Anforderungen easy Version !
‣ klassische Android Anwendung ‣ Kunden CI und White Label ‣ Anbindung von (Web) Services ‣ Vorlieben/Einstellungen merken ‣ Time-to-Market
Es war einmal eine App ...Real-Life Architecture
MTC2014
Anforderungen eXtended Edition !
‣ Smartphone & Tablet Support ‣ Android 2.3 & Android 4.x Support ‣Multi-Language Support ‣Multi-User Support ‣ Localization Support
Es war einmal eine App ...Real-Life Architecture
MTC2014
Anforderungen Directors Cut !
‣ Daten immer aktuell ‣ Daten auch offline ‣ Daten auch für Dritte ‣ batterieschonend ‣ ... und natürlich „Top Security“ ‣ ... und natürlich „Top Usabillity“
... und die hatte eine ArchitekturReal-Life Architecture
MTC2014
... und die hatte eine ArchitekturReal-Life Architecture
MTC2014
Es war einmal eine App ...Real-Life Architecture
MTC2014
Let‘s go !
‣ klein starten ‣ schrittweise erweitern ‣ stets lauffähig und sinnvoll !
‣ Refactoring ist ein Zeichen von Stärke
MTC2014 Real-Life Architecture
Schritt 1: POI senden
Schritt 1: POI sendenReal-Life Architecture
MTC2014
Schritt 1: POI sendenReal-Life Architecture
MTC2014
automatisch vs. manuell
Position / Notiz
MTC2014
POI senden Best Practices !
‣ POI via GPS erfragen ‣ POI via Adresseingabe erfragen !
‣ UI und Logik trennen ‣ Kommunikation in eigene Lib auslagern ‣ „Application not Responding“ vermeiden
Real-Life ArchitectureSchritt 1: POI senden
MTC2014
Code Diving ...
Real-Life ArchitectureSchritt 1: POI senden
MTC2014
POI senden Pitfalls !
‣ Network on Main Thread ‣ Strict Mode
Real-Life ArchitectureSchritt 1: POI senden
MTC2014
POI senden Pitfalls !
‣ Network on Main Thread ‣ Strict Mode
Real-Life ArchitectureSchritt 1: POI senden
MTC2014
Netzwerkzugriff seit 4.x nur via ...
Async
Real-Life ArchitectureSchritt 1: POI senden
MTC2014
POI senden Async !
‣ Handler & Threads ‣ AsyncTask & „nix“ ‣ IntentService & BroadcastReceiver
Real-Life ArchitectureSchritt 1: POI senden
MTC2014class PostToFriendFinder extends AsyncTask<Void, Integer, String> { ! // Invoked on the background thread immediately after onPreExecute() // finishes executing. Performs background computation that can take // a long time. The parameters of the async task are passed to this // step. // @Override protected String doInBackground(Void... params) { try { getFriendFinder().sharePointOfInterstVisit(poi, note); return "Sending point of interest visitation was successfull."; } catch (FriendFinderException ex) { return "Sending point of interest visitation failed."; } } @Override protected void onProgressUpdate(Integer... values) { ... } ! @Override protected void onPostExecute(String result) { ... } !}
Real-Life ArchitectureSchritt 1: POI senden
AsyncTasks
MTC2014class PostToFriendFinder extends AsyncTask<Void, Integer, String> { ! // Invoked on the background thread immediately after onPreExecute() // finishes executing. Performs background computation that can take // a long time. The parameters of the async task are passed to this // step. // @Override protected String doInBackground(Void... params) { try { getFriendFinder().sharePointOfInterstVisit(poi, note); return "Sending point of interest visitation was successfull."; } catch (FriendFinderException ex) { return "Sending point of interest visitation failed."; } } @Override protected void onProgressUpdate(Integer... values) { ... } ! @Override protected void onPostExecute(String result) { ... } !}
Async
Real-Life ArchitectureSchritt 1: POI senden
POI & Note?
AsyncTasks
MTC2014class PostToFriendFinder extends AsyncTask<Void, Integer, String> { ! private PointOfInterest poi; private String note; public PostToFriendFinder(PointOfInterest poi, String note) { ... } // @Override protected String doInBackground(Void... params) { try { getFriendFinder().sharePointOfInterstVisit(poi, note); return "Sending point of interest visitation was successfull."; } catch (FriendFinderException ex) { return "Sending point of interest visitation failed."; } } @Override protected void onProgressUpdate(Integer... values) { ... } ! @Override protected void onPostExecute(String result) { ... } !}
Real-Life ArchitectureSchritt 1: POI senden
AsyncTasks
MTC2014// onClick handler to collect input data, create a new Point of Interest // and share it async via related service public void onClick(View view) { float latitude = ...; float longitude = ...; String note = ...; ! // create new point of interest PointOfInterest poi = new PointOfInterest(longitude, latitude, 0.00F); ! // create async tasks to communicate with the cloud service new PostToFriendFinder(poi, note).execute(); !}
Real-Life ArchitectureSchritt 1: POI senden
AsyncTasks
MTC2014// onClick handler to collect input data, create a new Point of Interest // and share it async via related service public void onClick(View view) { float latitude = ...; float longitude = ...; String note = ...; ! // create new point of interest PointOfInterest poi = new PointOfInterest(longitude, latitude, 0.00F); ! // create async tasks to communicate with the cloud service new PostToFriendFinder(poi, note).execute(); !}
Real-Life ArchitectureSchritt 1: POI senden
AsyncTasks
MTC2014public class SendService extends IntentService { ... @Override protected void onHandleIntent(Intent intent) { ! // get point of interest and note form intent extras PointOfInterest poi = (PointOfInterest)intent.getSerializableExtra(POINT_OF_INTEREST_EXTRA); String note = intent.getStringExtra(NOTE_EXTRA); // get current friend finder instance (connection to friend finder WS) FriendFinder friendFinder = new FriendFinder(getCurrentContact()); String result; try { // share own position to WS friendFinder.sharePointOfInterstVisit(poi, note); result = "Sending point of interest visitation was successfull."; } catch (FriendFinderException e) { result = "Sending point of interest visitation failed."; } // inform registered receivers via notification LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(POI_SHARED)); } }
Real-Life ArchitectureSchritt 1: POI senden
IntentService
MTC2014// onClick handler to collect input data, create an Intent // and share it async via related intent service public void onClick(View view) { float latitude = ...; float longitude = ...; String note = ...; ! // create new point of interest PointOfInterest poi = new PointOfInterest(longitude, latitude, 0.00F); ! Intent serviceIntent = new Intent(this, SendService.class); serviceIntent.putExtra(SendService.POINT_OF_INTEREST_EXTRA, poi); serviceIntent.putExtra(SendService.NOTE_EXTRA, note); startService(serviceIntent); }
Real-Life ArchitectureSchritt 1: POI senden
IntentService
MTC2014// onClick handler to collect input data, create an Intent // and share it async via related intent service public void onClick(View view) { float latitude = ...; float longitude = ...; String note = ...; ! // create new point of interest PointOfInterest poi = new PointOfInterest(longitude, latitude, 0.00F); ! Intent serviceIntent = new Intent(this, SendService.class); serviceIntent.putExtra(SendService.POINT_OF_INTEREST_EXTRA, poi); serviceIntent.putExtra(SendService.NOTE_EXTRA, note); startService(serviceIntent); }
Real-Life ArchitectureSchritt 1: POI senden
IntentService
MTC2014 Real-Life Architecture
Schritt 2: Einstellungen merken
Real-Life ArchitectureMTC2014
Schritt 2: Einstellungen merken
Schritt 2: Einstellungen merkenMTC2014
Einstellungen merken Best Practices !
‣ Einstellungen zentral verwalten ‣ Einstellungen gruppieren ‣ UI zur Bearbeitung bereit stellen ‣ Settings Design Guide beachten
Real-Life Architecture
Schritt 2: Einstellungen merkenMTC2014
Einstellungen merken Best Practices !
‣ Einstellungen zentral verwalten ‣ Einstellungen gruppieren ‣ UI zur Bearbeitung bereit stellen ‣ Settings Design Guide beachten
Real-Life Architecture
MTC2014
Einstellungen merken Pitfalls !
‣ Hierarchie von Einstellungen ‣ Einstellungen können sich ändern ‣ Zugriff auf Einstellungen an mehreren Stellen !
‣ Android 2.x vs. Android 4.x
Real-Life ArchitectureSchritt 2: Einstellungen merken
MTC2014
Einstellungen via ...
Preferences
Real-Life ArchitectureSchritt 2: Einstellungen merken
MTC2014<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <!-- opens a subscreen of settings --> <PreferenceScreen android:key="button_voicemail_category_key" android:title="@string/voicemail" android:persistent="false"> <ListPreference android:key="button_voicemail_provider_key" android:title="@string/voicemail_provider" ... /> <!-- opens another nested subscreen --> <PreferenceScreen android:key="button_voicemail_setting_key" android:title="@string/voicemail_settings" android:persistent="false"> ... </PreferenceScreen> <RingtonePreference android:key="button_voicemail_ringtone_key" android:title="@string/voicemail_ringtone_title" android:ringtoneType="notification" ... /> ... </PreferenceScreen> ... </PreferenceScreen>
Real-Life ArchitectureSchritt 2: Einstellungen merken
MTC2014<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <!-- opens a subscreen of settings --> <PreferenceScreen android:key="button_voicemail_category_key" android:title="@string/voicemail" android:persistent="false"> <ListPreference android:key="button_voicemail_provider_key" android:title="@string/voicemail_provider" ... /> <!-- opens another nested subscreen --> <PreferenceScreen android:key="button_voicemail_setting_key" android:title="@string/voicemail_settings" android:persistent="false"> ... </PreferenceScreen> <RingtonePreference android:key="button_voicemail_ringtone_key" android:title="@string/voicemail_ringtone_title" android:ringtoneType="notification" ... /> ... </PreferenceScreen> ... </PreferenceScreen>
Real-Life ArchitectureSchritt 2: Einstellungen merken
MTC2014public static class SettingsFragment extends PreferenceFragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ! // Load the preferences from an XML resource addPreferencesFromResource(R.xml.preferences); } ... } !!public class SettingsActivity extends Activity { ! @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ! // Display the fragment as the main content. getFragmentManager().beginTransaction() .replace(android.R.id.content, new SettingsFragment()) .commit(); } }
Real-Life ArchitectureSchritt 2: Einstellungen merken
MTC2014public class FriendFinderApplication extends Application implements OnSharedPreferenceChangeListener { ! SharedPreferences preferences; ... ! @Override public void onCreate() { super.onCreate(); this.preferences = PreferenceManager.getDefaultSharedPreferences(this); ! // recommanded in onResume (register) / onPause (unregister) this.preferences.registerOnSharedPreferenceChangeListener(this); // use preferences to initialize app data ... } ... ! @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { // force reload of preferences and reinitialization of global data ... } }
Real-Life ArchitectureSchritt 2: Einstellungen merken
Ok, aber wofür brauche ich dann
noch die PreferenceActivity?
MTC2014 Real-Life Architecture
Schritt 2: Einstellungen merken
MTC2014 Real-Life Architecture
Schritt 2: Einstellungen merken
MTC2014public class AllPreferenceActivity extends PreferenceActivity { ! // --- load all headers @Override public void onBuildHeaders(List<Header> target) { ! super.onBuildHeaders(target); loadHeadersFromResource(R.xml.pref_header, target); } ! // --- checks if chosen fragment is valid (added in API 19) @Override protected boolean isValidFragment(String fragmentName) { return MapsPreferenceFragment.class.getName().equals(fragmentName) || ServerPreferenceFragment.class.getName().equals(fragmentName) || UserPreferenceFragment.class.getName().equals(fragmentName) || super.isValidFragment(fragmentName); } }
Real-Life ArchitectureSchritt 2: Einstellungen merken
Wie reagiere ich auf Änderungen?
MTC2014public class FriendFinderApplication extends Application implements OnSharedPreferenceChangeListener { ...! @Override public void onSharedPreferenceChanged(SharedPreferences sharedPrefs, String key) {! // user name must not be empty if (Preferences.PREF_USER_NICK_NAME.equals(key)) { // check if changed user name forces restart of updater service // to load new friend list ... }! // update UI with new preference settings ... }
Real-Life ArchitectureSchritt 2: Einstellungen merken
Und wie verhindere ich falsche Eingaben?
MTC2014!@Overridepublic void onSharedPreferenceChanged(SharedPreferences sharedPrefs, String key) {! // user name must not be empty if (Preferences.PREF_USER_NICK_NAME.equals(key)) {! if (sharedPrefs.getString(Preferences.PREF_USER_NICK_NAME,"").equals("")) {! Editor editor = sharedPrefs.edit(); editor.putString(Preferences.PREF_USER_NICK_NAME, DEFAULT_USER_NAME); editor.commit(); return; }! }! // check if changed preferences forces restart of updater service ... }
Real-Life ArchitectureSchritt 2: Einstellungen merken
MTC2014
Code Diving ...
Real-Life ArchitectureSchritt 2: Einstellungen merken
MTC2014 Real-Life Architecture
Schritt 3: POIs abgleichen
Real-Life ArchitectureMTC2014
Schritt 3: POIs abgleichen
Schritt 3: POIs abgleichenReal-Life Architecture
MTC2014
MTC2014
POIs abgleichen Best Practices !
‣ POIs regelmäßig abgleichen ‣ POIs so aktuell wie möglich halten ‣ POIs im Hintergrund laden
Real-Life ArchitectureSchritt 3: POIs abgleichen
MTC2014
POIs abgleichen Pitfalls !
‣ regelmäßig ‣ aktuell ‣ im Hintergrund
Real-Life ArchitectureSchritt 3: POIs abgleichen
MTC2014
Hintergrundaufgaben via ...
Service
Real-Life ArchitectureSchritt 3: POIs abgleichen
MTC2014 Real-Life Architecture
Schritt 3: POIs abgleichen
MTC2014public class UpdaterService extends Service { ! private Updater updater; ... public IBinder onBind(Intent intent) { ... } public void onCreate() { ... } public int onStartCommand(Intent intent, int flags, int startId) { .. } public void onDestroy() { ... } ! private class Updater extends Thread { ! public Updater() { ... } ! public void run() { UpdaterService updaterService = UpdaterService.this; while (updaterService.running) { try { ... // do some work Thread.sleep(DELAY); } catch (InterruptedException ex) { updaterService.running = false; } } ! } } }
Real-Life ArchitectureSchritt 3: POIs abgleichen
MTC2014public class UpdaterService extends Service { ! private Updater updater; ... public IBinder onBind(Intent intent) { ... } public void onCreate() { ... } public int onStartCommand(Intent intent, int flags, int startId) { .. } public void onDestroy() { ... } ! private class Updater extends Thread { ! public Updater() { ... } ! public void run() { UpdaterService updaterService = UpdaterService.this; while (updaterService.running) { try { ... // do some work Thread.sleep(DELAY); } catch (InterruptedException ex) { updaterService.running = false; } } ! } } }
Real-Life Architecture Starten & Stoppen?
Online vs. Offline?
Schritt 3: POIs abgleichen
MTC2014public class FriendFinderApplication extends Application { ... /** check is updater service is running with the help of the * <code>RunningServiceInfo</code> of the <code>ActivityManager</code> */ public boolean isUpdaterServiceRunning() { ! String serviceName; ActivityManager manager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE); for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { serviceName = service.service.getClassName(); ! // is the requested service running? if(UpdaterService.class.getName().equals(serviceName)) { return true; } } return false; } !}
Real-Life ArchitectureSchritt 3: POIs abgleichen
MTC2014
Code Diving ...
Real-Life ArchitectureSchritt 3: POIs abgleichen
MTC2014 Real-Life Architecture
Schritt 4: Offline-Modus
Real-Life ArchitectureMTC2014
Schritt 4: Offline-Modus
Schritt 4: Offline-ModusReal-Life Architecture
MTC2014
MTC2014
Offline Modus Best Practices !
‣ POIs via Online-Modus abgleichen ‣ POIs für Offline-Modus speichern ‣ POI UI aktuell halten !
‣ Datenzugriff kapseln ‣ Datenzugriff optimieren
Real-Life ArchitectureSchritt 4: Offline-Modus
MTC2014
Offline-Modus Pitfalls !
‣ Online vs. Offline ‣ Read vs. Write ‣ UI aktuell halten !
‣ Testen
Real-Life ArchitectureSchritt 4: Offline-Modus
MTC2014
Daten verfügbar machen via ...
SQL & Adapter
Real-Life ArchitectureSchritt 4: Offline-Modus
MTC2014 Real-Life Architecture
?
Schritt 4: Offline-Modus
MTC2014 Real-Life Architecture
Schritt 4: Offline-Modus
MTC2014 Real-Life Architecture
PoiVisitations
username, name,
...
Adapter FROM TO
username tx_username
... ...
!
<Row-Layout />
<ListView>
</ListView>
<Row-Layout />
<Row-Layout />
<LinearLayout>
</Linearlayout>
t_timestamp
tx_name
tx_note
res/layout/row.xmlres/layout/mylist.xml
src/MyAdapter.java src/MyDbHelper.java
Schritt 4: Offline-Modus
MTC2014 Real-Life Architecture
public class PositionOverviewActivity extends Activity { ! Cursor cursor; ListView listView; PositionOverviewAdapter adapter; FriendFinderData friendFinderData; ! static final String[] FROM = { ... }; static final int[] TO = { ... }; ! public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_position_overview); // lookup list view listView = (ListView) findViewById(R.id.list_position_overview); ! // lookup data friendFinderData = ((FriendFinderApplication) getApplication()) .getFriendFinderData(); } ... }
Schritt 4: Offline-Modus
MTC2014 Real-Life Architecture
public class PositionOverviewActivity extends Activity { ! Cursor cursor; ListView listView; PositionOverviewAdapter adapter; FriendFinderData friendFinderData; ! ... ! public void onResume() { super.onResume(); cursor = friendFinderData.getPoiVisitations(); // start managing the cursor startManagingCursor(cursor); // created special adapter adapter = new PositionOverviewAdapter(this, cursor); listView.setAdapter(adapter); } ... }
Schritt 4: Offline-Modus
MTC2014 Real-Life Architecture
public class PositionOverviewActivity extends Activity { ! Cursor cursor; ListView listView; PositionOverviewAdapter adapter; FriendFinderData friendFinderData; ! ... ! public void onResume() { super.onResume(); cursor = friendFinderData.getPoiVisitations(); // deprecated in Android 4.x startManagingCursor(cursor); // created special adapter adapter = new PositionOverviewAdapter(this, cursor); listView.setAdapter(adapter); } ... }
Schritt 4: Offline-Modus
MTC2014 Real-Life Architecture
Schritt 4: Offline-Modus
MTC2014 Real-Life Architecture
Schritt 4: Offline-Modus
MTC2014 Real-Life Architecture
Schritt 4: Offline-Modus
MTC2014
Das kleine Loader 1x1 !
‣ asynchrones Laden von Daten ‣ verfügbar in Activities und Fragments ‣ Content-Change-Monitoring der Datensource ‣ Reconnection zu vorheriger Position !
‣ CursorLoader (für ContentProvider) ‣ Loader oder AsyncTaskLoader
Real-Life ArchitectureSchritt 4: Offline-Modus
MTC2014// Activity implementing Loader Callbacks public class MyActivity extends Activity implements LoaderManager.LoaderCallback<Cursor> { ! @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... adapter = new SimpleCursorAdapter(...) setListAdapter(adapter) getLoaderManager().initLoader(0, null, this); } ... !}
Real-Life ArchitectureSchritt 4: Offline-Modus
MTC2014// Activity implementing Loader Callbacks public class MyActivity extends Activity implements LoaderManager.LoaderCallback<Cursor> { ! ... // loader was created and is ready to work public Loader<Cursor> onCreateLoader(int id, Bundle args){ // set content provider query URI etc. ... // access content provider return new CursorLoader(this, cpQueryUri, projection, where, whereArgs, sortOrder); } ... }
Real-Life ArchitectureSchritt 4: Offline-Modus
MTC2014// Activity implementing Loader Callbacks public class MyActivity extends Activity implements LoaderManager.LoaderCallback<Cursor> { ! ... // loader finished loading - data is available public void onLoadFinished(Loader<Cursor> l, Cursor c){ adapter.swapCursor(c); } ! // loader was reseted - its data is unavailable public void onLoaderReset(Loader<Cursor> l){ adapter.swapCursor(null); } }
Real-Life ArchitectureSchritt 4: Offline-Modus
MTC2014
Loader und ... !
‣ Content Provider für Lau ‣ SQLite via eigenem AsyncTaskLoader<Cursor>
Real-Life ArchitectureSchritt 4: Offline-Modus
MTC2014
Und wie kann ich die DB Daten sehen ... !
‣ DB liegt unter
/data/data/[mypackage]/databases/[myapp].db !
‣ DB Copy auf den Rechner ‣ SQLiteManager PlugIn für Eclipse
Real-Life ArchitectureSchritt 4: Offline-Modus
MTC2014
Code Diving ...
Real-Life ArchitectureSchritt 4: Offline-Modus
MTC2014 Real-Life Architecture
Schritt 5: Mobile Intelligenz
Real-Life ArchitectureMTC2014
Schritt 5: Mobile Intelligenz
Schritt 5: Mobile IntelligenzReal-Life Architecture
MTC2014
MTC2014
Mobile Intelligenz Best Practices !
‣ POIs nur senden/abfragen, wenn Internet ‣ POIs nur senden/abfragen, wenn Strom ‣ UI aktualisieren, wenn neue Daten ‣ ...
Real-Life ArchitectureSchritt 5: Mobile Intelligenz
MTC2014
Mobile Intelligenz Pitfalls !
‣ POIs im günstigsten Moment abfragen ‣ Umgebungsänderungen feststellen ‣ Zugriffe ausreichend absichern
Real-Life ArchitectureSchritt 5: Mobile Intelligenz
MTC2014
Mobile Intelligenz via ...
Broadcast Receiver
Real-Life ArchitectureSchritt 5: Mobile Intelligenz
MTC2014
Was sind Broadcast Receiver? !
‣ Publisher-Subscriber-Pattern ‣ Observer-Pattern !
‣ App / System sendet Nachricht ‣ Broadcast Receiver hört auf Nachricht
Real-Life ArchitectureSchritt 5: Mobile Intelligenz
MTC2014 Real-Life Architecture
Schritt 5: Mobile Intelligenz
MTC2014
Was helfen uns die Broadcast Receiver? !
‣ Service starten sobald Device gebootet ist !
‣ auf Internet-Verfügbarkeit reagieren ‣ auf Batteriestatus reagieren !
‣ auf neue POIs reagieren
Real-Life ArchitectureSchritt 5: Mobile Intelligenz
MTC2014 Real-Life Architecture
public class BootReceiver extends BroadcastReceiver { private static final String TAG = BootReceiver.class.getSimpleName(); ! // start update service after system start automatically @Override public void onReceive(Context context, Intent intent) { context.startService(new Intent(context, UpdaterService.class)); Log.d(TAG, "onReceived"); } }
‣ Service starten sobald Devices gebootet ist
Schritt 5: Mobile Intelligenz
MTC2014 Real-Life Architecture
<!-- AndroidManifest.xml --> !... !<receiver android:name=".BootReceiver" > <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> !<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> !...
Schritt 5: Mobile Intelligenz
MTC2014 Real-Life Architecture
public class NetworkReceiver extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { // check if network is available boolean isNetworkDown = intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); if (isNetworkDown) { context.stopService(new Intent(context, UpdaterService.class)); } else { context.startService(new Intent(context, UpdaterService.class)); } } }
‣ auf Internet-Verfügbarkeit reagieren
Schritt 5: Mobile Intelligenz
MTC2014 Real-Life Architecture
<!-- AndroidManifest.xml --> !... !<receiver android:name=".NetworkReceiver" > <intent-filter> <action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> </intent-filter> </receiver> !<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> !...
Schritt 5: Mobile Intelligenz
MTC2014 Real-Life Architecture
public class PositionOverviewActivity extends Activity { ... ! class NewLocationReceiver extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { LoaderManager lm = getLoaderManager(); lm.restartLoader(0, null, PositionOverviewActivity.this); } } }
‣ auf neue POIs reagieren
Schritt 5: Mobile Intelligenz
MTC2014 Real-Life Architecture
public class PositionOverviewActivity extends Activity { ... ! @Override public void onResume() { super.onResume(); getActivity().registerReceiver(newLocationReceiver, new IntentFilter(UpdaterService.NEW_LOCATION_INTENT)); } ! @Override public void onPause() { super.onPause(); getActivity().unregisterReceiver(newLocationReceiver); } }
‣ auf neue POIs reagieren
Schritt 5: Mobile Intelligenz
MTC2014
Code Diving ...
Real-Life ArchitectureSchritt 5: Mobile Intelligenz
MTC2014 Real-Life Architecture
Schritt 6: Externer Datenzugriff
Real-Life ArchitectureMTC2014
Schritt 6: Externer Datenzugriff
Schritt 6: Externer DatenzugriffReal-Life Architecture
MTC2014
MTC2014
Externer Datenzugriff Best Practices !
‣ Content Provider zur Datenbereitstellung ‣Widget / App zur Datennutzung
Real-Life ArchitectureSchritt 6: Externer Datenzugriff
MTC2014
Externer Datenzugriff Pitfalls !
‣ Lesen vs. Schreiben ‣ Security Defaults ‣ Android 2.x vs. Android 4.x
Real-Life ArchitectureSchritt 6: Externer Datenzugriff
MTC2014
Daten verfügbar machen via ...
Content Provider
Real-Life ArchitectureSchritt 6: Externer Datenzugriff
MTC2014 Real-Life Architecture
Schritt 6: Externer Datenzugriff
MTC2014 Real-Life Architecture
<!-- AndroidManifest.xml --> !... ! <provider android:name =".PositionProvider" android:authorities ="de.openknowledge.mtc.ff.poi" /> !...
Schritt 6: Externer Datenzugriff
MTC2014 Real-Life Architecture
<!-- AndroidManifest.xml --> !... ! <provider android:name =".PositionProvider" android:authorities ="de.openknowledge.mtc.ff.poi" /> !...
Öffentlich?
Lesen vs. Schreiben?
Schritt 6: Externer Datenzugriff
MTC2014 Real-Life Architecture
<!-- AndroidManifest.xml --> !... ! <provider android:name =".PositionProvider" android:authorities ="de.openknowledge.mtc.ff.poi" android:exported ="true" android:readPermission ="de.openknowledge.mtc.ff.poi.READ_DATA" /> ...
Schritt 6: Externer Datenzugriff
MTC2014
Code Diving ...
Real-Life ArchitectureSchritt 6: Externer Datenzugriff
MTC2014 Real-Life Architecture
Schritt 7: Standorte anzeigen
Real-Life ArchitectureMTC2014
Schritt 7: Standorte anzeigen
MTC2014
Standorte anzeigen Best Practices !
‣ Interaktive Karte ‣ eigenen Standort hervorheben ‣ Detailinfos beim „Anklicken“ ‣ User Controlls
Real-Life ArchitectureSchritt 7: Standorte anzeigen
MTC2014
Standorte anzeigen Pitfalls !
‣ Google Maps v2 ‣ Google Play Service ‣ Emulator
Real-Life ArchitectureSchritt 7: Standorte anzeigen
MTC2014
Standorte anzeigen via ...
Google Maps API v2
Real-Life ArchitectureSchritt 7: Standorte anzeigen
Real-Life ArchitectureMTC2014
Schritt 7: Google Maps API v2
MTC2014
Google Maps v2 Pitfalls !
‣ API laden via Google Play Service ‣ Google Maps API Key generieren ‣Manifest.xml anpassen ‣Map Fragment „bauen“ ‣Map Activity implementieren
Schritt 7: Google Maps API v2Real-Life Architecture
MTC2014
Google Maps v2 Pitfalls !
‣ API laden via Google Play Service ‣ Google Maps API Key generieren ‣Manifest.xml anpassen ‣Map Fragment „bauen“ ‣Map Activity implementieren ‣ ... sie sehen, sie sehen nix (im Emulator)
Schritt 7: Google Maps API v2Real-Life Architecture
MTC2014
Google Maps v2 Pitfalls !
‣ API laden via Google Play Service ‣ Google Maps API Key generieren ‣Manifest.xml anpassen ‣Map Fragment „bauen“ ‣Map Activity implementieren ‣ ... sie sehen, sie sehen nix (im Emulator)
Schritt 7: Google Maps API v2Real-Life Architecture
adb -e install com.google.android.gms.apk
adb -e install com.android.vending.apk
MTC2014
Code Diving ...
Schritt 7: Google Maps API v2Real-Life Architecture
Pho
to c
redi
t: S
antiM
B .
/ Fot
er.c
om /
CC
BY-
NC
-ND
@mobileLarson @_openKnowledge
Lars Röwekamp | CIO New Technologies
Real-Life Architecture