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

Engineering Android apps for Performance & Quality

Paresh Mayani
December 27, 2015

Engineering Android apps for Performance & Quality

Delivered this talk on android best practices/do's/don'ts in GDG DevFest Ahmedabad 2015 event.

P.S. There are many best practices you can follow for improving your application's quality and performance, but these are the do's, don'ts and best practices I have noted/observed by reviewing applications of start-ups, stackoverflow Q/As, company projects, etc.

Paresh Mayani

December 27, 2015
Tweet

More Decks by Paresh Mayani

Other Decks in Technology

Transcript

  1. Outline - Scalability - Maintainable - Easily adoptable - Proper

    architecture - App testing Performance Quality - Optimizing app performance using tools - Removing unused resources - Using XML drawables & Vectors - Compressing and Caching data - Loading and Syncing images - Proper API design
  2. Optimizing layout hierarchies Each widget and layout you add to

    your application requires initialization, layout, and drawing. Avoid deep hierarchy of views How to optimize layout? • Inspect your layout • Revise your layout Use Hierarchy Viewer
  3. Reusable layouts Define a common layout and reuse it with

    <include/> It’s powerful as it allows you create reusable complex layouts. For example, <include layout=”@layout/toolbar”/>
  4. Compound drawables Lint warning => “This tag and its children

    can be replaced by one <TextView/> and a compound drawable” A LinearLayout which contains an ImageView and a TextView can be more efficiently handled as a compound drawable. TextView supports 4 compound drawables • drawableLeft • drawableRight • drawableTop • drawableBottom
  5. <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" > <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:contentDescription="@string/text" android:src="@drawable/ic_launcher"

    /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/text" /> </LinearLayout> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:drawableLeft="@drawable/ic_launcher" android:text="@string/text" /> Bad practice of designing layout Compound drawable
  6. Optimizing and lowering GPU Overdraw Debug GPU Overdraw using the

    developer options http://developer.android. com/tools/performance/debug-gpu-overdraw/index. html
  7. Use UI thread for only UI It should not happen

    on main UI thread: • Networking (request/response) • Images (loading images and syncing) • Querying large set of data from Database Then what should I do? • Use loaders for the database access • Use Retrofit, OkHttp for networking
  8. Removing unused resources The Gradle build system for Android supports

    "resource shrinking": the automatic removal of resources that are unused, at build time, in the packaged app. Also removes resources from libraries you are depending on if they are not actually needed by your application
  9. android { ... buildTypes { release { minifyEnabled true shrinkResources

    true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } }
  10. android { ... buildTypes { release { minifyEnabled true shrinkResources

    true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } To turn on code shrinking To turn on resource shrinking
  11. > gradlew clean assembleDebug --info > output_file.txt :app:shrinkDebugResources (Thread[Task worker

    Thread 2,5,main]) started. :app:shrinkDebugResources Skipped unused resource res/anim/abc_fade_in.xml: 396 bytes Skipped unused resource res/mipmap-xhdpi-v4/ic_launcher.png: 5233 bytes Removed unused resources: Binary resource data reduced from 4010KB to 2906KB: Removed 27% Note: If necessary, you can disable resource shrinking by adding android { buildTypes { debug { shrinkResources false }
  12. Warning: Removing unused resources Warning: 1. tools:keep 2. tools:discard 3.

    tools:shrinkMode tools:keep when using shrinkMode="strict", you can specify specific resources to keep (typically because they are referenced in an indirect way via runtime code, such as Resources#getIdentifier(some dynamically computed resource name). tools:discard When using resource shrinking to automatically strip out unused resources, you can specify specific resources to discard (typically because the resource is referenced in some way that you know does not affect your app tools:shrinkMode When using resource shrinking to automatically strip out unused resources, you can specify whether the Gradle plugin should play it safe and whitelist all resources that might be referenced via a getIdentifier lookup from string resources
  13. Removing unused resources Tip: Removing unused resources that lint identifies

    is still useful because removing them: • Speeds up your build • Reduces your project's maintenance burden.
  14. Caching Data Effective cache controls will dramatically reduce server load

    Cache data as much as possible, why? - Less number of web requests => No use of network radio => Improved battery life - Important: It will make user busy in reading something while loading new set of data
  15. private void fetchGitHubContributors() { App.getInstance().getGithub().getContributors(Const.GITHUB_ORGA, Const.GITHUB_REPO, new Callback<ContributorList>() { @Override

    public void success(final ContributorList contributors, retrofit.client.Response response) { mAdapter.addAll(contributors); App.getInstance().getModelCache().putAsync(Const.CACHE_KEY_FRISBEE_CONTRIBUTORS, contributors, DateTime.now().plusDays(1), null); } }); } For an example, check ModelCache of GDG Frisbee app: https://goo.gl/HHKfMI
  16. Image optimization Do you fetch images from the server every

    time? Cache decoded images - One of the best way to display images quickly There are some libraries available: Picasso, Glide, Fresco Scale down bitmaps Use Nine Patch image Try to create drawables via XML if possible Use Vector images
  17. Load image(s) priority wise Note: Not a performance tip but

    a good practice in loading UI Picasso library provides functionality to prioritize the image loading through priority(). priority() takes one of three constants, HIGH, MEDIUM, or LOW
  18. XML drawables Are you using lots of images? You can

    achieve some of the images by creating XML. Creating an image with XML is very cheap and it is more efficient. Using a 500 kb background image or using an Xml drawable which has size of just 400 bytes?
  19. Use Vectors One of the benefits of using vectors in

    Android is Scalability (No need to put different size of drawable images) VectorDrawable (Added in API 21) Problem: OutOfMemory exception while animation-list (Use case in next slide) Can animate vector drawables easily
  20. Use case: Using <animation-list> Use case: One drawable is having

    2-3 animated portions into it 30 images each set Each image is of 320*480 resolution Bytes per pixel, typically 4 (32 bits) 1 image = 320*480*4 bytes which will be 614,000 bytes 30 frames (images) = 614,000 * 30 = 18420000 1 set = 18420000 bytes If 3 set then total 55260000 bytes = 55.26 MB to hold raw bitmap data of all frames
  21. Use case: Using <animation-list> Use case: One drawable is having

    2-3 animated portions into it 30 images each set Each image is of 320*480 resolution Bytes per pixel, typically 4 (32 bits) 1 image = 320*480*4 bytes which will be 614,000 bytes 30 frames (images) = 614,000 * 30 = 18420000 1 set = 18420000 bytes If 3 set then total 55260000 bytes = 55.26 MB to hold raw bitmap data of all frames 55.26 MB?? Congrats for OutOfMemoryException
  22. Use case: Using <animation-list> Use case: One drawable is having

    2-3 animated portions into it 30 images each set Each image is of 320*480 resolution Bytes per pixel, typically 4 (32 bits) 1 image = 320*480*4 bytes which will be 614,000 bytes 30 frames (images) = 614,000 * 30 = 18420000 1 set = 18420000 bytes If 3 set then total 55260000 bytes = 55.26 MB to hold raw bitmap data of all frames 55.26 MB?? Congrats for OutOfMemoryException Reason: Heap memory limit exceeded Keep in mind: 16 MB/24 MB/32 MB
  23. Use Vectors Problem: OutOfMemory exception while animation-list (Use case in

    next slide) Solution: Use vector drawable (AnimatedVectorDrawable)
  24. <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="64dp" android:width="64dp" android:viewportHeight="600" android:viewportWidth="600"> <group android:name="rotationGroup" android:pivotX="300.0" android:pivotY="300.0"

    android:rotation="45.0" > <path android:name="v" android:fillColor="#000000" android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" /> </group> </vector> <animated-vector android:drawable="@drawable/vectordrawable" > <target android:name="rotationGroup" android:animation="@anim/rotation" /> <target android:name="v" android:animation="@anim/path_morph" /> </animated-vector> <objectAnimator android:duration="6000" android:propertyName="rotation" android:valueFrom="0" android:valueTo="360" /> animated_vd.xml rotation.xml vectordrawable.xml
  25. Parsing JSON response Because Parsing has a performance effect Small

    JSON - GSON Large JSON - Jackson GSON or Jackson increases code readability
  26. Effective API Design Send only required fields and avoid unnecessary

    details Most of the time enemy is Select * query Can optimize APIs with “Expiry date” or “Last updated” date
  27. External libraries, be careful! Good libraries provide documentation/example Who knows

    developers behind library has written solid code!! Test before integrate https://medium.com/@orhanobut/test-before-integrate-e713090070d6#. ian7jqalo
  28. Remember to remove your preview tags Case 1: It would

    be fine If the image is only loaded once but what if the sample image is on item layout Case 2: It would be fine if android:text attribute is set successfully with the server response. What if it would not, “Demo text”?
  29. Version Control System Project should be done under version control

    system Open source projects are using Git Follow Git branching work • Gitflow workflow • Feature branch workflow Follow development process via PR(Pull Requests)
  30. PR, Code Review & Continuous Integration Submits PR Do Code

    Review on PR PR triggers a build of the project on CI server Merge if build of a PR successful Developers Code review comments
  31. Continuous Integration CI should perform below tasks: • Perform static

    analysis • Run unit tests • Run functional tests • Check code coverage report
  32. Static Code/Resource Analysis Helps you finding issues in your code

    before you ship it to production Which are the tools available? => Android Lint, SonarQube, FB Infer, etc. The lint tool checks your Android project source files for potential bugs and optimization improvements for correctness, security, performance, usability, accessibility, and internationalization Select Analyze => Inspect code...
  33. Testing matters a lot... Unit Testing Tests that are designed

    to ensure the smallest divisible pieces of code (units or components) are working the way they were intended. Typically placed in the app/src/test/java folder. Instrumentation testing Android Instrumentation is a set of "hooks" into the Android system that allow you to control the lifecycle of Android components Typically placed in the app/src/androidTest/java folder.
  34. Code Coverage How to know how much code has been

    covered through Unit test? Use tool like JaCoCo (Java Code Coverage) JaCoCo gives detailed report (graphical) of code coverage
  35. Don’t wait for users to submit crash reports You would

    not come to know about crashes being faced by users, until they submit voluntarily Then how to get it automatically? Use tools like Crashlytics, Crittercism, ACRA
  36. Managing signing configs efficient way Don’t include signingConfigs in your

    build.gradle file, because it would be available in the version control system Efficient way: Include it in gradle.properties and don’t include it in version control system
  37. Manage dependencies in effective way 1. Create a dependencies.gradle file

    in the root of the project and include all the required permissions 2. Apply the this dependencies gradle file in build.gradle of root project apply from: 'dependencies.gradle' 3. Refer required dependencies in module(s)
  38. Manage dependencies in effective way dependencies.gradle at root level module

    level build.gradle apply dependencies.gradle in build. gradle at root level
  39. Follow android code styles all the time How? Use some

    static code analysis tools like SonarQube or Checkstyle http://checkstyle.sourceforge.net/
  40. Naming convention How? Follow the convention of prefixing the type,

    as in type_foo_bar.xml. Examples: • fragment_contact_details.xml • view_primary_button.xml • activity_main.xml.
  41. All the styles in single file? Wait... Split a large

    file into different files like: • styles.xml • app_theme.xml Check GDG Frisbee for the example: https://github.com/gdg-x/frisbee
  42. Localization - an easy way How? Do you know strings.xml?

    Define a string with common name but different values in different values folder
  43. Conclusion - Analyse and optimize application performance - Have an

    awesome architecture followed - Have an awesome UI/UX - Remove unused code/resources - Testing matters a lot! Follow best practices and standards but… but… - Don’t over engineer - Work with framework, not against!