Post on 15-Jan-2015
description
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