PERFMATTERS for Android - DroidCon Berlin 2016

PERFMATTERS for Android - DroidCon Berlin 2016

The slogan '#PERFMATTERS' by Colt McAnlis is one of the most important statements, which an Android developer should follow. A well performing application makes the difference between a nice looking application and one, which gets successful. You, as an Android developer, have the responsibility to delight your users, so they don't feel how much work your application has to burden to fulfill the user's needs. This session will show you some easy optimizations and how to avoid glitches in the application. A lot of developers would shout, that this is premature optimization. In contrary it is preventive. With a small amount of work while developing you can avoid a lot of pitfalls. You will hopefully have a better understanding of the underlying system afterwards. During the session some tools and frameworks will be presented for helping you to fulfill your mission.

2e100380ab0a1ba03a340932fc6e77ef?s=128

Hasan Hosgel

June 17, 2016
Tweet

Transcript

  1. #DroidconDE www.immobilienscout24.de Hasan Hosgel – Droidcon Berlin 2016 #PERFMATTERS for

    Android
  2. #DroidconDE #PERFMATTERS for Android | Hasan Hosgel Immobilien Scout GmbH

    Location: Berlin Employees: ~ 520 > 850.000 ads per month > 787 M visits in 2014* > 6 M Android downloads *source: comScore Digital Analytix, January 2015, complete IS24 (Portal & Mobile)
  3. #DroidconDE Why we are developers?

  4. #DroidconDE Why we are developers? ESPECIALLY ANDROID

  5. Source: https://www.flickr.com/photos/theinfamousgdub/1765952198

  6. We are smart! Source: https://www.flickr.com/photos/theinfamousgdub/1765952198

  7. Source: http://giphy.com/gifs/apple-looney-tunes-android-4A41Wn7C32RiM

  8. We Love Android! Source: http://giphy.com/gifs/apple-looney-tunes-android-4A41Wn7C32RiM

  9. Source: Photo by Sujaki-F

  10. We have Passion! Source: Photo by Sujaki-F

  11. We have Passion! Source: Photo by Sujaki-F

  12. We have Passion! Source: Photo by Sujaki-F We Love Challenges

  13. Source: https://www.flickr.com/photos/68751915@N05/6793826885

  14. We Love Money Source: https://www.flickr.com/photos/68751915@N05/6793826885

  15. Source: http://agenciabrasil.ebc.com.br/geral/foto/2014-07/cerimonia-de-premiacao-da-copa-do-mundo-no-brasil

  16. We love to delight our users Source: http://agenciabrasil.ebc.com.br/geral/foto/2014-07/cerimonia-de-premiacao-da-copa-do-mundo-no-brasil

  17. #DroidconDE User Expectations

  18. #DroidconDE User Expectations Mobile App Users are impatient Source: https://ssl.www8.hp.com/ww/en/secure/pdf/4aa5-7696enw.pdf

  19. #DroidconDE User Expectations Mobile App Users are impatient •  61%

    app start < 4 s Source: https://ssl.www8.hp.com/ww/en/secure/pdf/4aa5-7696enw.pdf
  20. #DroidconDE User Expectations Mobile App Users are impatient •  61%

    app start < 4 s •  49% app start < 2 s Source: https://ssl.www8.hp.com/ww/en/secure/pdf/4aa5-7696enw.pdf
  21. #DroidconDE User Expectations Mobile App Users are impatient •  61%

    app start < 4 s •  49% app start < 2 s Users are intolerant of issues and are quick to uninstall mobile apps Source: https://ssl.www8.hp.com/ww/en/secure/pdf/4aa5-7696enw.pdf
  22. #DroidconDE User Expectations Mobile App Users are impatient •  61%

    app start < 4 s •  49% app start < 2 s Users are intolerant of issues and are quick to uninstall mobile apps •  80% three attempts or less Source: https://ssl.www8.hp.com/ww/en/secure/pdf/4aa5-7696enw.pdf
  23. #DroidconDE User Expectations Mobile App Users are impatient •  61%

    app start < 4 s •  49% app start < 2 s Users are intolerant of issues and are quick to uninstall mobile apps •  80% three attempts or less •  53% apps with severe issues like crashes, freezes or errors Source: https://ssl.www8.hp.com/ww/en/secure/pdf/4aa5-7696enw.pdf
  24. #DroidconDE User Expectations Mobile App Users are impatient •  61%

    app start < 4 s •  49% app start < 2 s Users are intolerant of issues and are quick to uninstall mobile apps •  80% three attempts or less •  53% apps with severe issues like crashes, freezes or errors •  36% heavy battery usage Source: https://ssl.www8.hp.com/ww/en/secure/pdf/4aa5-7696enw.pdf
  25. #DroidconDE User Expectations Source: https://ssl.www8.hp.com/ww/en/secure/pdf/4aa5-7696enw.pdf

  26. #DroidconDE User Expectations Users blame the app and the company

    who made it Source: https://ssl.www8.hp.com/ww/en/secure/pdf/4aa5-7696enw.pdf
  27. #DroidconDE User Expectations Users blame the app and the company

    who made it •  55% app is responsible for performance issues Source: https://ssl.www8.hp.com/ww/en/secure/pdf/4aa5-7696enw.pdf
  28. #DroidconDE User Expectations Users blame the app and the company

    who made it •  55% app is responsible for performance issues •  37% Stated that app crashes or errors make them think less of a company’s brand Source: https://ssl.www8.hp.com/ww/en/secure/pdf/4aa5-7696enw.pdf
  29. #DroidconDE Performance Impacts

  30. #DroidconDE Performance Impacts 500 ms delay Source: http://www.mobilejoomla.com/media/press/responsive-vs-serverside/Responsive-Design-vs- Server-Side-Solutions-Infographic.jpg

  31. #DroidconDE Performance Impacts 500 ms delay bounce rate -4.7% --

    conversion rate -1.9% Source: http://www.mobilejoomla.com/media/press/responsive-vs-serverside/Responsive-Design-vs- Server-Side-Solutions-Infographic.jpg
  32. #DroidconDE Performance Impacts 500 ms delay bounce rate -4.7% --

    conversion rate -1.9% 1,000 ms delay Source: http://www.mobilejoomla.com/media/press/responsive-vs-serverside/Responsive-Design-vs- Server-Side-Solutions-Infographic.jpg
  33. #DroidconDE Performance Impacts 500 ms delay bounce rate -4.7% --

    conversion rate -1.9% 1,000 ms delay bounce rate -8.3% -- Conversion rate -3.5% Source: http://www.mobilejoomla.com/media/press/responsive-vs-serverside/Responsive-Design-vs- Server-Side-Solutions-Infographic.jpg
  34. #DroidconDE Performance Impacts 500 ms delay bounce rate -4.7% --

    conversion rate -1.9% 1,000 ms delay bounce rate -8.3% -- Conversion rate -3.5% 100ms delay mean for Amazon -1% revenue Source: http://www.mobilejoomla.com/media/press/responsive-vs-serverside/Responsive-Design-vs- Server-Side-Solutions-Infographic.jpg
  35. #DroidconDE Performance Impacts 500 ms delay bounce rate -4.7% --

    conversion rate -1.9% 1,000 ms delay bounce rate -8.3% -- Conversion rate -3.5% 100ms delay mean for Amazon -1% revenue 11% scream at their device Source: http://www.mobilejoomla.com/media/press/responsive-vs-serverside/Responsive-Design-vs- Server-Side-Solutions-Infographic.jpg
  36. #DroidconDE Performance Impacts 500 ms delay bounce rate -4.7% --

    conversion rate -1.9% 1,000 ms delay bounce rate -8.3% -- Conversion rate -3.5% 100ms delay mean for Amazon -1% revenue 11% scream at their device 4 % throw their phones Source: http://www.mobilejoomla.com/media/press/responsive-vs-serverside/Responsive-Design-vs- Server-Side-Solutions-Infographic.jpg https://www.flickr.com/photos/ 9009139@N08/1263954439
  37. None
  38. Source: http://giphy.com/gifs/sad-kawaii-pretty-10XOyCb09KyjLy

  39. #DroidconDE www.immobilienscout24.de Let‘s find some performance improvements

  40. #DroidconDE Possible improvements CPU Memory I/O

  41. #DroidconDE Possible improvements CPU Memory I/O

  42. #DroidconDE Possible improvements CPU Memory I/O •  Keep the work

    of the main thread ! ANR
  43. #DroidconDE Possible improvements CPU Memory I/O •  Keep the work

    of the main thread ! ANR •  Avoid GPU overdrawings • https://www.youtube.com/ watch?v=T52v50r-JfE
  44. #DroidconDE Possible improvements CPU Memory I/O •  Keep the work

    of the main thread ! ANR •  Avoid GPU overdrawings • https://www.youtube.com/ watch?v=T52v50r-JfE •  Avoid nested multi-pass layouts • http://goo.gl/Q5te24
  45. #DroidconDE Possible improvements CPU Memory I/O •  Keep the work

    of the main thread ! ANR •  Avoid GPU overdrawings • https://www.youtube.com/ watch?v=T52v50r-JfE •  Avoid nested multi-pass layouts • http://goo.gl/Q5te24 •  Consider RenderScript for performance critical code
  46. #DroidconDE Possible improvements CPU Memory I/O

  47. #DroidconDE Possible improvements CPU Memory I/O •  Do not allocate

    memory inside onDraw()
  48. #DroidconDE Possible improvements CPU Memory I/O •  Do not allocate

    memory inside onDraw() •  Avoid Leaks
  49. #DroidconDE LeakCanary to the Rescue https://github.com/square/ leakcanary

  50. #DroidconDE Setup LeakCanary debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1' releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1’ build.gradle public class

    DcItApplication extends Application { @Override public void onCreate() { super.onCreate(); LeakCanary.install(this); } } DcItApplication.java
  51. #DroidconDE Use Your Debug Build Normally

  52. #DroidconDE Analyze Memory

  53. #DroidconDE Analyze Heap Dump

  54. #DroidconDE Analyze Heap Dump

  55. #DroidconDE Analyze Heap Dump since AS 1.5

  56. #DroidconDE Analyze Memory

  57. #DroidconDE Possible improvements CPU Memory I/O •  Do not allocate

    memory inside onDraw() •  Avoid Leaks •  Avoid listener ! EventBus
  58. #DroidconDE Possible improvements CPU Memory I/O •  Do not allocate

    memory inside onDraw() •  Avoid Leaks •  Avoid listener ! EventBus •  Use SparseArray* family instead of JDK Collections/ Maps
  59. #DroidconDE Possible improvements CPU Memory I/O •  Do not allocate

    memory inside onDraw() •  Avoid Leaks •  Avoid listener ! EventBus •  Use SparseArray* family instead of JDK Collections/ Maps •  Be aware that enums need more resource, but use them, if it will make sense for you
  60. #DroidconDE Possible improvements CPU Memory I/O

  61. #DroidconDE Possible improvements CPU Memory I/O •  Activate gzip compression

    on incoming and outgoing network traffic or FlatBuffer
  62. #DroidconDE Analyze First Start

  63. #DroidconDE Analyze Second Start

  64. #DroidconDE Analyze Second Start

  65. #DroidconDE Possible improvements CPU Memory I/O •  Activate gzip compression

    on incoming and outgoing network traffic or FlatBuffer •  Cache data on disk (image, http responses) with reasonable TTL
  66. #DroidconDE Finally Second Start

  67. #DroidconDE Possible improvements CPU Memory I/O •  Activate gzip compression

    on incoming and outgoing network traffic or FlatBuffer •  Cache data on disk (image, http responses) with reasonable TTL •  Server side cache headers like ETag & Last-Modified
  68. #DroidconDE Possible improvements CPU Memory I/O •  Activate gzip compression

    on incoming and outgoing network traffic or FlatBuffer •  Cache data on disk (image, http responses) with reasonable TTL •  Server side cache headers like ETag & Last-Modified •  Use JobScheduler API to batch across system or better use push notifications for update information
  69. Source: http://www.flickr.com/photos/16210667@N02/9172895225

  70. #DroidconDE Optimizing ArrayList

  71. #DroidconDE Optimizing ArrayList public List<String> getDcItNames(List<Attendee> attendees) { List<String> names

    = new ArrayList<>(); for (Attendee attendee : attendees) { names.add(attendee.name); } return names; } JAVA
  72. #DroidconDE Optimizing ArrayList public List<String> getDcItNames(List<Attendee> attendees) { List<String> names

    = new ArrayList<>(attendees.size()); for (Attendee attendee : attendees) { names.add(attendee.name); } return names; } JAVA
  73. #DroidconDE Optimizing ArrayList @Override public boolean add(E object) { Object[]

    a = array; int s = size; if (s == a.length) { Object[] newArray = new Object[s + (s < (MIN_CAPACITY_INCREMENT / 2) ? MIN_CAPACITY_INCREMENT : s >> 1)]; System.arraycopy(a, 0, newArray, 0, s); array = a = newArray; } a[s] = object; size = s + 1; modCount++; return true; } JAVA
  74. #DroidconDE Optimizing ArrayList void addDcItSpeaker(ArrayList<String> attendees, List<String> speakers) { for

    (String speaker : speakers) { attendees.add(speaker); } } JAVA
  75. #DroidconDE Optimizing ArrayList void addDcItSpeaker(ArrayList<String> attendees, List<String> speakers) { attendees.ensureCapacity(attendees.size()

    + speakers.size()); for (String speaker : speakers) { attendees.add(speaker); } } JAVA
  76. #DroidconDE Optimizing Bundle

  77. #DroidconDE Optimizing Bundle public static DcItScheduleFragment instance(int startMode) { final

    DcItScheduleFragment fragment = new DcItScheduleFragment(); final Bundle bundle = new Bundle(); bundle.putInt(START_MODE, startMode); fragment.setArguments(bundle); return fragment; } JAVA
  78. #DroidconDE Optimizing Bundle public static DcItScheduleFragment instance(int startMode) { final

    DcItScheduleFragment fragment = new DcItScheduleFragment(); final Bundle bundle = new Bundle(1); bundle.putInt(START_MODE, startMode); fragment.setArguments(bundle); return fragment; } JAVA
  79. #DroidconDE Optimizing StringBuilder

  80. #DroidconDE Optimizing StringBuilder public StringBuilder getDcItNames(List<String> names) { StringBuilder builder

    = new StringBuilder(); for (String name : names) { if (builder.length() > 0) { builder.append(", "); } builder.append(name); } return builder; } JAVA
  81. #DroidconDE Optimizing StringBuilder public StringBuilder getDcItNames(List<String> names) { StringBuilder builder

    = new StringBuilder(names.size() * 13); for (String name : names) { if (builder.length() > 0) { builder.append(", "); } builder.append(name); } return builder; } JAVA
  82. #DroidconDE Optimizing StringBuilder final void append0(char[] chars) { int newCount

    = count + chars.length; if (newCount > value.length) { enlargeBuffer(newCount); } System.arraycopy(chars, 0, value, count, chars.length); count = newCount; } JAVA
  83. #DroidconDE Optimizing StringBuilder public StringBuilder addDcItOrganizer(StringBuilder names, List<String> organizers) {

    names.ensureCapacity(names.length() + organizers.size() * 13); for (String organizer : organizers) { if (names.length() > 0) { names.append(", "); } names.append(organizer); } } JAVA
  84. #DroidconDE Optimizing Loops

  85. #DroidconDE Optimizing Moar – The Loop public StringBuilder addDcItOrganizer(StringBuilder names,

    List<String> organizers) { names.ensureCapacity(names.length() + organizers.size() * 13); for (String organizer : organizers) { if (names.length() > 0) { names.append(", "); } names.append(organizer); } } JAVA
  86. #DroidconDE Optimizing Moar – The Loop public StringBuilder addDcItOrganizer(StringBuilder names,

    List<String> organizers) { names.ensureCapacity(names.length() + organizers.size() * 13); for (int i = 0; i < organizers.size(); i++) { String organizer = organizers.get(i); if (names.length() > 0) { names.append(", "); } names.append(organizer); } } JAVA
  87. #DroidconDE Optimizing Moar – The Loop public StringBuilder addDcItOrganizer(StringBuilder names,

    List<String> organizers) { names.ensureCapacity(names.length() + organizers.size() * 13); for (int i = 0, organizersSize = organizers.size(); i < organizersSize; i++) { String organizer = organizers.get(i); if (names.length() > 0) { names.append(", "); } names.append(organizer); } } JAVA
  88. #DroidconDE Optimizing Moar – The Loop public StringBuilder addDcItOrganizer(StringBuilder names,

    List<String> organizers) { final int size = organizers.size(); names.ensureCapacity(names.length() + size * 13); for (int i = 0; i < size; i++) { String organizer = organizers.get(i); if (names.length() > 0) { names.append(", "); } names.append(organizer); } } JAVA
  89. #DroidconDE Optimizing Moar – The Loop public StringBuilder addDcDeOrganizer(StringBuilder names,

    List<String> organizers) { final int size = organizers.size(); names.ensureCapacity(names.length() + size * 13); for (int i = 0; i < size; i++) { String organizer = organizers.get(i); names.append(organizer); names.append(", "); } names.substring(0, names.length()-1); } JAVA
  90. #DroidconDE Optimizing Method invocations

  91. #DroidconDE Optimizing Much Moar – Method Invocation public void onViewCreated(View

    view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); text.setTextColor(attendee.isOrganizer() ? getResources().getColor(R.color.accent) : getResources().getColor(R.color.black)); avatar.setImageResource(attendee.getAvatar()); welcomeText.setText(getString( R.string.hello_world, attendee.name)); } JAVA
  92. #DroidconDE Optimizing Much Moar – Method Invocation public void onViewCreated(View

    view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); final Resources res = getResources(); text.setTextColor(attendee.isOrganizer() ? res.getColor(R.color.accent) : res.getColor(R.color.black)); avatar.setImageResource(attendee.getAvatar()); welcomeText.setText(getString( R.string.hello_world, attendee.name)); } JAVA
  93. #DroidconDE Optimizing Much Moar – Method Invocation public void onViewCreated(View

    view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); final Resources res = getResources(); text.setTextColor(attendee.isOrganizer() ? res.getColor(R.color.accent) : res.getColor(R.color.black)); avatar.setImageResource(attendee.getAvatar()); welcomeText.setText(res.getString( R.string.hello_world, attendee.name)); } JAVA
  94. #DroidconDE Optimizing Access Methods

  95. #DroidconDE Optimizing Access Methods private Attendee attendee; public void onViewCreated(View

    view, Bundle savedInstanceState) { button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { makeText(getContext(), getWelcomeTextFor(attendee.getName()), LENGTH_SHORT).show(); } }); } private String getWelcomeTextFor(String name) { return getResources().getString(R.string.welcome_text, name); } JAVA
  96. #DroidconDE Finding Access Methods https://github.com/JakeWharton/dex-method-list $ ./build/dex-method-list src/test/resources/one.apk … package.DcItScheduleFragment

    access$000(DcItScheduleFragment ) package.DcItScheduleFragment access$100(DcItScheduleFragment , String) … CL
  97. #DroidconDE Optimizing Access Methods Attendee attendee; public void onViewCreated(View view,

    Bundle savedInstanceState) { button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { makeText(getContext(), getWelcomeTextFor(attendee.getName()), LENGTH_SHORT).show(); } }); } String getWelcomeTextFor(String name) { return getResources().getString(R.string.welcome_text, name); } JAVA
  98. Source: https://www.flickr.com/photos/marcus_t_ward/10715983613

  99. Source: http://www.bhmpics.com/success_kid-wallpapers.html

  100. Source: http://www.flickr.com/photos/21496790@N06/5065834411/

  101. #DroidconDE www.immobilienscout24.de Contact: +HasanHosgel alosdev Thanks for attending! We are

    Hiring! http://www.immobilienscout24.de/jobs https://speakerdeck.com/alosdev/perfmatters-for-android- droidcon-berlin-2016
  102. #DroidconDE

  103. #DroidconDE

  104. #DroidconDE

  105. #DroidconDE

  106. #DroidconDE www.immobilienscout24.de Contact: +HasanHosgel alosdev Thanks for attending! We are

    Hiring! http://www.immobilienscout24.de/jobs https://speakerdeck.com/alosdev/perfmatters-for-android- droidcon-berlin-2016
  107. #DroidconDE Sources •  https://ssl.www8.hp.com/ww/en/secure/pdf/4aa5-7696enw.pdf •  http://www.mobilejoomla.com/media/press/responsive-vs- serverside/Responsive-Design-vs-Server-Side-Solutions- Infographic.jpg •  https://speakerdeck.com/jakewharton/eliminating-code-overhead-

    square-hq-2015 •  http://periplanisi.com/android/2013/11/multi-pass-viewgroup- and-performance/ •  https://slideshare.net/dougsillars/android-app-performance- europe-2015 •  https://www.youtube.com/playlist? list=PLWz5rJ2EKKc9CBxr3BVjPTPoDPLdPIFCE