Android Application Design im Praxischeck
Upcoming SlideShare
Loading in...5
×
 

Android Application Design im Praxischeck

on

  • 241 views

Android Application Design im Praxischeck ...

Android Application Design im Praxischeck
Speaker: Lars Röwekamp

Spätestens seit Android 4 ist Google der Sprung vom Smartphone aufs Tablet gelungen und somit ein echter Konkurrent für die iOS-Welt entstanden. Unmengen an APIs machen es dem Android-Entwickler einfach, die vielen Frameworkfeatures, wie Maps, Contacts, Kalender etc., in eigene Apps zu integrieren. Wie kommt es dann aber, dass nach wie vor ein Großteil der am Markt existierenden Apps eher schlecht als recht funktionieren und entsprechende Bewertungen bekommen? Die Session zeigt typische Pitfalls im Android Application Design und zeigt, wie man mit einer gut durchdachten Architektur und Ergonomie bei den Endanwendern punkten kann.

Statistics

Views

Total Views
241
Views on SlideShare
236
Embed Views
5

Actions

Likes
0
Downloads
4
Comments
0

1 Embed 5

http://www.slideee.com 5

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Android Application Design im Praxischeck Android Application Design im Praxischeck Presentation Transcript

  • @mobileLarson 
 @_openKnowledge Lars Röwekamp | CIO New Technologies App Design im 
 Praxis-Check
  • Kunde droht mit Auftrag ...
  • „Can you do this?“ View slide
  • Can we do this? View slide
  • „Can you do this?“
  • „Yes, of course we can!“ „Can you do this?“
  • „But, lots of pitfalls ahead.“ „And, lots of work to do.“
  • 35.000+ Produkte 5.000+ Änderungen p.m. 2000+ Produktgruppen Tablets & Smartphones 15.000+ Bilder In-App Navigation Komplexe Suche
  • Pack mas’!
  • Problem: Sehr, sehr viele Daten Lösung: No problèmo!
  • Leider doch. 50MB (+2x2GB)
  • > 50 MB (+ 2 x 5 GB) > APK Limit von 50 MB > Main Expension File (max. 2 GB) > Patch Expension File (max. 2 GB) ! > Google Support via API > „automatischer“ Download im Hintergrund > Ablage in <shared-storage>/Android/… ! > Google Play Store Limits Problem: Sehr, sehr viele Daten
  • > 50 MB (+ 2 x 5 GB) > APK Limit von 50 MB > Main Expension File (max. 2 GB) > Patch Expension File (max. 2 GB) ! > Google Support via API > „automatischer“ Download im Hintergrund > Ablage in <shared-storage>/Android/… ! > Google Play Store Limits Problem: Sehr, sehr viele Daten
  • > 50 MB (+ 2 x 5 GB) > APK Limit von 50 MB > Main Expension File (max. 2 GB) > Patch Expension File (max. 2 GB) ! > Google Support via API > „automatischer“ Download im Hintergrund > Ablage in <shared-storage>/Android/… ! > Google Play Store Limits Problem: Sehr, sehr viele Daten
  • > 50 MB (+ 2 x 5 GB) > APK Limit von 50 MB > Main Expension File (max. 2 GB) > Patch Expension File (max. 2 GB) ! > Google Support via API > „automatischer“ Download im Hintergrund > Ablage in <shared-storage>/Android/… ! > Google Play Store Limits Problem: Sehr, sehr viele Daten
  • Problem: Sehr, sehr viele Daten Lösung: DB Download ...
  • > 200 MB+ > Wifi Check inkl. optional Loading > Inkrementeller Download inkl. Resume ! > einige Minuten > Vorab Hinweis auf „Ladezeit“ anzeigen > Working Progress Feedback inkl. „Step X of Y“ > Loading Progress Feedback inkl. „X von Y“ > „manueller“ DB Download Problem: Sehr, sehr viele Daten
  • > DB Download > Check for WiFi <!-- WiFi check: Declare Wifi as needed -->! <manifest ...>!     <uses-feature android:name="android.hardware.wifi" ! android:required=["true"|"false"] />!     ...! </manifest> Problem: Sehr, sehr viele Daten
  • > DB Download > Check for WiFi only // WiFi check: check for existing WiFi! ! ConnectivityManager cm = (ConnectivityManager) ! getSystemService(Context.CONNECTIVITY_SERVICE);! ! ! State wifi = ! cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI)! ! ! ! ! .getState();! ! if (wifi == NetworkInfo.State.CONNECTED || 
 wifi == NetworkInfo.State.CONNECTING) {! // WiFi is available! ! } else { ! ... Problem: Sehr, sehr viele Daten
  • Problem: Sehr, sehr viele Daten // WiFi check: check for WiFi detail info! WifiManager wifiManager = (WifiManager) ! getSystemService(Context.WIFI_SERVICE);! ! WifiInfo wifiInfo = wifiManager.getConnectionInfo();! SupplicantState wifiState = ! wifiInfo.getSupplicantState();! ! if (wifiState == SupplicantState.COMPLETED) {! // WiFi is available - get some wifiInfo details! int linkSpeed = wifiInfo.getLinkSpeed(); ...! ! } else { ! ... > DB Download > Check for WiFi Details
  • Problem: Sehr, sehr viele Daten // WiFi check: check for WiFi detail info! WifiManager wifiManager = (WifiManager) ! getSystemService(Context.WIFI_SERVICE);! ! WifiInfo wifiInfo = wifiManager.getConnectionInfo();! SupplicantState wifiState = ! wifiInfo.getSupplicantState();! ! if (wifiState == SupplicantState.COMPLETED) {! // WiFi is available - get some wifiInfo details! int linkSpeed = wifiInfo.getLinkSpeed(); ...! ! } else { ! ... > DB Download > Check for WiFi Details
  • > DB Download > Incremental Loading // Step 1: Prepare download with DownloadManager! DownloadManager.Request downloadRequest = new ! DownloadManager.Request(Uri.parse(DOWNLOAD_URI));! ! // set additional optional information: ! // Title, Description, MimeType, ExternalFileDir, ...! ! // allow download via Mobile or Wifi! downloadRequest.setAllowedNetworkTypes(
 DownloadManager.Request.NETWORK_WIFI | ! DownloadManager.Request.NETWORK_MOBILE);! ! // do not show the file in "Downloads"! downloadRequest.setVisibleInDownloadsUi(false); Problem: Sehr, sehr viele Daten
  • > DB Download > Incremental Loading ! // Step 2: Start download with DownloadManager! ...! // register broadcast receiver for DOWNLOAD_COMPLETE! IntentFilter intentFilter = new IntentFilter(! DownloadManager.ACTION_DOWNLOAD_COMPLETE);! registerReceiver(receiver, intentFilter);! ! // Enqueue download! downloadId = downloadManager.enqueue(downloadRequest); ! ! ! ! Problem: Sehr, sehr viele Daten
  • > DB Download > Incremental Loading // Step 3: Do some stuff after download has finished !      BroadcastReceiver  receiver  =  new  BroadcastReceiver()  {   !            @Override              public  void  onReceive(Context  ctx,  Intent  intent)  {                                            //  1.  DownloadManager.ACTION_DOWNLOAD_COMPLETE?                        //  2.  ID  =  DownloadManager.EXTRA_DOWNLOAD_ID                      //  3.  query  DownloadManager  for  ID                      //  4.  DownloadManager.STATUS_SUCCESSFUL?                      //  5.  do  some  stuff  with  download               }        };   Problem: Sehr, sehr viele Daten
  • > DB Download > Showing Progress ! ! // show progress via DownloadManager (easy version)! public void showDownload(View view) {!         Intent intent = new Intent();!         intent.setAction(
 DownloadManager.ACTION_VIEW_DOWNLOADS);!         startActivity(i);!    } Problem: Sehr, sehr viele Daten
  • > DB Download > Showing Progress // show progress via own Activity (eXtended version)! public class ProgressActivity extends Activity {! ! ! private ProgressBar mProgress; // or ProgressDialog! private int mProgressStatus = 0;! private Handler mHandler = new Handler();! ! protected void onCreate(Bundle icicle) {! super.onCreate(icicle);! setContentView(R.layout.progressbar_activity); ! mProgress = (ProgressBar)findViewById(R.id.pb);! // Start some work in background thread ! doWorkAndUpdateProgressBarInBackground(); ! } …! } Problem: Sehr, sehr viele Daten
  • > DB Download > Showing Progress ! // doWorkAndUpdateProgressBarInBackground() ! new Thread(new Runnable() {! ! public void run() {! ! while (mProgressStatus < 100) {! ! mProgressStatus = checkDonwloadManagerStatus(); ! ! ! // Update progress bar in UI thread! ! mHandler.post(new Runnable() {! ! public void run() {! ! mProgress.setProgress(mProgressStatus);! ! }! ! });! ! }! }! }).start(); Problem: Sehr, sehr viele Daten
  • > DB Download > Showing Progress <!-- Spinning wheel -->!   <ProgressBar!      style="@android:style/Widget.ProgressBar.Large"!      ... /> ! ! <!-- Progress bar from 0 .. 100 -->!   <ProgressBar!      style="@android:style/Widget.ProgressBar.Horizontal"!      ... /> Problem: Sehr, sehr viele Daten
  • Problem: Sehr, sehr viele Daten Lösung 2: DB Download +
  • > ca. 10 MB > Lazy Loading Ansatz zum Füllen der DB > Nur Basisdaten initial laden/vorhalten > Produktdetails und Bilder „as needed“ ! > Unterstützte Funktionen > Home Page Angebote anzeigen > Kategorien anzeigen > Suchen1), Filtern, ... > DB Download+ Problem: Sehr, sehr viele Daten
  • > ca. 10 MB > Lazy Loading Ansatz zum Füllen der DB > Nur Basisdaten initial laden/vorhalten > Produktdetails und Bilder „as needed“ ! > Unterstützte Funktionen > Home Page Angebote anzeigen > Kategorien anzeigen > Suchen1), Filtern, ... > DB Download+ 1) Online-/Offline-Suche Problem: Sehr, sehr viele Daten
  • > DB Download+ Problem: Sehr, sehr viele Daten
  • > DB Download+ Problem: Sehr, sehr viele Daten
  • > Picasso (Square / komfortable API) > UrlImageViewHelper (sehr schnell) > Volley (Google / gut durchdacht) > Android Universal Image Loader (most popular) ! > „Do it yourself“ vs. „3rd Party“ Problem: Sehr, sehr viele Daten
  • Problem: Sehr, sehr viele Updates Lösung: Update-Protokoll
  • > ca. 5000 Änderungen p.M. > Modification XML Info zum „Start-up“ laden > Neue/geänderte/gelöschte Produkte/Bilder ! > Protokoll (XML basiert) > Gelöschte Produkte/Bilder löschen > Neue Produkte/Bilder laden > Geänderte Produkte/Bilder laden > Update Protokoll Problem: Sehr, sehr viele Updates
  • Problem: Sehr, sehr viele Updates Lösung 2: Update-Protokoll+
  • > ca. 5000 Änderungen p.M. > Modification Info zum „Start-up“ laden > Produktdetails „as needed“ ! > Protokoll (JSON basiert) > Neue Produkte als Dummy anlegen > Gelöschte Produkte markieren > Geänderte Produkte markieren > Aufräumen „on-the-fly“ > Update Protokoll+ Problem: Sehr, sehr viele Updates
  • > ca. 5000 Änderungen p.M. > Modification Info zum „Start-up“ laden > Produktdetails „as needed“ ! > Protokoll (JSON basiert) > Neue Produkte als Dummy anlegen > Gelöschte Produkte markieren > Geänderte Produkte markieren > Aufräumen „on-the-fly“ > Update Protokoll+ Problem: Sehr, sehr viele Updates
  • Problem: Sehr, sehr viele Updates > JSON
  • Problem: Sehr, sehr viele Updates > JSON
  • Problem: Sehr, sehr viele Updates > JSON
  • Problem: Sehr, sehr viele Updates > JSON
  • Problem: Lesbare Datenbank Lösung: brauche ich eine?
  • Problem: Lesbare Datenbank
  • Problem: Lesbare Datenbank Lösung: Encryption
  • > DB im Android Filesystem > DB Verschlüsselung via SQLCipher > 256-bit AES Encryption für SQLite > Proxies für wichtigsten Android DB Klassen ! > SQLCipher anwenden > Step 1: Imports setzen > Step 2: SQLCipher Libs laden > Step 3: alles wie sonst auch > Encryption Problem: Lesbare Datenbank
  • > Encryption // Step 1: import sqlcipher! import info.guardianproject.database.! sqlcipher.SQLDatabase; ! ! // Trigger database initialization! public class SqlCypherActivity extends Activity {! ...! } Problem: Lesbare Datenbank
  • > Encryption // Trigger database initialization! public class SqlCypherActivity extends Activity {! ! ! @Override! public void onCreate(Bundle savedInstanceState) {! super.onCreate(savedInstanceState);! setContentView(R.layout.main); ! initializeSQLCipher();! }! ...! } Problem: Lesbare Datenbank
  • > Encryption // Trigger database initialization! public class SqlCypherActivity extends Activity {! ! private void InitializeSQLCipher() {! // Step 2: Load SQLCipher Libs! SQLiteDatabase.loadLibs(this);! ! // Step 3: Business as usual! ...! } ! ...! ! } Problem: Lesbare Datenbank
  • Problem: Diversifikation Lösung: Ignorieren
  • 4.3 10 % 4.2 17 % 4.1 35,3 % 4.0 15,2 % 2.3 19 % 2.2 2.3 3.x 4.0 4.1 4.2 4.3 4.4 VersionProblem: Diversifikation
  • xlarge 4,9 %large 7,7 % middle 79 % small 8 % small middle large xlarge GrößeProblem: Diversifikation
  • xxhdpi 12 % xhdpi 20,7 % hdpi 34,6 % mdpi 22 % ldpi 9 % ldpi mdpi tvdpi hdpi xhdpi xxhdpi Problem: Diversifikation Auflösung
  • Lösung 2: Teilweise ignorieren Problem: Diversifikation
  • 4.3 10 % 4.2 17 % 4.1 35,3 % 4.0 15,2 % 2.3 19 % 2.2 2.3 3.x 4.0 4.1 4.2 4.3 4.4 VersionProblem: Diversifikation
  • Lösung 3: Unified UI Problem: Diversifikation
  • > Unified UI Problem: Diversifikation
  • > Unified UI Problem: Diversifikation
  • > wäre schön, wären da nicht die Versionen ! > Step 1: wenn möglich kompatibel bleiben > Step 2: wenn möglich Support Library > Step 3: wenn möglich 3rd Party Lösungen > Step 4: wenn möglich „Weiche“ ! > Step 5: WTF! Verschiedene APKs ! > Unified UI Problem: Diversifikation Version
  • > unterstützt Abwärtskompabilität > v4 für Android 1.6 und höher > v7 für Android 2.1 und höher > v8 für Android 2.2 und höher > v13 für Android 3.2 und höher ! ! ! > Unified UI via Support Libraries Problem: Diversifikation Version
  • > Fragment (v4) > NotificationCombat (v4) > LocalBroadcastManager (v4) > ViewPager (v4) > DrawerLayout (v4) > SlidingPaneLayout (v4) > Loader (v4) ! > Unified UI via Support Libraries Problem: Diversifikation Version
  • > ActionBar (v7) > ActionBarActivity (v7) > ShareActionProvider (v7) > GridLayout (v7) > MediaRouter (v7) > RenderScript (v8) > FragmentCombat (v13) ! > Unified UI via Support Libraries Problem: Diversifikation Version
  • > ActionBarSherlock (Android 2.x) ! > Unified UI via 3rd Party Libraries Problem: Diversifikation Version
  • > Unified UI via „Versions-Weiche“ Version ! // check version of current device! int apiVersion = android.os.Build.VERSION.SDK_INT;! if (apiVersion >= android.os.Build.VERSION_CODES.FROYO){! // Do something for froyo and above versions! } else{! // do something for phones running an SDK before froyo! } Problem: Diversifikation
  • > Unified UI via „Feature-Weiche“ Version ! // check version of current device! PackageManager packageManager = this.getPackageManager();! ! if (packageManager.hasSystemFeature(! PackageManager.FEATURE_NFC)) {! // NFC feature is available ! ! ! ...!! } else { ! // NFC feature is NOT available ! ! ! ...!! } Problem: Diversifikation
  • Lösung 4: Multi-Device UI Problem: Diversifikation
  • > Multi-Device UI Problem: Diversifikation
  • > Multi-Device UI Problem: Diversifikation > UI Fragments als „Building Blocks“ > UI Activity Layout als „Block Assembler“ > UI Activity Class als „Block Assembler Logic“ ! > UI Ressources als „Switch“ ! Größe
  • > Multi-Device UI Problem: Diversifikation Größe
  • > Activity Modul a.k.a. Sub Activity > inkl. eigener UI > inkl. eigenem Lifecycle ! > benötigt immer eine umliegende Activity > Lifecycle passt sich Activity-Lifecycle an > Fragment Problem: Diversifikation Größe
  • > Size: small, normal, large, xlarge > Density: ldpi, mdpi, hdpi, xhdpi > Orientation: landscape, portrait > Designation: sw, w, h, ...dp ! > Ressources Problem: Diversifikation Größe
  • > Size: small, normal, large, xlarge > Density: ldpi, mdpi, hdpi, xhdpi > Orientation: landscape, portrait > Designation: sw, w, h, ...dp ! > Ressources Problem: Diversifikation Deprecated Added Größe
  • Größe > Multi-Device UI in Aktion Problem: Diversifikation
  • Größe > Multi-Device UI in Aktion Problem: Diversifikation
  • Problem: In-App Navigation Lösung: In-App Navigation ;-)
  • > Activity Stack Problem: In-App Navigation
  • > Activity Stack > Intent Flags > FLAG_ACTIVITY_NEW_TASK > FLAG_ACTIVITY_SINGLE_TOP > FLAG_ACTIVITY_CLEAR_TOP > Launch Mode > standard > singleTop > singleTask > singleInstance Problem: In-App Navigation
  • > Activity Stack Problem: In-App Navigation
  • > Back vs. Up Problem: In-App Navigation
  • > Back vs. Up > „Back“ berücksichtigt Activity Stack > „Up“ berücksichtigt Use Case Stack ! > In-App Navigation > Up-Navigation via In-App Buttons > Manipulation des Activity Stacks > Für Home und „Main Activities“ > In-App Navigation Problem: In-App Navigation
  • Problem: In-App Navigation
  • Problem: In-App Navigation
  • Problem: In-App Navigation
  • Problem: In-App Navigation Deep Dive
  • Problem: In-App Navigation ! ! @Override public void onCreate(Bundle savedInstanceState) {! super.onCreate(savedInstanceState);! ! ! setContentView(R.layout.main);! ! ! ActionBar actionBar = getActionBar();! ! actionBar.setDisplayHomeAsUpEnabled(true);! ! ...! } Step 1: Enable „Up Navigation“
  • Problem: In-App Navigation ! ! @Override! public boolean onOptionsItemSelected(MenuItem item) {! switch (item.getItemId()) {! ! // app icon in action bar clicked: go home! case android.R.id.home:! Intent i = new Intent(this, MainActivity.class);! i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);! startActivity(intent);! return true;! ! default:! return super.onOptionsItemSelected(item);! } ! } Step 2: Realize „Up Navigation“
  • > … ziemlich viel Aufwand, oder? > … was ist mit „Deep-Dive“ Intents? > Eigentlich ganz einfach, aber … Problem: In-App Navigation
  • Problem: In-App Navigation ! ! ! <activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> ... </intent-filter> </activity> ! <activity android:name=".ResultActivity" android:parentActivityName=".MainActivity"> <meta-data android:name="android.support.PARENT_ACTIVITY" android:value=".MainActivity"/> </activity> Step 1: Enable „Up Navigation“
  • Problem: In-App Navigation ! ! @Override! public boolean onOptionsItemSelected(MenuItem item) {! switch (item.getItemId()) {! ! // app icon in action bar clicked: go home! case android.R.id.home:! Intent i = new Intent(this, MainActivity.class);! i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);! startActivity(intent);! return true;! ... ! default:! return super.onOptionsItemSelected(item);! } ! } Step 2: Realize „Up Navigation“
  • > Einsprung von außen „mitten“ in die App > Navigation Stack wird künstlich aufgebaut a.k.a. Synthetic Back Stack ! > Was ist mit dem fehlenden Kontext? „Music 
 Details“ navigiert zurück zur „Music Liste“, 
 aber zu welcher? > „Deep-Dive“ Intents Problem: In-App Navigation
  • Problem: In-App Navigation ! ! @Override public void onPrepareNavigateUpTaskStack(! TaskStackBuilder builder) {! ! // retrieve intent for manipulation! int position = ...; ! Intent intent = builder.getIntentAt(position);! ! // manipulate intent ! intent.putExtra(INTENT_SPECIFIC_INFO, specificInfo); ! ...! } Step 3: Manipulate Intent Data
  • @mobileLarson 
 @_openKnowledge Lars Röwekamp | CIO New Technologies Thanks! Questions?
  • Demo