Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Android, the life of your app

Android, the life of your app

You can find the slides with speaker notes here : http://bit.ly/lifeofapp

During this talk we live the life of your app on the user's point of view.
The idea is to follow the user experience from the Play Store to the daily use, measure each time its frustration to find ways for us, as developers, to avoid them.

Eyal LEZMY

October 22, 2013
Tweet

More Decks by Eyal LEZMY

Other Decks in Programming

Transcript

  1. Request only what your app requires 1/3 of apps request

    more permissions than they need MINIMISE PERMISSIONS Users should prefer apps requesting the least permissions
  2. MINIMISE PERMISSIONS Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType(Phone.CONTENT_ITEM_TYPE); startActivityForResult(intent, MY_REQUEST_CODE);

    void onActivityResult(int requestCode, int resultCode, Intent data) { if (data != null) { Uri uri = data.getData(); if (uri != null) { Cursor c = getContentResolver().query(uri, new String[] {Contacts.DISPLAY_NAME, Phone.NUMBER}, null, null, null);} } } }
  3. MINIMISE PERMISSIONS Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType(Phone.CONTENT_ITEM_TYPE); startActivityForResult(intent, MY_REQUEST_CODE);

    Start the contact app void onActivityResult(int requestCode, int resultCode, Intent data) { if (data != null) { Uri uri = data.getData(); if (uri != null) { Cursor c = getContentResolver().query(uri, new String[] {Contacts.DISPLAY_NAME, Phone.NUMBER}, null, null, null);} } } }
  4. MINIMISE PERMISSIONS Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType(Phone.CONTENT_ITEM_TYPE); startActivityForResult(intent, MY_REQUEST_CODE);

    Start the contact app Handle the result void onActivityResult(int requestCode, int resultCode, Intent data) { if (data != null) { Uri uri = data.getData(); if (uri != null) { Cursor c = getContentResolver().query(uri, new String[] {Contacts.DISPLAY_NAME, Phone.NUMBER}, null, null, null);} } } }
  5. Need an UUID? Generate your own UUID and use Backup

    API ! MINIMISE PERMISSIONS String id = UUID.randomUUID(). toString();
  6. Need an UUID? Generate your own UUID and use Backup

    API ! MINIMISE PERMISSIONS String id = UUID.randomUUID(). toString(); YES!
  7. Android Backup API · API is available on all Android

    devices. · Manufacturors can implements their own transport and storage for the API · Each device as its own backup data · A new device will take a backup from a device associated with your google account. · IT'S NOT A SYNC API ! MINIMISE PERMISSIONS
  8. THE FIRST LAUNCH db.beginTransaction(); try{ for(int i=0; i<selectedIds.length; i++){ values.put(COLUMN_ID,selectedIds[i]);

    values.put(COLUMN_STARRED,starred); db.insert(TABLE_STARRED,null,values); db.yieldIfContendedSafely(); } db.setTransactionSuccessful(); } finally { db.endTransaction(); } Start transaction
  9. THE FIRST LAUNCH db.beginTransaction(); try{ for(int i=0; i<selectedIds.length; i++){ values.put(COLUMN_ID,selectedIds[i]);

    values.put(COLUMN_STARRED,starred); db.insert(TABLE_STARRED,null,values); db.yieldIfContendedSafely(); } db.setTransactionSuccessful(); } finally { db.endTransaction(); } Start transaction End transaction
  10. THE FIRST LAUNCH db.beginTransaction(); try{ for(int i=0; i<selectedIds.length; i++){ values.put(COLUMN_ID,selectedIds[i]);

    values.put(COLUMN_STARRED,starred); db.insert(TABLE_STARRED,null,values); db.yieldIfContendedSafely(); } db.setTransactionSuccessful(); } finally { db.endTransaction(); } Start transaction End transaction Optimise multi-threaded insertion
  11. openDatabase(dbPath, null, SQLiteDatabase.NO_LOCALIZED_COLLATORS | SQLiteDatabase.CREATE_IF_NECESSARY); · Create your db file

    using SQlite and fill it · Name your primary key columns "_id" · Create the table : "android_metadata" · And insert a single row containing the local if defined (ex: "en_US"), or open your database using : THE FIRST LAUNCH ON THE SERVER
  12. · Grab the zipped database from assets or network ·

    Unzip it to your getDatabaseDir() · Open the database THE FIRST LAUNCH ON THE MOBILE Be careful of the SQLite version !
  13. UI Thread = Events+Draw 16 ms to draw a frame

    (~60 fps) SMOOTHEN YOUR UI Garbage Collector may take 10ms And stop all threads
  14. Flatten the View Hierarchy · Use RelativeLayout instead of LinearLayout

    · Use the <merge/> tag when possible · Use hierarchyviewer to inspect your layouts SMOOTHEN YOUR UI
  15. Avoid overdraws · Do not draw your background several times

    · Use the “GPU Overdraw” tool from Android 4.2 SMOOTHEN YOUR UI
  16. SMOOTHEN YOUR UI Hashmap<Integer, Object> hashmap = new HashMap<Integer, Object>();

    hashmap.put(1789, mRevolution); ... hashmap.get(1789); SparseArray<Object> sparseArray = new SparseArray<Object>(); sparseArray.put(1789, mRevolution); ... sparseArray.get(1789);
  17. SMOOTHEN YOUR UI Hashmap<Integer, Object> hashmap = new HashMap<Integer, Object>();

    hashmap.put(1789, mRevolution); ... hashmap.get(1789); new Integer(1789) SparseArray<Object> sparseArray = new SparseArray<Object>(); sparseArray.put(1789, mRevolution); ... sparseArray.get(1789);
  18. SMOOTHEN YOUR UI Hashmap<Integer, Object> hashmap = new HashMap<Integer, Object>();

    hashmap.put(1789, mRevolution); ... hashmap.get(1789); new Integer(1789) new Integer(1789) SparseArray<Object> sparseArray = new SparseArray<Object>(); sparseArray.put(1789, mRevolution); ... sparseArray.get(1789);
  19. SMOOTHEN YOUR UI Hashmap<Integer, Object> hashmap = new HashMap<Integer, Object>();

    hashmap.put(1789, mRevolution); ... hashmap.get(1789); new Integer(1789) new Integer(1789) Low memory footprint and no more GC! SparseArray<Object> sparseArray = new SparseArray<Object>(); sparseArray.put(1789, mRevolution); ... sparseArray.get(1789);
  20. SMOOTHEN YOUR UI BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); bitmapOptions.inBitmap =

    myOldBitmap; Bitmap scaledBitmap = BitmapFactory.decode...(..., bitmapOptions); BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); bitmapOptions.inSampleSize = sampleSize;
  21. SMOOTHEN YOUR UI BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); bitmapOptions.inBitmap =

    myOldBitmap; Bitmap scaledBitmap = BitmapFactory.decode...(..., bitmapOptions); BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); bitmapOptions.inSampleSize = sampleSize; Define the bitmap to reuse (API level 11)
  22. SMOOTHEN YOUR UI BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); bitmapOptions.inBitmap =

    myOldBitmap; Bitmap scaledBitmap = BitmapFactory.decode...(..., bitmapOptions); BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); bitmapOptions.inSampleSize = sampleSize; Use the option for loading Define the bitmap to reuse (API level 11)
  23. SMOOTHEN YOUR UI BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); bitmapOptions.inBitmap =

    myOldBitmap; Bitmap scaledBitmap = BitmapFactory.decode...(..., bitmapOptions); Define the subsampling level (API Level 1) BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); bitmapOptions.inSampleSize = sampleSize; Use the option for loading Define the bitmap to reuse (API level 11)
  24. Screen: 1st item of consumtion 30 to 70% of the

    battery life PRESERVE THE BATTERY
  25. Basically you don’t need it Only a few kind of

    applications should need to stay the device awake (Reader, Games, …) Think about the context Release wake lock if you have good assumptions that the user is not using your app anymore WakeLocks PRESERVE THE BATTERY
  26. GPS · Permission ACCESS_FINE_LOCATION · Drains a lot of power

    · Works offline Network location · Permission ACCESS_COARSE_LOCATION · Need to be online · Fast · Precise in urban area Geolocation PRESERVE THE BATTERY
  27. Define a strategy · What is the needed precision for

    my app? · Define the measure interval wisely · Consider the GPS fix time Use Fused Location Provider On Google Play Services Geolocation PRESERVE THE BATTERY
  28. Radio drains a lot of power Group data to minimize

    the number of requests Use caching! Network PRESERVE THE BATTERY
  29. PRESERVE THE BATTERY private void enableHttpResponseCache() { try { long

    httpCacheSize = 10 * 1024 * 1024; // 10 MiB File httpCacheDir = new File(getCacheDir(), “http”); Class.forName(“android.net.http.HttpResponseCache”) .getMethod(“install”, File.class, long.class) .invoke(null, httpCacheDir, httpCacheSize); } catch (Exception httpResponseCacheNotAvailable) { Log.d(TAG, “HTTP response cache is unavailable.”); } } Enable cache, if available (API level 13) Or use a backport like HttpResponseCache
  30. Enable GZIP on the server 30 to 50% less trafic

    Use ETAGs Network PRESERVE THE BATTERY