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

PERFMATTERS for Android - DevFest Switzerland 2016

Hasan Hosgel
November 27, 2016

PERFMATTERS for Android - DevFest Switzerland 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.

Hasan Hosgel

November 27, 2016
Tweet

More Decks by Hasan Hosgel

Other Decks in Technology

Transcript

  1. #DevFestCH
    www.immobilienscout24.de
    Hasan Hosgel – DevFest Switzerland 2016
    #PERFMATTERS
    for Android

    View Slide

  2. #DevFestCH #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)

    View Slide

  3. #DevFestCH
    Why we are developers?

    View Slide

  4. #DevFestCH
    Why we are developers?
    ESPECIALLY ANDROID

    View Slide

  5. #DevFestCH
    We are smart!
    Source: http://giphy.com/gifs/computer-nerd-geek-D0EjguuQzYr9m

    View Slide

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

    View Slide

  7. #DevFestCH
    We have Passion!
    Source: http://giphy.com/gifs/love-black-and-white-bw-WN2996KtK4V68

    View Slide

  8. #DevFestCH
    We Love Challenges
    Source: http://memecrunch.com/generator/template/1080659/raging-rambo/

    View Slide

  9. #DevFestCH
    Source: Photo by Sujaki-F
    To Handle These

    View Slide

  10. #DevFestCH
    We Love Money
    Source: http://giphy.com/gifs/m7FO0p9hTc59e

    View Slide

  11. #DevFestCH
    We love to delight our users
    Source: http://giphy.com/gifs/the-simpsons-brake-my-wife-please-d4OFne9zaO9Ne

    View Slide

  12. #DevFestCH
    User Expectations

    View Slide

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

    View Slide

  14. #DevFestCH
    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

    View Slide

  15. #DevFestCH
    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

    View Slide

  16. #DevFestCH
    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

    View Slide

  17. #DevFestCH
    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

    View Slide

  18. #DevFestCH
    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

    View Slide

  19. #DevFestCH
    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

    View Slide

  20. #DevFestCH
    User Expectations
    Source: https://ssl.www8.hp.com/ww/en/secure/pdf/4aa5-7696enw.pdf

    View Slide

  21. #DevFestCH
    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

    View Slide

  22. #DevFestCH
    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

    View Slide

  23. #DevFestCH
    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

    View Slide

  24. #DevFestCH
    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
    http://giphy.com/gifs/reactiongifs-angry-time-
    1306MTkHlXkUZG
    http://giphy.com/gifs/homer-simpson-fat-mumu-
    Sm8Ec3ddRWXa8

    View Slide

  25. View Slide

  26. Source: http://giphy.com/gifs/sad-kawaii-pretty-10XOyCb09KyjLy

    View Slide

  27. #DevFestCH
    www.immobilienscout24.de
    Let‘s find some performance improvements

    View Slide

  28. #DevFestCH
    Possible improvements
    CPU
    Memory
    I/O

    View Slide

  29. #DevFestCH
    Possible improvements
    CPU
    Memory
    I/O

    View Slide

  30. #DevFestCH
    Possible improvements
    CPU
    Memory
    I/O
    • Keep the work of the main
    thread à ANR

    View Slide

  31. #DevFestCH
    Possible improvements
    CPU
    Memory
    I/O
    • Keep the work of the main
    thread à ANR
    • Avoid GPU overdrawings
    • https://www.youtube.com/watc
    h?v=T52v50r-JfE

    View Slide

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

    View Slide

  33. #DevFestCH
    Possible improvements
    CPU
    Memory
    I/O
    • Keep the work of the main
    thread à ANR
    • Avoid GPU overdrawings
    • https://www.youtube.com/watc
    h?v=T52v50r-JfE
    • Avoid nested multi-pass
    layouts
    • http://goo.gl/Q5te24
    • Consider RenderScript for
    performance critical code

    View Slide

  34. #DevFestCH
    Possible improvements
    CPU
    Memory
    I/O

    View Slide

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

    View Slide

  36. #DevFestCH
    Possible improvements
    CPU
    Memory
    I/O
    • Do not allocate memory inside
    onDraw()
    • Avoid Leaks

    View Slide

  37. #DevFestCH
    LeakCanary to the Rescue
    https://github.com/square/leakcan
    ary

    View Slide

  38. #DevFestCH
    Setup LeakCanary
    debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5'
    releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5’
    build.gradle
    public class DfChApplication extends Application {
    @Override
    public void onCreate() {
    super.onCreate();
    LeakCanary.install(this);
    }
    }
    DfChApplication.java

    View Slide

  39. #DevFestCH
    Use Your Debug Build Normally

    View Slide

  40. #DevFestCH
    Analyze Memory

    View Slide

  41. #DevFestCH
    Analyze Heap Dump

    View Slide

  42. #DevFestCH
    Analyze Heap Dump

    View Slide

  43. #DevFestCH
    Analyze Heap Dump since AS 1.5

    View Slide

  44. #DevFestCH
    Analyze Memory

    View Slide

  45. #DevFestCH
    Possible improvements
    CPU
    Memory
    I/O
    • Do not allocate memory inside
    onDraw()
    • Avoid Leaks
    • Avoid listener à EventBus

    View Slide

  46. #DevFestCH
    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

    View Slide

  47. #DevFestCH
    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

    View Slide

  48. #DevFestCH
    Possible improvements
    CPU
    Memory
    I/O

    View Slide

  49. #DevFestCH
    Possible improvements
    CPU
    Memory
    I/O
    • Activate gzip compression on
    incoming and outgoing
    network traffic or FlatBuffer

    View Slide

  50. #DevFestCH
    Analyze First Start

    View Slide

  51. #DevFestCH
    Analyze Second Start

    View Slide

  52. #DevFestCH
    Analyze Second Start

    View Slide

  53. #DevFestCH
    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

    View Slide

  54. #DevFestCH
    Finally Second Start

    View Slide

  55. #DevFestCH
    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

    View Slide

  56. #DevFestCH
    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

    View Slide

  57. Source: http://www.flickr.com/photos/16210667@N02/9172895225

    View Slide

  58. Source: http://giphy.com/gifs/nintendo-internet-reacts-SFmuVYqfnn4re

    View Slide

  59. #DevFestCH
    Optimizing ArrayList

    View Slide

  60. #DevFestCH
    Optimizing ArrayList
    public List getDfChNames(List attendees) {
    List names = new ArrayList<>();
    for (Attendee attendee : attendees) {
    names.add(attendee.name);
    }
    return names;
    }
    JAVA

    View Slide

  61. #DevFestCH
    Optimizing ArrayList
    public List getDfChNames(List attendees) {
    List names = new ArrayList<>(attendees.size());
    for (Attendee attendee : attendees) {
    names.add(attendee.name);
    }
    return names;
    }
    JAVA

    View Slide

  62. #DevFestCH
    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

    View Slide

  63. #DevFestCH
    Optimizing ArrayList
    void addDfChSpeaker(ArrayList attendees, List speakers) {
    for (String speaker : speakers) {
    attendees.add(speaker);
    }
    }
    JAVA

    View Slide

  64. #DevFestCH
    Optimizing ArrayList
    void addDfChSpeaker(ArrayList attendees, List speakers) {
    attendees.ensureCapacity(attendees.size() + speakers.size());
    for (String speaker : speakers) {
    attendees.add(speaker);
    }
    }
    JAVA

    View Slide

  65. #DevFestCH
    Optimizing Bundle

    View Slide

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

    View Slide

  67. #DevFestCH
    Optimizing Bundle
    public static DfChScheduleFragment instance(int startMode) {
    final DfChScheduleFragment fragment = new DfChScheduleFragment();
    final Bundle bundle = new Bundle(1);
    bundle.putInt(START_MODE, startMode);
    fragment.setArguments(bundle);
    return fragment;
    }
    JAVA

    View Slide

  68. #DevFestCH
    Optimizing StringBuilder

    View Slide

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

    View Slide

  70. #DevFestCH
    Optimizing StringBuilder
    public StringBuilder getDfChNames(List names) {
    StringBuilder builder = new StringBuilder(names.size() * 13);
    for (String name : names) {
    if (builder.length() > 0) {
    builder.append(", ");
    }
    builder.append(name);
    }
    return builder;
    }
    JAVA

    View Slide

  71. #DevFestCH
    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

    View Slide

  72. #DevFestCH
    Optimizing StringBuilder
    public StringBuilder addDfChOrganizer(StringBuilder names, List organizers) {
    names.ensureCapacity(names.length() + organizers.size() * 13);
    for (String organizer : organizers) {
    if (names.length() > 0) {
    names.append(", ");
    }
    names.append(organizer);
    }
    }
    JAVA

    View Slide

  73. #DevFestCH
    Optimizing Loops

    View Slide

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

    View Slide

  75. #DevFestCH
    Optimizing Moar – The Loop
    public StringBuilder addDfChOrganizer(StringBuilder names, List 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

    View Slide

  76. #DevFestCH
    Optimizing Moar – The Loop
    public StringBuilder addDfChOrganizer(StringBuilder names, List 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

    View Slide

  77. #DevFestCH
    Optimizing Moar – The Loop
    public StringBuilder addDfChOrganizer(StringBuilder names, List 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

    View Slide

  78. #DevFestCH
    Optimizing Moar – The Loop
    public StringBuilder addDfChOrganizer(StringBuilder names, List 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

    View Slide

  79. #DevFestCH
    Optimizing Method invocations

    View Slide

  80. #DevFestCH
    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

    View Slide

  81. #DevFestCH
    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

    View Slide

  82. #DevFestCH
    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

    View Slide

  83. #DevFestCH
    Optimizing Access Methods

    View Slide

  84. #DevFestCH
    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

    View Slide

  85. #DevFestCH
    Finding Access Methods
    https://github.com/JakeWharton/dex-method-list
    $ ./build/dex-method-list src/test/resources/one.apk

    package.DfChScheduleFragment access$000(DfChScheduleFragment )
    package.DfChScheduleFragment access$100(DfChScheduleFragment , String)

    CL

    View Slide

  86. #DevFestCH
    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

    View Slide

  87. #DevFestCH
    Proguard/ Multidex

    View Slide

  88. #DevFestCH
    Proguard/ Multidex
    64k method limit

    View Slide

  89. #DevFestCH
    Proguard/ Multidex
    64k method limit
    Aggressive Proguard configs

    View Slide

  90. #DevFestCH
    Proguard/ Multidex
    64k method limit
    Aggressive Proguard configs
    Multidex

    View Slide

  91. #DevFestCH
    Proguard/ Multidex
    64k method limit
    Aggressive Proguard configs
    Multidex
    minSdk > 20 è fine

    View Slide

  92. #DevFestCH
    Proguard/ Multidex
    64k method limit
    Aggressive Proguard configs
    Multidex
    minSdk > 20 è fine
    minSdk < 21 è start up penalties

    View Slide

  93. Source: http://giphy.com/gifs/cute-kpop-secret-EXmJKDuBNNR16

    View Slide

  94. Source: http://giphy.com/gifs/description-shown-d23-of402MXFD9Mac

    View Slide

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

    View Slide

  96. #DevFestCH
    www.immobilienscout24.de
    Contact:
    +HasanHosgel
    alosdev
    Thanks for attending!
    We are Hiring! http://www.immobilienscout24.de/jobs
    https://speakerdeck.com/alosdev/perfmatters-for-android-devfest-
    switzerland-2016

    View Slide

  97. #DevFestCH

    View Slide

  98. #DevFestCH

    View Slide

  99. #DevFestCH

    View Slide

  100. #DevFestCH

    View Slide

  101. #DevFestCH
    www.immobilienscout24.de
    Contact:
    +HasanHosgel
    alosdev
    Thanks for attending!
    We are Hiring! http://www.immobilienscout24.de/jobs
    https://speakerdeck.com/alosdev/perfmatters-for-android-devfest-
    switzerland-2016

    View Slide

  102. #DevFestCH
    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=PLWz5rJ2EKKc9CBxr3BVjPT
    PoDPLdPIFCE

    View Slide