Share
Embed
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
Transcript
67 slides
-
Engineering Android apps for Performance & Quality Do’s, Don’ts and
best practices +PareshMayani @pareshmayani -
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 -
Improving layout performance 1. Optimizing layout hierarchies 2. Reusable layouts
3. Compound drawables -
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 -
Using Hierarchy Viewer Revise your layout and again inspect using
Hierarchy viewer tool -
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”/> -
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 -
<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 -
Optimizing and lowering GPU Overdraw Debug GPU Overdraw using the
developer options http://developer.android. com/tools/performance/debug-gpu-overdraw/index. html -
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 -
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 -
android { ... buildTypes { release { minifyEnabled true shrinkResources
true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } -
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 -
> 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 } -
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 -
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. -
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 -
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 -
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 -
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 -
Picasso .with(context) .load(myAdapter.exampleImages[0]) .fit() .priority(Picasso.Priority.HIGH) .into(imageViewHero); Picasso .with(context) .load(myAdapter.exampleImages[0]) .fit()
.priority(Picasso.Priority.LOW) .into(imageViewDetail); -
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? -
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 -
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 -
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 -
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 -
Use Vectors Problem: OutOfMemory exception while animation-list (Use case in
next slide) Solution: Use vector drawable (AnimatedVectorDrawable) -
<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 -
Parsing JSON response Because Parsing has a performance effect Small
JSON - GSON Large JSON - Jackson GSON or Jackson increases code readability -
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 -
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 -
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”? -
Tools attribute tools:ignore tools:targetApi tools:locale tools:context tools:layout tools:listitem / listheader
/ listfooter tools:showIn tools:menu tools:actionBarNavMode -
<EditText android:text="John Doe" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <EditText tools:text="John Doe" android:layout_width="wrap_content"
android:layout_height="wrap_content" /> Using tools:text attribute -
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) -
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 -
Continuous Integration CI should perform below tasks: • Perform static
analysis • Run unit tests • Run functional tests • Check code coverage report -
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... -
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. -
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 -
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 -
Why Architecture best practices? Have you heard about? Scalability Easily
adoptable Maintainable -
Follow architecture pattern MVC (Model View Controller) MVP (Model View
Presenter) MVVM (Model View View Model) -
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 -
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) -
Manage dependencies in effective way dependencies.gradle at root level module
level build.gradle apply dependencies.gradle in build. gradle at root level -
Follow android code styles all the time How? Use some
static code analysis tools like SonarQube or Checkstyle http://checkstyle.sourceforge.net/ -
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. -
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 -
Localization - an easy way How? Do you know strings.xml?
Define a string with common name but different values in different values folder -
Strings.xml - best practice Bad <string name="error.message.call">CALL FAILED</string> Good <string
name="error.message.call">Call failed</string> -
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!