Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Performance tools Droidcon Eastern Europe

759 views

Published on

Slides for the "Performance Tools" presentation for Droidcon Bucharest. It is a talk about Traceview, Systrace and Battery Historian, with some practical, or better yet impractical examples.

Description:
The Android ecosystem has a lot of tools to help us out when it comes to development. We all use Android Studio, gradle and adb, for instance. We need to, as these are essential tools for building our products. But what about the tools that help us diagnose problems in our apps? This session is all about asking 2 very common questions:

Why is my app stuttering or dropping below 60fps?

Why are my users telling me that my app drains their batteries?

Most of the times solving a problem means asking the right questions in order to find the right answers. So starting from these 2 questions, with concrete problem-code examples we will be using tools from Google to find our answers.

I don't want it to be a secret, so I'm telling you that the tools we will be using the most will be Traceview and Battery Historian. We will also be looking at Profiling GPU Rendering and Debugging Overdraw.

I know that these tools might look a bit scary or dull but I promise to keep it simple so that you walk away with information you can use at work the next day.

Published in: Mobile
  • Be the first to comment

Performance tools Droidcon Eastern Europe

  1. 1. Performance Tools Andrei Diaconu
  2. 2. Andrei Diaconu
  3. 3. Andrei Diaconu
  4. 4. Andrei Diaconu
  5. 5. Andrei Diaconu • Cofounder of Android Iași
  6. 6. Andrei Diaconu • Cofounder of Android Iași • 5 years of Android
  7. 7. Andrei Diaconu • Cofounder of Android Iași • 5 years of Android • Trainer & Speaker
  8. 8. Why is my app dropping below 60fps?
  9. 9. Why is my app dropping below 60fps? Why is my app draining the battery?
  10. 10. Why is my app dropping below 60fps?
  11. 11. Why is my app dropping below 60fps?
  12. 12. Why is my app dropping below 60fps? • Simple list
  13. 13. Why is my app dropping below 60fps? • Simple list • Items have some text
  14. 14. Why is my app dropping below 60fps? • Simple list • Items have some text
  15. 15. Why is my app dropping below 60fps? • Simple list • Items have some text • Scroll is not smooth
  16. 16. • Scroll is not smooth
  17. 17. • Scroll is not smooth Why?
  18. 18. • Scroll is not smooth Why? Can we make sure?
  19. 19. • Scroll is not smooth Why? Can we make sure?
  20. 20. Can we make sure?
  21. 21. Can we make sure?
  22. 22. Can we make sure?
  23. 23. Can we make sure?
  24. 24. Frames
  25. 25. Frames Time
  26. 26. Frames 16ms line Time
  27. 27. Frames 16ms line Time Choreographer: Skipped 34 frames! The application may be doing too much work on its main thread.
  28. 28. Traceview
  29. 29. Traceview • Measure how long each method takes to execute
  30. 30. Traceview • Measure how long each method takes to execute • Analyse measurements using Traceview
  31. 31. Traceview • Measure how long each method takes to execute
  32. 32. Traceview //Start in OnResume
 Debug.startMethodTracing("bad_list"); • Measure how long each method takes to execute
  33. 33. Traceview //Start in OnResume
 Debug.startMethodTracing("bad_list"); //Stop in OnPause
 Debug.stopMethodTracing(); • Measure how long each method takes to execute
  34. 34. Traceview //Start in OnResume
 Debug.startMethodTracing("bad_list"); //Stop in OnPause
 Debug.stopMethodTracing(); //Grab using adb
 adb pull • Measure how long each method takes to execute
  35. 35. Traceview //Start in OnResume
 Debug.startMethodTracing("bad_list"); //Stop in OnPause
 Debug.stopMethodTracing(); //Grab using adb
 adb pull • Measure how long each method takes to execute //Grab using adb
 adb pull /sdcard/bad_list.trace bad_list.trace
  36. 36. Traceview //Start in OnResume
 Debug.startMethodTracing("bad_list"); //Stop in OnPause
 Debug.stopMethodTracing(); //Grab using adb
 adb pull • Measure how long each method takes to execute //Needs permission WRITE_EXTERNAL_STORAGE //Grab using adb
 adb pull /sdcard/bad_list.trace bad_list.trace
  37. 37. Traceview //Start in OnResume
 Debug.startMethodTracing("bad_list"); //Stop in OnPause
 Debug.stopMethodTracing(); //Grab using adb
 adb pull • Measure how long each method takes to execute //Needs permission WRITE_EXTERNAL_STORAGE //Grab using adb
 adb pull /sdcard/bad_list.trace bad_list.trace
  38. 38. Traceview • Measure how long each method takes to execute
  39. 39. Traceview • Measure how long each method takes to execute
  40. 40. Traceview • Measure how long each method takes to execute • Analyse measurements using Traceview• Analyse measurements using Traceview
  41. 41. Traceview • Measure how long each method takes to execute • Analyse measurements using Traceview • Analyse measurements using Traceview
  42. 42. Traceview • Measure how long each method takes to execute • Analyse measurements using Traceview • Analyse measurements using Traceview
  43. 43. Traceview • Measure how long each method takes to execute • Analyse measurements using Traceview • Analyse measurements using Traceview
  44. 44. ~ 2 seconds
  45. 45. ~ 200 ms
  46. 46. Adaptor -> getView() ~ 200 ms
  47. 47. inflate()
  48. 48. inflate() 4 x Html.fromHtml()
  49. 49. @Override
 public View getView(int position, View convertView, ViewGroup parent) { 
 View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_jumpy_list, parent, false);
 
 TextView text1 = (TextView) view.findViewById(R.id.item_text1);
 TextView text2 = (TextView) view.findViewById(R.id.item_text2);
 TextView text3 = (TextView) view.findViewById(R.id.item_text3);
 TextView text4 = (TextView) view.findViewById(R.id.item_text4);
 
 String string1 = parent.getContext().getString(R.string.item_number_x, position);
 String string2 = parent.getContext().getString(R.string.item_description);
 String string3 = parent.getContext().getString(R.string.item_details);
 String string4 = parent.getContext().getString(R.string.item_related);
 
 text1.setText(Html.fromHtml(string1));
 text2.setText(Html.fromHtml(string2));
 text3.setText(Html.fromHtml(string3));
 text4.setText(Html.fromHtml(string4)); 
 return view;
 }
  50. 50. @Override
 public View getView(int position, View convertView, ViewGroup parent) { 
 View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_jumpy_list, parent, false);
 
 TextView text1 = (TextView) view.findViewById(R.id.item_text1);
 TextView text2 = (TextView) view.findViewById(R.id.item_text2);
 TextView text3 = (TextView) view.findViewById(R.id.item_text3);
 TextView text4 = (TextView) view.findViewById(R.id.item_text4);
 
 String string1 = parent.getContext().getString(R.string.item_number_x, position);
 String string2 = parent.getContext().getString(R.string.item_description);
 String string3 = parent.getContext().getString(R.string.item_details);
 String string4 = parent.getContext().getString(R.string.item_related);
 
 text1.setText(Html.fromHtml(string1));
 text2.setText(Html.fromHtml(string2));
 text3.setText(Html.fromHtml(string3));
 text4.setText(Html.fromHtml(string4)); 
 return view;
 }
  51. 51. @Override
 public View getView(int position, View convertView, ViewGroup parent) { 
 View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_jumpy_list, parent, false);
 
 TextView text1 = (TextView) view.findViewById(R.id.item_text1);
 TextView text2 = (TextView) view.findViewById(R.id.item_text2);
 TextView text3 = (TextView) view.findViewById(R.id.item_text3);
 TextView text4 = (TextView) view.findViewById(R.id.item_text4);
 
 String string1 = parent.getContext().getString(R.string.item_number_x, position);
 String string2 = parent.getContext().getString(R.string.item_description);
 String string3 = parent.getContext().getString(R.string.item_details);
 String string4 = parent.getContext().getString(R.string.item_related);
 
 text1.setText(Html.fromHtml(string1));
 text2.setText(Html.fromHtml(string2));
 text3.setText(Html.fromHtml(string3));
 text4.setText(Html.fromHtml(string4)); 
 return view;
 }
  52. 52. @Override
 public View getView(int position, View convertView, ViewGroup parent) { 
 View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_jumpy_list, parent, false);
 
 TextView text1 = (TextView) view.findViewById(R.id.item_text1);
 TextView text2 = (TextView) view.findViewById(R.id.item_text2);
 TextView text3 = (TextView) view.findViewById(R.id.item_text3);
 TextView text4 = (TextView) view.findViewById(R.id.item_text4);
 
 String string1 = parent.getContext().getString(R.string.item_number_x, position);
 String string2 = parent.getContext().getString(R.string.item_description);
 String string3 = parent.getContext().getString(R.string.item_details);
 String string4 = parent.getContext().getString(R.string.item_related);
 
 text1.setText(Html.fromHtml(string1));
 text2.setText(Html.fromHtml(string2));
 text3.setText(Html.fromHtml(string3));
 text4.setText(Html.fromHtml(string4)); 
 return view;
 }
  53. 53. @Override
 public View getView(int position, View convertView, ViewGroup parent) { 
 No recycling
 
 No View Holder
 
 
 Html.fromHtml is slow 
 return view;
 }
  54. 54. But 200ms? Isn't this a bit much?
  55. 55. But 200ms? Isn't this a bit much? Method tracking has a big impact itself
  56. 56. Systrace measure at kernel level
  57. 57. Systrace
  58. 58. Systrace
  59. 59. Systrace
  60. 60. Systrace
  61. 61. Systrace It will run for 5 seconds Hit Ok and scroll the list
  62. 62. Alert: Inflation during ListView recycling Time spent: 103.545 ms ListView items inflated: 20
  63. 63. @Override
 public View getView(int position, View convertView, ViewGroup parent) {
 
 View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_jumpy_list, parent, false);
 
 
 
 TextView text1 = (TextView) view.findViewById(R.id.item_text1);
 TextView text2 = (TextView) view.findViewById(R.id.item_text2);
 TextView text3 = (TextView) view.findViewById(R.id.item_text3);
 TextView text4 = (TextView) view.findViewById(R.id.item_text4);
 
 
 String string1 = parent.getContext().getString(R.string.item_number_x, position);
 String string2 = parent.getContext().getString(R.string.item_description);
 String string3 = parent.getContext().getString(R.string.item_details);
 String string4 = parent.getContext().getString(R.string.item_related);
 
 
 text1.setText(Html.fromHtml(string1));
 text2.setText(Html.fromHtml(string2));
 text3.setText(Html.fromHtml(string3));
 text4.setText(Html.fromHtml(string4));
 
 
 return view;
 }
  64. 64. @Override
 public View getView(int position, View convertView, ViewGroup parent) {
 Trace.beginSection("inflating");
 View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_jumpy_list, parent, false);
 Trace.endSection();
 
 Trace.beginSection("noViewHolder");
 TextView text1 = (TextView) view.findViewById(R.id.item_text1);
 TextView text2 = (TextView) view.findViewById(R.id.item_text2);
 TextView text3 = (TextView) view.findViewById(R.id.item_text3);
 TextView text4 = (TextView) view.findViewById(R.id.item_text4);
 Trace.endSection();
 
 String string1 = parent.getContext().getString(R.string.item_number_x, position);
 String string2 = parent.getContext().getString(R.string.item_description);
 String string3 = parent.getContext().getString(R.string.item_details);
 String string4 = parent.getContext().getString(R.string.item_related);
 
 Trace.beginSection("fromHtml");
 text1.setText(Html.fromHtml(string1));
 text2.setText(Html.fromHtml(string2));
 text3.setText(Html.fromHtml(string3));
 text4.setText(Html.fromHtml(string4));
 Trace.endSection();
 
 return view;
 }
  65. 65. inflating
  66. 66. inflating noViewHolder
  67. 67. inflating noViewHolder fromHtml
  68. 68. • obtainView = ~5ms
  69. 69. • obtainView = ~5ms • doFrame = ~20 * obtainView
  70. 70. • obtainView = ~5ms • doFrame = ~20 * obtainView • each frame = ~100ms
  71. 71. • obtainView = ~5ms • doFrame = ~20 * obtainView • each frame = ~100ms Alert: Inflation during ListView recycling Time spent: 103.545 ms ListView items inflated: 20
  72. 72. Why is my app dropping below 60fps?
  73. 73. Why is my app dropping below 60fps? Why is my app draining the battery?
  74. 74. Why is my app draining the battery?
  75. 75. Why is my app draining the battery? What is a wake lock?
  76. 76. Why is my app draining the battery?
  77. 77. Why is my app draining the battery? Battery Historian 2.0
  78. 78. Why is my app draining the battery? Battery Historian 2.0
  79. 79. Why is my app draining the battery? Battery Historian 2.0 adb bugreport > bugreport.txt
  80. 80. Why is my app draining the battery? Battery Historian 2.0 adb bugreport > bugreport.txt go run battery-historian.go
  81. 81. Why is my app draining the battery? Battery Historian 2.0 adb bugreport > bugreport.txt go run battery-historian.go open http://127.0.0.1:9999
  82. 82. public static class UselessWorker {
 Runnable uselessWorker = new Runnable() {
 @Override
 public void run() {
 PowerManager pm = (PowerManager) BadApplication.instance
 .getSystemService(Context.POWER_SERVICE);
 PowerManager.WakeLock wakeLock = 
 pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "cool wakelock tag");
 wakeLock.acquire();
 
 //Some useless, but hard work
 
 wakeLock.release();
 try {
 Thread.sleep(45 * 1000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 } finally {
 work();
 }
 }
 };
 
 private void work() {
 new Thread(uselessWorker, "cool thread name").start();
 }
 } public static class UselessWorker {
 Runnable uselessWorker = new Runnable() {
 @Override
 public void run() {
 PowerManager pm = (PowerManager) BadApplication.instance
 .getSystemService(Context.POWER_SERVICE);
 PowerManager.WakeLock wakeLock = 
 pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "cool wakelock tag");
 wakeLock.acquire();
 
 //Some useless, but hard work
 
 wakeLock.release();
 try {
 Thread.sleep(45 * 1000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 } finally {
 work();
 }
 }
 };
 
 private void work() {
 new Thread(uselessWorker, "cool thread name").start();
 }
 }
  83. 83. public static class UselessWorker {
 Runnable uselessWorker = new Runnable() {
 @Override
 public void run() {
 PowerManager pm = (PowerManager) BadApplication.instance
 .getSystemService(Context.POWER_SERVICE);
 PowerManager.WakeLock wakeLock = 
 pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "cool wakelock tag");
 wakeLock.acquire();
 
 //Some useless, but hard work
 
 wakeLock.release();
 try {
 Thread.sleep(45 * 1000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 } finally {
 work();
 }
 }
 };
 
 private void work() {
 new Thread(uselessWorker, "cool thread name").start();
 }
 } public static class UselessWorker {
 Runnable uselessWorker = new Runnable() {
 @Override
 public void run() {
 PowerManager pm = (PowerManager) BadApplication.instance
 .getSystemService(Context.POWER_SERVICE);
 PowerManager.WakeLock wakeLock = 
 pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "cool wakelock tag");
 wakeLock.acquire();
 
 //Some useless, but hard work
 
 wakeLock.release();
 try {
 Thread.sleep(45 * 1000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 } finally {
 work();
 }
 }
 };
 
 private void work() {
 new Thread(uselessWorker, "cool thread name").start();
 }
 }
  84. 84. public static class UselessWorker {
 Runnable uselessWorker = new Runnable() {
 @Override
 public void run() {
 PowerManager pm = (PowerManager) BadApplication.instance
 .getSystemService(Context.POWER_SERVICE);
 PowerManager.WakeLock wakeLock = 
 pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "cool wakelock tag");
 wakeLock.acquire();
 
 //Some useless, but hard work
 
 wakeLock.release();
 try {
 Thread.sleep(45 * 1000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 } finally {
 work();
 }
 }
 };
 
 private void work() {
 new Thread(uselessWorker, "cool thread name").start();
 }
 } public static class UselessWorker {
 Runnable uselessWorker = new Runnable() {
 @Override
 public void run() {
 PowerManager pm = (PowerManager) BadApplication.instance
 .getSystemService(Context.POWER_SERVICE);
 PowerManager.WakeLock wakeLock = 
 pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "cool wakelock tag");
 wakeLock.acquire();
 
 //Some useless, but hard work
 
 wakeLock.release();
 try {
 Thread.sleep(45 * 1000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 } finally {
 work();
 }
 }
 };
 
 private void work() {
 new Thread(uselessWorker, "cool thread name").start();
 }
 }
  85. 85. public static class UselessWorker {
 Runnable uselessWorker = new Runnable() {
 @Override
 public void run() {
 PowerManager pm = (PowerManager) BadApplication.instance
 .getSystemService(Context.POWER_SERVICE);
 PowerManager.WakeLock wakeLock = 
 pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "cool wakelock tag");
 wakeLock.acquire();
 
 //Some useless, but hard work
 
 wakeLock.release();
 try {
 Thread.sleep(45 * 1000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 } finally {
 work();
 }
 }
 };
 
 private void work() {
 new Thread(uselessWorker, "cool thread name").start();
 }
 } public static class UselessWorker {
 Runnable uselessWorker = new Runnable() {
 @Override
 public void run() {
 PowerManager pm = (PowerManager) BadApplication.instance
 .getSystemService(Context.POWER_SERVICE);
 PowerManager.WakeLock wakeLock = 
 pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "cool wakelock tag");
 wakeLock.acquire();
 
 //Some useless, but hard work
 
 wakeLock.release();
 try {
 Thread.sleep(45 * 1000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 } finally {
 work();
 }
 }
 };
 
 private void work() {
 new Thread(uselessWorker, "cool thread name").start();
 }
 }
  86. 86. ? ? Questions http://j.mp/fps60 http://j.mp/androidiasi

×