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


Recommended