android real-life architecture
Post on 13-May-2015
585 Views
Preview:
DESCRIPTION
TRANSCRIPT
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+
MTC2013
Eigentlich ist ja alles ganz einfach ...
Real-Life Architecture Android 4+
MTC2013
Eigentlich ist ja alles ganz einfach ...
Real-Life Architecture Android 4+
MTC2013
... auch wenn es kompliziert(er) wird.
Real-Life Architecture Android 4+
MTC2013
... auch wenn es kompliziert(er) wird.
Real-Life ArchitectureMTC2013
Splash Overview Share
Preferences
Map
Real-Life Architecture
Android 4+
Real-Life Architecture Android 4+
MTC2013
Real-Life Architecture Android 4+
MTC2013
Wo liegt das ...
Problem?
Es war einmal eine App ...Real-Life Architecture
MTC2013
Splash Overview Share
Preferences
Map
Es war einmal eine App ...Real-Life Architecture
MTC2013
Es war einmal eine App ...Real-Life Architecture
MTC2013
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
MTC2013
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
MTC2013
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
MTC2013
... und die hatte eine ArchitekturReal-Life Architecture
MTC2013
Es war einmal eine App ...Real-Life Architecture
MTC2013
Let‘s go
‣ klein starten‣ schrittweise erweitern‣ stets lauffähig und sinnvoll
‣ Refactoring ist ein Zeichen von Stärke
MTC2013 Real-Life Architecture
Schritt 1: POI senden
Schritt 1: POI sendenReal-Life Architecture
MTC2013
MTC2013
POI senden Best Practices
‣ UI und Logik trennen‣ Kommunikation in eigene Lib auslagern‣ „Application not Responding“ vermeiden
Real-Life ArchitectureSchritt 1: POI senden
MTC2013
Code Diving ...
Real-Life ArchitectureSchritt 1: POI senden
MTC2013
POI senden Pitfalls
‣ Network on Main Thread‣ Strict Mode
Real-Life ArchitectureSchritt 1: POI senden
MTC2013
POI senden Pitfalls
‣ Network on Main Thread‣ Strict Mode
Real-Life ArchitectureSchritt 1: POI senden
MTC2013
Netzwerkzugriff seit 4.x nur via ...
Async
Real-Life ArchitectureSchritt 1: POI senden
MTC2013class 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
MTC2013class 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?
MTC2013class 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) { ... }
}
Async
Real-Life ArchitectureSchritt 1: POI senden
MTC2013// 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
MTC2013// 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
MTC2013 Real-Life Architecture
Schritt 2: Einstellungen merken
Real-Life ArchitectureMTC2013
Schritt 2: Einstellungen merken
Schritt 2: Einstellungen merkenMTC2013
Einstellungen merken Best Practices
‣ Einstellungen zentral verwalten‣ Einstellungen gruppieren ‣ UI zur Bearbeitung bereit stellen‣ Settings Design Guide beachten
Real-Life Architecture
Schritt 2: Einstellungen merkenMTC2013
Einstellungen merken Best Practices
‣ Einstellungen zentral verwalten‣ Einstellungen gruppieren ‣ UI zur Bearbeitung bereit stellen‣ Settings Design Guide beachten
Real-Life Architecture
MTC2013
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
MTC2013
Einstellungen via ...
Preferences
Real-Life ArchitectureSchritt 2: Einstellungen merken
MTC2013<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
MTC2013<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
MTC2013public 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
MTC2013public 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?
MTC2013 Real-Life Architecture
Schritt 2: Einstellungen merken
MTC2013 Real-Life Architecture
Schritt 2: Einstellungen merken
MTC2013
Code Diving ...
Real-Life ArchitectureSchritt 2: Einstellungen merken
MTC2013 Real-Life Architecture
Schritt 3: POIs abgleichen
Real-Life ArchitectureMTC2013
Schritt 3: POIs abgleichen
Schritt 3: POIs abgleichenReal-Life Architecture
MTC2013
MTC2013
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
MTC2013
POIs abgleichen Pitfalls
‣ regelmäßig‣ aktuell‣ im Hintergrund
Real-Life ArchitectureSchritt 3: POIs abgleichen
MTC2013
Hintergrundaufgaben via ...
Service
Real-Life ArchitectureSchritt 3: POIs abgleichen
MTC2013 Real-Life Architecture
Schritt 3: POIs abgleichen
MTC2013public 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
MTC2013public 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
MTC2013
Code Diving ...
Real-Life ArchitectureSchritt 3: POIs abgleichen
MTC2013 Real-Life Architecture
Schritt 4: Offline-Modus
Real-Life ArchitectureMTC2013
Schritt 4: Offline-Modus
Schritt 4: Offline-ModusReal-Life Architecture
MTC2013
MTC2013
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
MTC2013
Offline-Modus Pitfalls
‣ Online vs. Offline ‣ Read vs. Write ‣ UI aktuell halten
‣ Testen
Real-Life ArchitectureSchritt 4: Offline-Modus
MTC2013
Daten verfügbar machen via ...
SQL & Adapter
Real-Life ArchitectureSchritt 4: Offline-Modus
MTC2013 Real-Life Architecture
?
Schritt 4: Offline-Modus
MTC2013 Real-Life Architecture
Schritt 4: Offline-Modus
MTC2013 Real-Life Architecture
PoiVisitations
username, name,
...
AdapterFROM 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
MTC2013 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
MTC2013 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
MTC2013 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
MTC2013 Real-Life Architecture
Schritt 4: Offline-Modus
MTC2013 Real-Life Architecture
Schritt 4: Offline-Modus
MTC2013 Real-Life Architecture
Schritt 4: Offline-Modus
MTC2013
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
MTC2013// Activity implementing Loader Callbackspublic 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
MTC2013// Activity implementing Loader Callbackspublic 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
MTC2013// Activity implementing Loader Callbackspublic 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
MTC2013
Loader und ...
‣ Content Provider für Lau‣ SQLite via eigenem AsyncTaskLoader<Cursor>
Real-Life ArchitectureSchritt 4: Offline-Modus
MTC2013
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
MTC2013
Code Diving ...
Real-Life ArchitectureSchritt 4: Offline-Modus
MTC2013 Real-Life Architecture
Schritt 5: Mobile Intelligenz
Real-Life ArchitectureMTC2013
Schritt 5: Mobile Intelligenz
Schritt 5: Mobile IntelligenzReal-Life Architecture
MTC2013
MTC2013
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
MTC2013
Mobile Intelligenz Pitfalls
‣ POIs im günstigsten Moment abfragen‣ Umgebungsänderungen feststellen‣ Zugriffe ausreichend absichern
Real-Life ArchitectureSchritt 5: Mobile Intelligenz
MTC2013
Mobile Intelligenz via ...
BroadcastReceiver
Real-Life ArchitectureSchritt 5: Mobile Intelligenz
MTC2013
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
MTC2013 Real-Life Architecture
Schritt 5: Mobile Intelligenz
MTC2013
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
MTC2013 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
MTC2013 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
MTC2013 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
MTC2013 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
MTC2013 Real-Life Architecture
public class PositionOverviewActivity extends BaseActivity { static final String SEND_LOCATION_UPDATE_NOTIFICATION = "de.openknowledge.mtc.ff.SEND_LOCATION_UPDATE_NOTIFICATION"; ...
class PositionOverviewReceiver 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
MTC2013 Real-Life Architecture
<!-- AndroidManifest.xml -->
...
<permission android:name="de.openknowledge.mtc.ff.SEND_LOCATION_UPDATE_NOTIFICATION" android:description="@string/send_location_update_notification_permission_description" android:label="@string/send_location_update_notification_permission_label" android:permissionGroup="android.permission-group.PERSONAL_INFO" android:protectionLevel="normal" />
<permission android:name="de.openknowledge.mtc.ff.RECEIVE_LOCATION_UPDATE_NOTIFICATION" android:description="@string/receive_location_update_notification_permission_description" android:label="@string/receive_location_update_notification_permission_label" android:permissionGroup="android.permission-group.PERSONAL_INFO" android:protectionLevel="normal" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="de.openknowledge.mtc.ff.SEND_LOCATION_UPDATE_NOTIFICATION" /> <uses-permission android:name="de.openknowledge.mtc.ff.RECEIVE_LOCATION_UPDATE_NOTIFICATION" />
...
Schritt 5: Mobile Intelligenz
MTC2013
Code Diving ...
Real-Life ArchitectureSchritt 5: Mobile Intelligenz
MTC2013 Real-Life Architecture
Schritt 6: Externer Datenzugriff
Real-Life ArchitectureMTC2013
Schritt 6: Externer Datenzugriff
Schritt 6: Externer DatenzugriffReal-Life Architecture
MTC2013
MTC2013
Externer Datenzugriff Best Practices
‣ Content Provider zur Datenbereitstellung‣Widget / App zur Datennutzung
Real-Life ArchitectureSchritt 6: Externer Datenzugriff
MTC2013
Externer Datenzugriff Pitfalls
‣ Lesen vs. Schreiben‣ Security Defaults‣ Android 2.x vs. Android 4.x
Real-Life ArchitectureSchritt 6: Externer Datenzugriff
MTC2013
Daten verfügbar machen via ...
ContentProvider
Real-Life ArchitectureSchritt 6: Externer Datenzugriff
MTC2013 Real-Life Architecture
Schritt 6: Externer Datenzugriff
MTC2013 Real-Life Architecture
<!-- AndroidManifest.xml -->
...
<provider android:name =".PositionProvider" android:authorities ="de.openknowledge.mtc.ff.poi" />
...
Schritt 6: Externer Datenzugriff
MTC2013 Real-Life Architecture
<!-- AndroidManifest.xml -->
...
<provider android:name =".PositionProvider" android:authorities ="de.openknowledge.mtc.ff.poi" />
...
Öffentlich?
Lesen vs. Schreiben?
Schritt 6: Externer Datenzugriff
MTC2013 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
MTC2013
Code Diving ...
Real-Life ArchitectureSchritt 6: Externer Datenzugriff
MTC2013 Real-Life Architecture
Schritt 7: Standorte anzeigen
Real-Life ArchitectureMTC2013
Schritt 7: Standorte anzeigen
MTC2013
Standorte anzeigen Best Practices
‣ Interaktive Karte‣ eigenen Standort hervorheben‣ Detailinfos beim „Anklicken“‣ User Controlls
Real-Life ArchitectureSchritt 7: Standorte anzeigen
MTC2013
Standorte anzeigen Pitfalls
‣ Google Maps v2‣ Google Play Service‣ Emulator
Real-Life ArchitectureSchritt 7: Standorte anzeigen
MTC2013
Standorte anzeigen via ...
GoogleMaps API v2
Real-Life ArchitectureSchritt 7: Standorte anzeigen
Real-Life ArchitectureMTC2013
Schritt 7: Google Maps API v2
MTC2013
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
MTC2013
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
MTC2013
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
MTC2013
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
Real-Life Architecture
@mobileLarson @_openKnowledge
Lars Röwekamp | CIO New Technologies
top related