android architecture design

125
Photo credit: SantiMB . / Foter.com / CC BY-NC-ND Real-Life Architecture @mobileLarson @_openKnowledge Lars Röwekamp | CIO New Technologies

Upload: open-knowledge-gmbh

Post on 15-Jan-2015

370 views

Category:

Technology


4 download

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.

TRANSCRIPT

Page 1: Android Architecture Design

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

Page 2: Android Architecture Design

Real-Life Architecture Android 4+

MTC2014

Eigentlich ist ja alles ganz einfach ...

Page 3: Android Architecture Design

Real-Life Architecture Android 4+

MTC2014

Eigentlich ist ja alles ganz einfach ...

Page 4: Android Architecture Design

Real-Life Architecture Android 4+

MTC2014

... auch wenn es kompliziert(er) wird.

Page 5: Android Architecture Design

Real-Life Architecture Android 4+

MTC2014

... auch wenn es kompliziert(er) wird.

Page 6: Android Architecture Design

Real-Life ArchitectureMTC2014

Splash Overview Share

Preferences

Map

Real-Life Architecture

Page 7: Android Architecture Design
Page 8: Android Architecture Design

Android 4+

Page 9: Android Architecture Design

Real-Life Architecture Android 4+

MTC2014

Page 10: Android Architecture Design

Real-Life Architecture Android 4+

MTC2014

Wo liegt das ...

Problem?

Page 11: Android Architecture Design

Es war einmal eine App ...Real-Life Architecture

MTC2014

Splash Overview Share

Preferences

Map

Page 12: Android Architecture Design

Es war einmal eine App ...Real-Life Architecture

MTC2014

Page 13: Android Architecture Design

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

Page 14: Android Architecture Design

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

Page 15: Android Architecture Design

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“

Page 16: Android Architecture Design

... und die hatte eine ArchitekturReal-Life Architecture

MTC2014

Page 17: Android Architecture Design

... und die hatte eine ArchitekturReal-Life Architecture

MTC2014

Page 18: Android Architecture Design

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

Page 19: Android Architecture Design

MTC2014 Real-Life Architecture

Schritt 1: POI senden

Page 20: Android Architecture Design

Schritt 1: POI sendenReal-Life Architecture

MTC2014

Page 21: Android Architecture Design

Schritt 1: POI sendenReal-Life Architecture

MTC2014

automatisch vs. manuell

Position / Notiz

Page 22: Android Architecture Design

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

Page 23: Android Architecture Design

MTC2014

Code Diving ...

Real-Life ArchitectureSchritt 1: POI senden

Page 24: Android Architecture Design

MTC2014

POI senden Pitfalls !

‣ Network on Main Thread ‣ Strict Mode

Real-Life ArchitectureSchritt 1: POI senden

Page 25: Android Architecture Design

MTC2014

POI senden Pitfalls !

‣ Network on Main Thread ‣ Strict Mode

Real-Life ArchitectureSchritt 1: POI senden

Page 26: Android Architecture Design

MTC2014

Netzwerkzugriff seit 4.x nur via ...

Async

Real-Life ArchitectureSchritt 1: POI senden

Page 27: Android Architecture Design

MTC2014

POI senden Async !

‣ Handler & Threads ‣ AsyncTask & „nix“ ‣ IntentService & BroadcastReceiver

Real-Life ArchitectureSchritt 1: POI senden

Page 28: Android Architecture Design

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

Page 29: Android Architecture Design

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

Page 30: Android Architecture Design

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

Page 31: Android Architecture Design

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

Page 32: Android Architecture Design

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

Page 33: Android Architecture Design

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

Page 34: Android Architecture Design

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

Page 35: Android Architecture Design

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

Page 36: Android Architecture Design

MTC2014 Real-Life Architecture

Schritt 2: Einstellungen merken

Page 37: Android Architecture Design

Real-Life ArchitectureMTC2014

Schritt 2: Einstellungen merken

Page 38: Android Architecture Design

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

Page 39: Android Architecture Design

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

Page 40: Android Architecture Design

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

Page 41: Android Architecture Design

MTC2014

Einstellungen via ...

Preferences

Real-Life ArchitectureSchritt 2: Einstellungen merken

Page 42: Android Architecture Design

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

Page 43: Android Architecture Design

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

Page 44: Android Architecture Design

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

Page 45: Android Architecture Design

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

Page 46: Android Architecture Design

Ok, aber wofür brauche ich dann

noch die PreferenceActivity?

Page 47: Android Architecture Design

MTC2014 Real-Life Architecture

Schritt 2: Einstellungen merken

Page 48: Android Architecture Design

MTC2014 Real-Life Architecture

Schritt 2: Einstellungen merken

Page 49: Android Architecture Design

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

Page 50: Android Architecture Design

Wie reagiere ich auf Änderungen?

Page 51: Android Architecture Design

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

Page 52: Android Architecture Design

Und wie verhindere ich falsche Eingaben?

Page 53: Android Architecture Design

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

Page 54: Android Architecture Design

MTC2014

Code Diving ...

Real-Life ArchitectureSchritt 2: Einstellungen merken

Page 55: Android Architecture Design

MTC2014 Real-Life Architecture

Schritt 3: POIs abgleichen

Page 56: Android Architecture Design

Real-Life ArchitectureMTC2014

Schritt 3: POIs abgleichen

Page 57: Android Architecture Design

Schritt 3: POIs abgleichenReal-Life Architecture

MTC2014

Page 58: Android Architecture Design

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

Page 59: Android Architecture Design

MTC2014

POIs abgleichen Pitfalls !

‣ regelmäßig ‣ aktuell ‣ im Hintergrund

Real-Life ArchitectureSchritt 3: POIs abgleichen

Page 60: Android Architecture Design

MTC2014

Hintergrundaufgaben via ...

Service

Real-Life ArchitectureSchritt 3: POIs abgleichen

Page 61: Android Architecture Design

MTC2014 Real-Life Architecture

Schritt 3: POIs abgleichen

Page 62: Android Architecture Design

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

Page 63: Android Architecture Design

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

Page 64: Android Architecture Design

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

Page 65: Android Architecture Design

MTC2014

Code Diving ...

Real-Life ArchitectureSchritt 3: POIs abgleichen

Page 66: Android Architecture Design

MTC2014 Real-Life Architecture

Schritt 4: Offline-Modus

Page 67: Android Architecture Design

Real-Life ArchitectureMTC2014

Schritt 4: Offline-Modus

Page 68: Android Architecture Design

Schritt 4: Offline-ModusReal-Life Architecture

MTC2014

Page 69: Android Architecture Design

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

Page 70: Android Architecture Design

MTC2014

Offline-Modus Pitfalls !

‣ Online vs. Offline ‣ Read vs. Write ‣ UI aktuell halten !

‣ Testen

Real-Life ArchitectureSchritt 4: Offline-Modus

Page 71: Android Architecture Design

MTC2014

Daten verfügbar machen via ...

SQL & Adapter

Real-Life ArchitectureSchritt 4: Offline-Modus

Page 72: Android Architecture Design

MTC2014 Real-Life Architecture

?

Schritt 4: Offline-Modus

Page 73: Android Architecture Design

MTC2014 Real-Life Architecture

Schritt 4: Offline-Modus

Page 74: Android Architecture Design

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

Page 75: Android Architecture Design

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

Page 76: Android Architecture Design

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

Page 77: Android Architecture Design

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

Page 78: Android Architecture Design

MTC2014 Real-Life Architecture

Schritt 4: Offline-Modus

Page 79: Android Architecture Design

MTC2014 Real-Life Architecture

Schritt 4: Offline-Modus

Page 80: Android Architecture Design

MTC2014 Real-Life Architecture

Schritt 4: Offline-Modus

Page 81: Android Architecture Design

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

Page 82: Android Architecture Design

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

Page 83: Android Architecture Design

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

Page 84: Android Architecture Design

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

Page 85: Android Architecture Design

MTC2014

Loader und ... !

‣ Content Provider für Lau ‣ SQLite via eigenem AsyncTaskLoader<Cursor>

Real-Life ArchitectureSchritt 4: Offline-Modus

Page 86: Android Architecture Design

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

Page 87: Android Architecture Design

MTC2014

Code Diving ...

Real-Life ArchitectureSchritt 4: Offline-Modus

Page 88: Android Architecture Design

MTC2014 Real-Life Architecture

Schritt 5: Mobile Intelligenz

Page 89: Android Architecture Design

Real-Life ArchitectureMTC2014

Schritt 5: Mobile Intelligenz

Page 90: Android Architecture Design

Schritt 5: Mobile IntelligenzReal-Life Architecture

MTC2014

Page 91: Android Architecture Design

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

Page 92: Android Architecture Design

MTC2014

Mobile Intelligenz Pitfalls !

‣ POIs im günstigsten Moment abfragen ‣ Umgebungsänderungen feststellen ‣ Zugriffe ausreichend absichern

Real-Life ArchitectureSchritt 5: Mobile Intelligenz

Page 93: Android Architecture Design

MTC2014

Mobile Intelligenz via ...

Broadcast Receiver

Real-Life ArchitectureSchritt 5: Mobile Intelligenz

Page 94: Android Architecture Design

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

Page 95: Android Architecture Design

MTC2014 Real-Life Architecture

Schritt 5: Mobile Intelligenz

Page 96: Android Architecture Design

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

Page 97: Android Architecture Design

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

Page 98: Android Architecture Design

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

Page 99: Android Architecture Design

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

Page 100: Android Architecture Design

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

Page 101: Android Architecture Design

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

Page 102: Android Architecture Design

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

Page 103: Android Architecture Design

MTC2014

Code Diving ...

Real-Life ArchitectureSchritt 5: Mobile Intelligenz

Page 104: Android Architecture Design

MTC2014 Real-Life Architecture

Schritt 6: Externer Datenzugriff

Page 105: Android Architecture Design

Real-Life ArchitectureMTC2014

Schritt 6: Externer Datenzugriff

Page 106: Android Architecture Design

Schritt 6: Externer DatenzugriffReal-Life Architecture

MTC2014

Page 107: Android Architecture Design

MTC2014

Externer Datenzugriff Best Practices !

‣ Content Provider zur Datenbereitstellung ‣Widget / App zur Datennutzung

Real-Life ArchitectureSchritt 6: Externer Datenzugriff

Page 108: Android Architecture Design

MTC2014

Externer Datenzugriff Pitfalls !

‣ Lesen vs. Schreiben ‣ Security Defaults ‣ Android 2.x vs. Android 4.x

Real-Life ArchitectureSchritt 6: Externer Datenzugriff

Page 109: Android Architecture Design

MTC2014

Daten verfügbar machen via ...

Content Provider

Real-Life ArchitectureSchritt 6: Externer Datenzugriff

Page 110: Android Architecture Design

MTC2014 Real-Life Architecture

Schritt 6: Externer Datenzugriff

Page 111: Android Architecture Design

MTC2014 Real-Life Architecture

<!-- AndroidManifest.xml --> !... ! <provider android:name =".PositionProvider" android:authorities ="de.openknowledge.mtc.ff.poi" /> !...

Schritt 6: Externer Datenzugriff

Page 112: Android Architecture Design

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

Page 113: Android Architecture Design

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

Page 114: Android Architecture Design

MTC2014

Code Diving ...

Real-Life ArchitectureSchritt 6: Externer Datenzugriff

Page 115: Android Architecture Design

MTC2014 Real-Life Architecture

Schritt 7: Standorte anzeigen

Page 116: Android Architecture Design

Real-Life ArchitectureMTC2014

Schritt 7: Standorte anzeigen

Page 117: Android Architecture Design

MTC2014

Standorte anzeigen Best Practices !

‣ Interaktive Karte ‣ eigenen Standort hervorheben ‣ Detailinfos beim „Anklicken“ ‣ User Controlls

Real-Life ArchitectureSchritt 7: Standorte anzeigen

Page 118: Android Architecture Design

MTC2014

Standorte anzeigen Pitfalls !

‣ Google Maps v2 ‣ Google Play Service ‣ Emulator

Real-Life ArchitectureSchritt 7: Standorte anzeigen

Page 119: Android Architecture Design

MTC2014

Standorte anzeigen via ...

Google Maps API v2

Real-Life ArchitectureSchritt 7: Standorte anzeigen

Page 120: Android Architecture Design

Real-Life ArchitectureMTC2014

Schritt 7: Google Maps API v2

Page 121: Android Architecture Design

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

Page 122: Android Architecture Design

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

Page 123: Android Architecture Design

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

Page 124: Android Architecture Design

MTC2014

Code Diving ...

Schritt 7: Google Maps API v2Real-Life Architecture

Page 125: Android Architecture Design

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