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

Improving Android Application Performance (Invited Talk at National Taiwan University)

David Wu
December 22, 2011

Improving Android Application Performance (Invited Talk at National Taiwan University)

Improving Android Application Performance (Invited Talk at National Taiwan University)

David Wu

December 22, 2011
Tweet

More Decks by David Wu

Other Decks in Programming

Transcript

  1. Improving Android Application Performance David Wu [email protected] Computer Science and

    Information Engineering, National Taiwan University December 22, 2011 © 2010 HTC Corporation. All rights reserved.
  2. 2

  3. 4

  4. 5

  5. 6

  6. 7

  7. 8 Agenda •  Performance •  Framework APIs •  UI performance

    •  I/O performance •  Scrolling performance •  Memory •  How Android memory management works •  OOM killer and low memory killer •  Memory measurement of an application •  Identifying memory leaks •  Best practices
  8. Frame rates 15 •  To achieve 60 fps, you have

    16ms to handle each frame. •  To achieve 24 fps, you have 41ms to handle each frame. •  Binder RPC call takes ~0.12ms. •  Reading a byte from flash takes ~5-25ms. •  Writing to flash takes ~5-100ms. •  TCP setup + HTTP fetch takes seconds. Numbers taken from http://www.youtube.com/watch?v=c4znvD-7VDA
  9. 19 new Thread(new Runnable() { @Override public void run() {

    // do some heavy work } }).start(); !
  10. 20 new AsyncTask<URL, Integer, Integer>() { protected Long doInBackground(URL... urls)

    { final int count = urls.length; for ( int i = 0; i < count; i++ ) { Downloader.download(url); publishProgress(i); } return count; } protected void onProgressUpdate(Integer... progress) { setProgress(progress[0]); } protected void onPostExecute(Integer result) { showDialog(“Downloaded “ + result + “ files”); } } !
  11. 21 HandlerThread mHandlerThread = new HandlerThread("WorkerThread"); Handler handler = new

    Handler(mHandlerThread.getLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { case JOB_1: // do job #1 break; case JOB_2: // do job #2 break; } } }; !
  12. 24 new AsyncQueryHandler(getContentResolver()) { @Override protected void onQueryComplete(int token, Object

    cookie, Cursor cursor) { if (token == 0) { // get data from cursor } } }.startQuery(0, // token null, // cookie RawContacts.CONTENT_URI, null, // projection RawContacts.CONTACT_ID + "<?", // selection new String[] { "888" }, // selectionArgs RawContacts.DISPLAY_NAME_PRIMARY + " ASC" // orderby ); !
  13. 25 public class WorkerService extends IntentService { public WorkerService() {

    super("WorkerThread"); } @Override protected void onHandleIntent(Intent intent) { String action = intent.getAction(); if ("com.test.DO_JOB_1".equals(action)) { // do job #1 } } } !
  14. 27 Summary for avoiding the UI thread •  Activity or

    Fragment •  AsyncTask •  Handler, HandlerThread •  AsyncTaskLoader •  ContentProvider •  AsyncQueryHandler •  CursorLoader •  Service •  IntentService •  Parcel.writeStrongBinder(IBinder)
  15. 28 Agenda •  Performance •  Framework APIs •  UI performance

    •  I/O performance •  Scrolling performance •  Memory •  How Android memory management works •  OOM killer and low memory killer •  Memory measurement of an application •  Identifying memory leaks •  Best practices
  16. 34

  17. 36

  18. 39

  19. 41 <ViewStub android:id="@+id/stub_import" android:inflatedId="@+id/panel_import" android:layout="@layout/progress_overlay" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" /> !

    ((ViewStub) findViewById(R.id.stub_import)).setVisibility(View.VISIBLE); // or View importPanel = ((ViewStub) findViewById(R.id.stub_import)).inflate();
  20. 43 Summary for improving UI performance •  Use Hierarchy Viewer

    to identify bottlenecks •  Simplify complex layouts with RelativeLayout or GridLayout! •  Reuse layouts with <merge />! •  Defer inflation of rarely used views with <viewstub />! •  Use layoutopt to detect common problems
  21. 44 Agenda •  Performance •  Framework APIs •  UI performance

    •  I/O performance •  Scrolling performance •  Memory •  How Android memory management works •  OOM killer and low memory killer •  Memory measurement of an application •  Identifying memory leaks •  Best practices
  22. 46 public class SharedPreferencesUtils { private static final Method sApplyMethod

    = findApplyMethod(); private static Method findApplyMethod() { try { Class cls = SharedPreferences.Editor.class; return cls.getMethod("apply"); } catch ( NoSuchMethodException unused ) { // fall through } return null; } public static void apply(SharedPreferences.Editor editor) { if ( sApplyMethod != null ) { try { sApplyMethod.invoke(editor); return; } catch ( InvocationTargetException unused ) { // fall through } catch ( IllegalAccessException unused ) { // fall through } } editor.commit(); } } !
  23. 47 Tip #2 Profile your database queries with EXPLAIN QUERY

    PLAN! EXPLAIN QUERY PLAN SELECT * FROM .... !
  24. 51 Tip #5 Precompile frequently used SQL statements String sql

    = “INSERT INTO table VALUES (?, ?)”; SQLiteStatement stmt = mDatabase.compileStatement(sql); DatabaseUtils.bindObjectToProgram(stmt, 1, 1); DatabaseUtils.bindObjectToProgram(stmt, 2, 2); stmt.execute(); stmt.close(); !
  25. 52 Tip #6 Defer automatic requery in ContentObserver.onChange()! getContentResolver().registerContentObserver(uri, true,

    new ContentObserver(new Handler()) { @Override public void onChange(boolean selfChange) { mDirty = true; } }); ! @Override protected void onResume() { super.onResume(); if (mDirty) { // start query again mDirty = false; } } !
  26. adb logcat -b events content_query_sample:I *:S! adb logcat -b events

    content_update_sample:I *:S! ! adb logcat -b events db_sample:I *:S! 55 Tip #9 Debug with event logs!
  27. 68 Summary for improving I/O performance •  Asynchronous write to

    SharedPreferences! •  Profile database queries with EXPLAIN QUERY PLAN and TraceView •  Minimize fillWindow time by reducing projection columns and using the LIMIT clause •  Use index to optimize database queries •  Precompile frequently used SQL statements using prepared statements •  Defer automatic requery in ContentObserver.onChange()! •  Use bulk operations in a transaction •  Yield occasionally in a long transaction •  Debug with event logs
  28. 69 Agenda •  Performance •  Framework APIs •  UI performance

    •  I/O performance •  Scrolling performance •  Memory •  How Android memory management works •  OOM killer and low memory killer •  Memory measurement of an application •  Identifying memory leaks •  Best practices
  29. Avoid unnecessary inflation by reusing convertView! 71 Tip #1 @Override

    public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = mInflater.inflate(R.layout.main, parent, false); } // .... } !
  30. Cache Views to avoid findViewById()! 72 Tip #2 @Override public

    View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = mInflater.inflate(R.layout.main, parent, false); ViewHolder holder = new ViewHolder(); holder.img = (ImageView) convertView.findViewById(R.id.image); holder.txt = (TextView) convertView.findViewById(R.id.text); convertView.setTag(holder); } ViewHolder holder = (ViewHolder) convertView.getTag(); holder.img.setImageResource(R.drawable.icon); holder.txt.setText(R.string.hello); return convertView; } private static class ViewHolder { ImageView img; TextView txt; } !
  31. 75 “A tablet built with Tegra 2 can touch every

    pixel of a 1200x800 screen about 2.5 times at 60fps.” -- Dianne Hackborn https://plus.google.com/105051985738280261832/posts/2FXDCz8x93s
  32. 76 If you have a parent View that will never

    be seen, don’t draw it (and its background).
  33. 77

  34. 79 In most cases you don’t need to draw the

    window background of an Activity getWindow().setBackgroundDrawable(null); ! android:windowBackground="@null" !
  35. If you have to use animations, disable the drawing cache.

    ListView.setDrawableCacheEnabled(false)! 82
  36. Debug with StrictMode enabled 85 Tip #7 public void onCreate()

    { if (DEVELOPER_MODE) { StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() .detectDiskReads() .detectDiskWrites() .detectNetwork() .penaltyLog() .build()); StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() .detectLeakedSqlLiteObjects() .detectLeakedClosableObjects() .penaltyLog() .penaltyDeath() .build()); } super.onCreate(); } !
  37. 87 Summary for improving scrolling performance •  Avoid unnecessary inflation

    by reusing convertView! •  Cache Views to avoid findViewById()! •  Avoid drawing the unnecessary •  Avoid runtime scaling of images •  Avoid animations during a scroll (or disable the drawing cache) •  Use Allocation Tracker to detect and avoid frequent garbage collection •  Debug with StrictMode enabled •  Check main thread Looper for unnecessary activities!
  38. 88 Agenda •  Performance •  Framework APIs •  UI performance

    •  I/O performance •  Scrolling performance •  Memory •  How Android memory management works •  OOM killer and low memory killer •  Memory measurement of an application •  Identifying memory leaks •  Best practices
  39. 90 At the system level, Android runs a modified OOM

    killer using the low memory driver
  40. Low memory thresholds in Android (init.rc) # Define the memory

    thresholds at which the above process classes will! # be killed. These numbers are in pages (4k).! ! setprop ro.FOREGROUND_APP_MEM 2048! setprop ro.VISIBLE_APP_MEM 3072! setprop ro.PERCEPTIBLE_APP_MEM 4096! setprop ro.HEAVY_WEIGHT_APP_MEM 4096! setprop ro.SECONDARY_SERVER_MEM 6144! setprop ro.BACKUP_APP_MEM 6144! setprop ro.HOME_APP_MEM 6144! setprop ro.HIDDEN_APP_MEM 7168! setprop ro.EMPTY_APP_MEM 8192! ! 94
  41. OOM_ADJ based on importance levels (init.rc) # Define the oom_adj

    values for the classes of processes that can be ! # killed by the kernel. These are used in ActivityManagerService. ! ! setprop ro.FOREGROUND_APP_ADJ 0 ! setprop ro.VISIBLE_APP_ADJ 1 ! setprop ro.PERCEPTIBLE_APP_ADJ 2 ! setprop ro.HEAVY_WEIGHT_APP_ADJ 3 ! setprop ro.SECONDARY_SERVER_ADJ 4 ! setprop ro.BACKUP_APP_ADJ 5 ! setprop ro.HOME_APP_ADJ 6 ! setprop ro.HIDDEN_APP_MIN_ADJ 7 ! setprop ro.EMPTY_APP_ADJ 15 ! ! 95
  42. Process importance levels •  Persistent •  OOM_ADJ < 0 • 

    system_server (-16) and com.android.phone (-12) •  Foreground •  FOREGROUND_APP_ADJ = 0 •  Runs a foreground Activity •  Runs a Service executing one of onCreate(), onStartCommand(), onDestroy() •  Hosts a Service that’s bound by a foreground Activity or a foreground process •  Runs a BroadcastReceiver executing onReceive() •  Hosts a ContentProvider which is currently used by persistent or foreground processes 96
  43. Process importance levels •  Visible •  VISIBLE_APP_ADJ = 1 • 

    Runs a visible Activity (not in foreground) •  Runs a Service started with startService() and the Service uses startForeground() to put itself in foreground state •  Service •  SECONDARY_SERVER_ADJ = 4 •  Runs a Service that’s been started with startService() and is not a visible process •  Background •  HIDDEN_APP_MIN_ADJ (7) .. EMPTY_APP_ADJ (15) •  Process that doesn’t hold any active application components http://developer.android.com/guide/topics/fundamentals/processes-and-threads.html 97
  44. 99 Agenda •  Performance •  Framework APIs •  UI performance

    •  I/O performance •  Scrolling performance •  Memory •  How Android memory management works •  OOM killer and low memory killer •  Memory measurement of an application •  Identifying memory leaks •  Best practices
  45. 101 Android defines a heap limit for each application and

    dictates when an OutOfMemoryError will be thrown.
  46. Terminologies •  Heap limit •  Maximum allowed footprint for the

    Dalvik heap •  Heap size •  Current footprint of the Dalvik heap •  Allocated •  Total number of bytes allocated on the Dalvik heap •  Free •  Heap size – Allocated •  % Used •  Allocated / Heap size * 100% •  External allocation (before Honeycomb) •  Bitmap byte[] 102
  47. Heap size limit n  Heap size limits n  G1: 16MB

    n  Droid: 24MB n  Nexus One: 32MB n  Xoom: 48MB n  ActivityManager.getMemoryClass() 103
  48. OOM Conditions •  OOM condition before Honeycomb •  Heap size

    + external allocation + new allocation request >= Heap limit •  OOM condition after Honeycomb •  Heap size + new allocation request >= Heap limit 104
  49. Not representative of the memory usage of a process • 

    Every process forks from zygote, which has 2MB+ initial overhead •  There is more memory allocation at the native level: •  Android applications run on the Dalvik VM, but can use JNI to load native libraries. •  Dalvik level APIs called by an application can use native libraries on application’s behalf. •  If you enable hardware acceleration (default in ICS), you get 8MB overhead to use OpenGL. 105
  50. Ranking processes in order of memory usage 106 adb shell

    procrank -p! PID Vss Rss Pss Uss cmdline! 3156 80272K 80220K 59228K 57624K com.htc.launcher! 1455 94540K 58728K 37488K 36060K system_server! 9000 55224K 55200K 33900K 32412K com.roguso.plurk! 6713 47912K 47880K 27719K 26788K tw.anddev.aplurk! 1624 44804K 44760K 24954K 24200K android.process.acore! 2081 44992K 44960K 23205K 21628K com.htc.android.mail! 1604 41288K 41248K 22393K 21752K com.htc.android.htcime! 1594 40912K 40844K 21588K 20284K com.htc.weatheridlescreen! 1622 39904K 39872K 21297K 20696K com.android.phone! 11961 49420K 44860K 20930K 18652K com.htc.friendstream! 6812 38756K 38696K 18285K 16964K com.htc.bg! 11725 36356K 36324K 14037K 12316K com.tam.ladyplurk! 1746 32560K 32488K 13056K 12336K com.htc.bgp! 1586 51016K 30076K 11382K 10636K com.android.systemui! 11086 30544K 30472K 10450K 9748K com.facebook.katana! 1651 29596K 29556K 10134K 9432K com.google.process.gapps!
  51. Terminologies •  VSS (Virtual Set Size) •  Total count of

    pages that the process can access •  RSS (Resident Set Size) •  Total count of pages in RAM that the process can access •  PSS (Proportional Set Size) •  Total count of pages that a process uses in RAM, where the size of each page is divided by the number of processes sharing it •  USS (Unique Set Size) •  Count of unshared pages that a process can access 107
  52. Listing virtual memory areas of a process 108 adb shell

    procmem -p <pid>! Vss Rss Pss Uss ShCl ShDi PrCl PrDi Name! ------- ------- ------- ------- ------- ------- ------- ------- ! 4K 4K 0K 0K 4K 0K 0K 0K /system/bin/app_process! 4K 4K 0K 0K 4K 0K 0K 0K /system/bin/app_process! 13908K 13908K 11571K 11508K 2400K 0K 11508K 0K [heap]! 0K 0K 0K 0K 0K 0K 0K 0K [heap]! 4K 4K 4K 4K 0K 0K 4K 0K [heap]! 36K 36K 0K 0K 0K 36K 0K 0K /dev/__properties__! 4K 4K 4K 4K 0K 0K 4K 0K /dev/__properties__! 12084K 12084K 8166K 8060K 4024K 0K 8060K 0K /dev/ashmem/dalvik-heap! 0K 0K 0K 0K 0K 0K 0K 0K /dev/ashmem/dalvik-heap! 212K 212K 212K 212K 0K 0K 212K 0K /dev/ashmem/dalvik-bitmap-1! 0K 0K 0K 0K 0K 0K 0K 0K /dev/ashmem/dalvik-bitmap-2! 256K 256K 256K 256K 0K 0K 256K 0K /dev/ashmem/dalvik-card-table! 12K 12K 12K 12K 0K 0K 12K 0K /dev/ashmem/dalvik-card-table! 0K 0K 0K 0K 0K 0K 0K 0K /dev/ashmem/dalvik-LinearAlloc! 2936K 2936K 2336K 2320K 616K 0K 2320K 0K /dev/ashmem/dalvik-LinearAlloc! 0K 0K 0K 0K 0K 0K 0K 0K /dev/ashmem/dalvik-LinearAlloc! 0K 0K 0K 0K 0K 0K 0K 0K /system/framework/core-junit.jar! 0K 0K 0K 0K 0K 0K 0K 0K /system/framework/ext.jar!
  53. Some important virtual memory areas •  /dev/ashmem/dalvik-heap •  Anonymous pages

    allocated for the heap at the Dalvik level •  [heap], [anonymous] •  Anonymous pages allocated via malloc() at the native level •  /system/framework/*.odex (release build) •  /data/dalvik-cache/*.odex (debug build) •  File-backed mmap pages 109
  54. Listing virtual memory areas of a process 110 adb shell

    dumpsys meminfo <pid>! Applications Memory Usage (kB):! Uptime: 89133197 Realtime: 106110266! ! ** MEMINFO in pid 11961 [com.htc.friendstream] **! native dalvik other total limit bitmap nativeBmp! size: 15032 8535 N/A 23567 32768 N/A N/A! allocated: 14565 5697 N/A 20262 N/A 4669 1918! free: 162 2838 N/A 3000 N/A N/A N/A! (Pss): 4105 2550 13952 20607 N/A N/A N/A! (shared dirty): 2440 1928 5532 9900 N/A N/A N/A! (priv dirty): 4044 708 12716 17468 N/A N/A N/A! ! Objects! Views: 0 ViewRoots: 0! AppContexts: 0 Activities: 0! Assets: 7 AssetManagers: 7! Local Binders: 11 Proxy Binders: 15! Death Recipients: 1! OpenSSL Sockets: 0! !
  55. Terminologies •  Private Dirty = USS •  Amount of RAM

    inside the process that cannot be paged to disk and is not shared with any other processes •  The RAM that will become available to the system when the process goes away http://stackoverflow.com/questions/2298208/how-to-discover-memory-usage-of-my-application-in-android 111
  56. 112 Agenda •  Performance •  Framework APIs •  UI performance

    •  I/O performance •  Scrolling performance •  Memory •  How Android memory management works •  OOM killer and low memory killer •  Memory measurement of an application •  Identifying memory leaks •  Best practices
  57. Garbage collection •  Pre-Gingerbread GC: •  Stop-the-world •  Full heap

    collection •  Pause times often > 100ms •  Gingerbread and beyond: •  Concurrent (mostly) •  Partial collections •  Pause times usually < 5ms 113 http://dubroy.com/blog/google-io-memory-management-for-android-apps/
  58. Memory leaks •  GC does not prevent memory leaks • 

    Have a long living reference to an unused object. This prevents the unused object from being garbage collected. •  In Android, the memory leak is often a reference to Context/Activity. 114
  59. Common cases of Context/Activity leakage •  Long-living reference to an

    object that is an instance of a non-static inner class of an Activity 116 http://dubroy.com/blog/google-io-memory-management-for-android-apps/
  60. Common cases of Context/Activity leakage •  Long-living reference to an

    object that is an instance of a non-static inner class of an Activity 117 http://dubroy.com/blog/google-io-memory-management-for-android-apps/
  61. Common cases of Context/Activity leakage •  Long-living Thread that outruns

    the Activity’s lifecycle 118 new Thread(new Runnable() { @Override public void run() { // do some heavy work } }).start(); !
  62. 119 Tip #1 Use logcat to check if there is

    an increase of memory usage over time (look out for step functions!)
  63. 127

  64. •  Use Histogram view to see the number of Activity

    instances. Having more than one instance of an Activity is a strong sign of Activity/Context leak. 130 http://dubroy.com/blog/google-io-memory-management-for-android-apps/
  65. •  The Dominator Tree view ordered by retained size helps

    you identify objects that retains lots of memory and cannot be freed. They are usually good starting points to find memory leaks. 131 http://dubroy.com/blog/google-io-memory-management-for-android-apps/
  66. •  The Dominator Tree view ordered by retained size helps

    you identify objects that retains lots of memory and cannot be freed. They are usually good starting points to find memory leaks. 132 http://dubroy.com/blog/google-io-memory-management-for-android-apps/
  67. •  The Dominator Tree view ordered by retained size helps

    you identify objects that retains lots of memory and cannot be freed. They are usually good starting points to find memory leaks. 133 http://dubroy.com/blog/google-io-memory-management-for-android-apps/
  68. 134 Agenda •  Performance •  Framework APIs •  UI performance

    •  I/O performance •  Scrolling performance •  Memory •  How Android memory management works •  OOM killer and low memory killer •  Memory measurement of an application •  Identifying memory leaks •  Best practices
  69. Best Practices • OutOfMemoryError thrown: •  Identify whether it’s Bitmap or

    Java objects that takes up more of the allocations. •  If it’s Bitmap: •  Down sample your images •  Recycle unused Bitmaps early with Bitmap.recycle()! •  If it’s Java objects: •  Use techniques to avoid fragmentation •  Reduce peak usage of Java heap allocation •  Use SoftReference to implement caches (rather than holding strong reference to entire dataset) •  Use WeakReference to avoid memory leaks 135
  70. Down sampling Bitmap 136 BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inJustDecodeBounds

    = true; BitmapFactory.decodeFile(path, opts); final int originalWidth = opts.outWidth; final int originalHeight = opts.outHeight; final int originalDim = Math.max(originalWidth, originalHeight); opts = new BitmapFactory.Options(); opts.inSampleSize = 1; while ( originalDim > MAX_IMAGE_DIM ) { opts.inSampleSize *= 2; originalDim /= 2; } return BitmapFactory.decodeFile(path, opts); !
  71. WeakReference 137 public class MainActivity extends Activity { static Leaky

    leak = null; static class Leaky { void doSomething() { System.out.println("Wheeee!!!"); } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (leak == null) { leak = new Leaky(); } } } !
  72. WeakReference 138 public class MainActivity extends Activity { static Leaky

    leak = null; static class Leaky { private final Context mContext; public Leaky(Context context) { super(); mContext = context; doSomethingWithOuterInstance(); } void doSomethingWithOuterInstance() { String text = mContext.getString(R.string.hello); System.out.println(text); } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (leak == null) { leak = new Leaky(this); } } } !
  73. WeakReference 139 public class MainActivity extends Activity { static Leaky

    leak = null; static class Leaky { private final WeakReference<Context> mContext; public Leaky(Context context) { super(); mContext = new WeakReference<Context>(context); doSomethingWithOuterInstance(); } void doSomethingWithOuterInstance() { Context context = mContext.get(); if (context != null) { String text = context.getString(R.string.hello); System.out.println(text); } } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (leak == null) { leak = new Leaky(this); } } }
  74. 140 D v d W @w m n d v

    d@w -m n.c m b o .w -m n.c m
  75. 141