android real-life architecture

86
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 23-Jun-2015

494 views

Category:

Technology


1 download

DESCRIPTION

Speaker: Lars Röwekamp MobileTechCon 14.3.2013 Apps mit Android 4.x zu programmieren, ist bekanntlich denkbar einfach. Dies gilt dank ActionBar, Fragments & Co. auch dann noch, wenn sie auf verschiedensten Devices visuell ansprechend sein sollen. Wie kommt es dann aber, dass so viele Anwendungen den "Real Life"-Check der Nutzer nicht bestehen und entsprechend schlecht bewertet werden? Eine ergonomisch gute App benötigt deutlich mehr als nur schöne UIs - das Zauberwort heißt "Architektur". Die Session zeigt anhand eines Beispiels klassische Anti-Patterns des Android Application Designs und hilft, sie zukünftig durch eine gut durchdachte Architektur zu vermeiden.

TRANSCRIPT

Page 1: Android Real-life Architecture

Ph

oto

cre

dit:

Sa

ntiM

B .

/ F

ote

r.co

m /

CC

BY

-NC

-ND

Real-Life Architecture

@mobileLarson @_openKnowledge

Lars Röwekamp | CIO New Technologies

Page 2: Android Real-life Architecture

Real-Life Architecture Android 4+

MTC2013

Eigentlich ist ja alles ganz einfach ...

Page 3: Android Real-life Architecture

Real-Life Architecture Android 4+

MTC2013

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

Page 4: Android Real-life Architecture
Page 5: Android Real-life Architecture

Android 4+

Page 6: Android Real-life Architecture

Real-Life Architecture Android 4+

MTC2013

Wo liegt das ...

Problem?

Page 7: Android Real-life Architecture

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

MTC2013

Splash Overview Share

Preferences

Map

Page 8: Android Real-life Architecture

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

MTC2013

Page 9: Android Real-life Architecture

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

Page 10: Android Real-life Architecture

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‣Localisation Support

Page 11: Android Real-life Architecture

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 „Security“

Page 12: Android Real-life Architecture

... und die hatte eine ArchitekturReal-Life Architecture

MTC2013

Page 13: Android Real-life Architecture

... und die hatte eine ArchitekturReal-Life Architecture

MTC2013

Page 14: Android Real-life Architecture

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

Page 15: Android Real-life Architecture

Schritt 1: POI sendenFriend Finder App

MTC2013

Page 16: Android Real-life Architecture

MTC2013

POI senden Best Practices

‣UI und Logik trennen‣Kommunikation in Lib auslagern

Friend Finder App

Schritt 1: POI senden

Page 17: Android Real-life Architecture

MTC2013 Friend Finder App

Schritt 1: POI senden

Page 18: Android Real-life Architecture

MTC2013

POI senden Pitfalls

‣Network on Main Thread‣Strict Mode

Friend Finder App

Schritt 1: POI senden

Page 19: Android Real-life Architecture

MTC2013

Netzwerkzugriff nur via ...

Async

Friend Finder App

Schritt 1: POI senden

Page 20: Android Real-life Architecture

MTC2013

class PostToFriendFinder extends AsyncTask<Void, Integer, String> {

private PointOfInterest poi; private String note;

public PostToFriendFinder(PointOfInterest poi, String note) { ... }

// @Overrideprotected 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."; }

}

@Overrideprotected void onProgressUpdate(Integer... values) { ... }

@Overrideprotected void onPostExecute(String result) { ... }

}

Async Async

Friend Finder App

Schritt 1: POI senden

Page 21: Android Real-life Architecture

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 interestPointOfInterest poi = new PointOfInterest(longitude, latitude, 0.00F);

// create async tasks to communicate with the cloud servicenew PostToFriendFinder(poi, note).execute();

}

Friend Finder App

Schritt 1: POI senden

Page 22: Android Real-life Architecture

Real-Life ArchitectureMTC2013

Schritt 2: Einstellungen merken

Page 23: Android 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

Friend Finder App

Page 24: Android 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

Friend Finder App

Schritt 2: Einstellungen merken

Page 25: Android Real-life Architecture

MTC2013

Einstellungen via ...

Preferences

Friend Finder App

Schritt 2: Einstellungen merken

Page 26: Android Real-life Architecture

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>

Friend Finder App

Schritt 2: Einstellungen merken

Page 27: Android Real-life Architecture

MTC2013

public 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 PreferenceActivity {

    @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();    }}

Friend Finder App

Schritt 2: Einstellungen merken

Page 28: Android Real-life Architecture

MTC2013

public class FriendFinderApplication extends Application implements OnSharedPreferenceChangeListener {

SharedPreferences preferences; ...

@Overridepublic 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 ...

} ...

@Overridepublic void onSharedPreferenceChanged(SharedPreferences sharedPreferences,

String key) {// force reload of preferences and reinitialization of global

data...

}}

Friend Finder App

Schritt 2: Einstellungen merken

Page 29: Android Real-life Architecture

MTC2013 Friend Finder App

Schritt 2: Einstellungen merken

Page 30: Android Real-life Architecture

Real-Life ArchitectureMTC2013

Schritt 3: POIs abgleichen

Page 31: Android Real-life Architecture

Schritt 3: POIs abgleichenReal-Life Architecture

MTC2013

Page 32: Android Real-life Architecture

MTC2013

POIs abgleichen Best Practices

‣POIs regelmäßig abgleichen‣POIs so aktuell wie möglich halten‣POIs im Hintergrund laden

Friend Finder App

Schritt 3: POIs abgleichen

Page 33: Android Real-life Architecture

MTC2013

POIs abgleichen Pitfalls

‣regelmäßig‣aktuell‣im Hintergrund

Friend Finder App

Schritt 3: POIs abgleichen

Page 34: Android Real-life Architecture

MTC2013

Hintergrundaufgaben via ...

Service

Friend Finder App

Schritt 3: POIs abgleichen

Page 35: Android Real-life Architecture

MTC2013 Friend Finder App

Schritt 3: POIs abgleichen

Page 36: Android Real-life Architecture

MTC2013

public 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;

}}

}}

}

Friend Finder App Starten & Stoppen? Starten & Stoppen?

Online vs. Offline? Online vs. Offline?

Schritt 3: POIs abgleichen

Page 37: Android Real-life Architecture

MTC2013 Friend Finder App

Schritt 3: POIs abgleichen

Page 38: Android Real-life Architecture

Real-Life ArchitectureMTC2013

Schritt 4: Offline-Modus

Page 39: Android Real-life Architecture

Schritt 4: Offline-ModusReal-Life Architecture

MTC2013

Page 40: Android Real-life Architecture

MTC2013

Offline Modus Best Practices

‣POIs via Online-Modus abgleichen‣POIs für Offline-Modus speichern‣POI UI aktuell halten

‣Datenzugriff kapseln ‣Datenzugriff optimieren

Friend Finder App

Schritt 4: Offline-Modus

Page 41: Android Real-life Architecture

MTC2013

Offline-Modus Pitfalls

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

‣Testen

Friend Finder App

Schritt 4: Offline-Modus

Page 42: Android Real-life Architecture

MTC2013

Daten verfügbar machen via ...

SQL & Adapter

Friend Finder App

Schritt 4: Offline-Modus

Page 43: Android Real-life Architecture

MTC2013 Friend Finder App

?

Schritt 4: Offline-Modus

Page 44: Android Real-life Architecture

MTC2013 Friend Finder App

Schritt 4: Offline-Modus

Page 45: Android Real-life Architecture

MTC2013 Friend Finder App

PoiVisitations

username, name, note,longitude, ...

...

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 46: Android Real-life Architecture

MTC2013 Friend Finder App

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 viewlistView = (ListView) findViewById(R.id.list_position_overview);

// lookup datafriendFinderData = ((FriendFinderApplication)

getApplication()).getFriendFinderData();

}...

}

Schritt 4: Offline-Modus

Page 47: Android Real-life Architecture

MTC2013 Friend Finder App

public class PositionOverviewActivity extends Activity {

Cursor cursor;ListView listView;PositionOverviewAdapter adapter;FriendFinderData friendFinderData;

...

public void onResume() {

super.onResume(); cursor = friendFinderData.getPoiVisitations();

// start managing the cursorstartManagingCursor(cursor);

// created special adapteradapter = new PositionOverviewAdapter(this, cursor);listView.setAdapter(adapter);

} ...}

Schritt 4: Offline-Modus

Page 48: Android Real-life Architecture

MTC2013 Friend Finder App

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 adapteradapter = new PositionOverviewAdapter(this, cursor);listView.setAdapter(adapter);

} ...}

Schritt 4: Offline-Modus

Page 49: Android Real-life Architecture

MTC2013 Friend Finder App

Schritt 4: Offline-Modus

Page 50: Android Real-life Architecture

MTC2013 Friend Finder App

Schritt 4: Offline-Modus

Page 51: Android Real-life Architecture

MTC2013 Friend Finder App

Schritt 4: Offline-Modus

Page 52: Android Real-life Architecture

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

Friend Finder App

Schritt 4: Offline-Modus

Page 53: Android Real-life Architecture

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); } ...

}

Friend Finder App

Schritt 4: Offline-Modus

Page 54: Android Real-life Architecture

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); } ...}

Friend Finder App

Schritt 4: Offline-Modus

Page 55: Android Real-life Architecture

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); }}

Friend Finder App

Schritt 4: Offline-Modus

Page 56: Android Real-life Architecture

MTC2013

Loader und ...

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

Friend Finder App

Schritt 4: Offline-Modus

Page 57: Android Real-life Architecture

MTC2013 Friend Finder App

Schritt 4: Offline-Modus

Page 58: Android Real-life Architecture

Real-Life ArchitectureMTC2013

Schritt 5: Mobile Intelligenz

Page 59: Android Real-life Architecture

Schritt 5: Mobile IntelligenzReal-Life Architecture

MTC2013

Page 60: Android Real-life Architecture

MTC2013

Mobile Intelligenz Best Practices

‣POIs nur senden/abfragen, wenn Internet‣POIs nur senden/abfragen, wenn Strom‣UI aktualisieren, wenn neue Daten‣...

Friend Finder App

Schritt 5: Mobile Intelligenz

Page 61: Android Real-life Architecture

MTC2013

Mobile Intelligenz Pitfalls

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

Friend Finder App

Schritt 5: Mobile Intelligenz

Page 62: Android Real-life Architecture

MTC2013

Mobile Intelligenz via ...

BroadcastReceiver

Friend Finder App

Schritt 5: Mobile Intelligenz

Page 63: Android Real-life Architecture

MTC2013

Was sind Broadcast Receiver?

‣Publisher-Subscriber-Pattern‣Observer-Pattern

‣App / System sendet Nachricht‣Broadcast Receiver hört auf Nachricht

Friend Finder App

Schritt 5: Mobile Intelligenz

Page 64: Android Real-life Architecture

MTC2013 Friend Finder App

Schritt 5: Mobile Intelligenz

Page 65: Android Real-life Architecture

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

Friend Finder App

Schritt 5: Mobile Intelligenz

Page 66: Android Real-life Architecture

MTC2013 Friend Finder App

public class BootReceiver extends BroadcastReceiver {

private static final String TAG = BootReceiver.class.getSimpleName();

// start update service after system start automatically @Overridepublic 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 67: Android Real-life Architecture

MTC2013 Friend Finder App

<!-- 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 68: Android Real-life Architecture

MTC2013 Friend Finder App

public class NetworkReceiver extends BroadcastReceiver {

public void onReceive(Context context, Intent intent) {

// check if network is availableboolean 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 69: Android Real-life Architecture

MTC2013 Friend Finder App

<!-- 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 70: Android Real-life Architecture

MTC2013 Friend Finder App

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) {cursor = ... .getPoiVisitations();adapter.changeCursor(cursor);

}}

}

‣auf neue POIs reagieren

Schritt 5: Mobile Intelligenz

Page 71: Android Real-life Architecture

MTC2013 Friend Finder App

<!-- 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

Page 72: Android Real-life Architecture

MTC2013 Friend Finder App

Schritt 5: Mobile Intelligenz

Page 73: Android Real-life Architecture

Real-Life ArchitectureMTC2013

Schritt 6: Externer Datenzugriff

Page 74: Android Real-life Architecture

Schritt 6: Externer DatenzugriffReal-Life Architecture

MTC2013

Page 75: Android Real-life Architecture

MTC2013

Externer Datenzugriff Best Practices

‣Content Provider zur Datenbereitstellung‣Widget / App zur Datennutzung

Friend Finder App

Schritt 6: Externer Datenzugriff

Page 76: Android Real-life Architecture

MTC2013

Externer Datenzugriff Pitfalls

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

Friend Finder App

Schritt 6: Externer Datenzugriff

Page 77: Android Real-life Architecture

MTC2013

Daten verfügbar machen via ...

ContentProvider

Friend Finder App

Schritt 6: Externer Datenzugriff

Page 78: Android Real-life Architecture

MTC2013 Friend Finder App

Schritt 6: Externer Datenzugriff

Page 79: Android Real-life Architecture

MTC2013 Friend Finder App

<!-- AndroidManifest.xml -->

...

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

...

Öffentlich? Öffentlich?

Lesen vs. Schreiben? Lesen vs. Schreiben?

Schritt 6: Externer Datenzugriff

Page 80: Android Real-life Architecture

MTC2013 Friend Finder App

<!-- 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 81: Android Real-life Architecture

MTC2013 Friend Finder App

Schritt 6: Externer Datenzugriff

Page 82: Android Real-life Architecture

Real-Life ArchitectureMTC2013

Schritt 7: Google Maps API v2

Page 83: Android Real-life Architecture

Real-Life ArchitectureMTC2013

Schritt 7: Google Maps API v2

Page 84: Android Real-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

‣Fehler suchen ;-)

Friend Finder App

Schritt 7: Google Maps API v2

Page 85: Android Real-life Architecture

MTC2013 Friend Finder App

Schritt 7: Google Maps API v2

Page 86: Android Real-life Architecture

Ph

oto

cre

dit:

Sa

ntiM

B .

/ F

ote

r.co

m /

CC

BY

-NC

-ND

Real-Life Architecture

@mobileLarson @_openKnowledge

Lars Röwekamp | CIO New Technologies