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

PERFMATTERS for Android - DroidCon VIenna 2016

Hasan Hosgel
September 12, 2016

PERFMATTERS for Android - DroidCon VIenna 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

September 12, 2016
Tweet

More Decks by Hasan Hosgel

Other Decks in Technology

Transcript

  1. #DroidconAT
    www.immobilienscout24.de
    Hasan Hosgel – Droidcon Vienna 2016
    #PERFMATTERS
    for Android

    View Slide

  2. #DroidconAT #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. #DroidconAT
    Why we are developers?

    View Slide

  4. #DroidconAT
    Why we are developers?
    ESPECIALLY ANDROID

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  9. Source: Photo by Sujaki-F

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  17. #DroidconAT
    User Expectations

    View Slide

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

    View Slide

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

  20. #DroidconAT
    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

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

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

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

  24. #DroidconAT
    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

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

    View Slide

  26. #DroidconAT
    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

  27. #DroidconAT
    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

  28. #DroidconAT
    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

  29. #DroidconAT
    Performance Impacts

    View Slide

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

    View Slide

  31. #DroidconAT
    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

    View Slide

  32. #DroidconAT
    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

    View Slide

  33. #DroidconAT
    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

    View Slide

  34. #DroidconAT
    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

    View Slide

  35. #DroidconAT
    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

    View Slide

  36. #DroidconAT
    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/1263954
    439

    View Slide

  37. View Slide

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

    View Slide

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

    View Slide

  40. #DroidconAT
    Possible improvements
    CPU
    Memory
    I/O

    View Slide

  41. #DroidconAT
    Possible improvements
    CPU
    Memory
    I/O

    View Slide

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

    View Slide

  43. #DroidconAT
    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

  44. #DroidconAT
    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

  45. #DroidconAT
    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

  46. #DroidconAT
    Possible improvements
    CPU
    Memory
    I/O

    View Slide

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

    View Slide

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

    View Slide

  49. #DroidconAT
    LeakCanary to the Rescue
    https://github.com/square/leakcan
    ary

    View Slide

  50. #DroidconAT
    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 DcAtApplication extends Application {
    @Override
    public void onCreate() {
    super.onCreate();
    LeakCanary.install(this);
    }
    }
    DcAtApplication.java

    View Slide

  51. #DroidconAT
    Use Your Debug Build Normally

    View Slide

  52. #DroidconAT
    Analyze Memory

    View Slide

  53. #DroidconAT
    Analyze Heap Dump

    View Slide

  54. #DroidconAT
    Analyze Heap Dump

    View Slide

  55. #DroidconAT
    Analyze Heap Dump since AS 1.5

    View Slide

  56. #DroidconAT
    Analyze Memory

    View Slide

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

    View Slide

  58. #DroidconAT
    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

  59. #DroidconAT
    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

  60. #DroidconAT
    Possible improvements
    CPU
    Memory
    I/O

    View Slide

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

    View Slide

  62. #DroidconAT
    Analyze First Start

    View Slide

  63. #DroidconAT
    Analyze Second Start

    View Slide

  64. #DroidconAT
    Analyze Second Start

    View Slide

  65. #DroidconAT
    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

  66. #DroidconAT
    Finally Second Start

    View Slide

  67. #DroidconAT
    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

  68. #DroidconAT
    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

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

    View Slide

  70. #DroidconAT
    Optimizing ArrayList

    View Slide

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

    View Slide

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

    View Slide

  73. #DroidconAT
    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

  74. #DroidconAT
    Optimizing ArrayList
    void addDcAtSpeaker(ArrayList attendees, List speakers) {
    for (String speaker : speakers) {
    attendees.add(speaker);
    }
    }
    JAVA

    View Slide

  75. #DroidconAT
    Optimizing ArrayList
    void addDcAtSpeaker(ArrayList attendees, List speakers) {
    attendees.ensureCapacity(attendees.size() + speakers.size());
    for (String speaker : speakers) {
    attendees.add(speaker);
    }
    }
    JAVA

    View Slide

  76. #DroidconAT
    Optimizing Bundle

    View Slide

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

    View Slide

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

    View Slide

  79. #DroidconAT
    Optimizing StringBuilder

    View Slide

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

    View Slide

  81. #DroidconAT
    Optimizing StringBuilder
    public StringBuilder getDcAtNames(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

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

  83. #DroidconAT
    Optimizing StringBuilder
    public StringBuilder addDcAtOrganizer(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

  84. #DroidconAT
    Optimizing Loops

    View Slide

  85. #DroidconAT
    Optimizing Moar – The Loop
    public StringBuilder addDcAtOrganizer(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

  86. #DroidconAT
    Optimizing Moar – The Loop
    public StringBuilder addDcAtOrganizer(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

  87. #DroidconAT
    Optimizing Moar – The Loop
    public StringBuilder addDcAtOrganizer(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

  88. #DroidconAT
    Optimizing Moar – The Loop
    public StringBuilder addDcAtOrganizer(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

  89. #DroidconAT
    Optimizing Moar – The Loop
    public StringBuilder addDcAtOrganizer(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

  90. #DroidconAT
    Optimizing Method invocations

    View Slide

  91. #DroidconAT
    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

  92. #DroidconAT
    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

  93. #DroidconAT
    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

  94. #DroidconAT
    Optimizing Access Methods

    View Slide

  95. #DroidconAT
    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

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

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

    CL

    View Slide

  97. #DroidconAT
    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

  98. Source: https://www.flickr.com/photos/marcus_t_ward/10715983613

    View Slide

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

    View Slide

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

    View Slide

  101. #DroidconAT
    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

    View Slide

  102. #DroidconAT

    View Slide

  103. #DroidconAT

    View Slide

  104. #DroidconAT

    View Slide

  105. #DroidconAT

    View Slide

  106. #DroidconAT
    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

    View Slide

  107. #DroidconAT
    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