Android Real-Life Architecture

Post on 13-May-2015

584 views 2 download

description

Speaker: Lars Röwekamp MobileTechCon 2013 Berlin Die mittlerweile ausgereiften Android-APIs sowie das zugehörige Tooling machen es einem Mobile Developer denkbar einfach, Android-4.x-Anwendungen zu programmieren. Dank ActionBar, Fragments und Co. ist auch das Portieren für unterschiedlichste Device-Typen, wie z.B. Smartphones und Tablets, heute kein Rocket Science mehr. Aber wie kommt es dann, dass so viele Anwendungen den "Real-Life"-Check nicht bestehen und schlecht bewertet werden? Fakt ist, dass eine ergonomisch gute App deutlich mehr benötigt als nur schöne UIs - das Zauberwort heißt "Architektur".

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