SlideShare a Scribd company logo
RecylerView
Performance Tuning
Oleksandr Tolstykh
@a_tolstykh
Head of Mobile Development,
Android engineer
How to measure UI performance?
● FPS
https://github.com/friendlyrobotnyc/TinyDancer
● Profile GPU rendering
https://developer.android.com/studio/profile/dev-options-rendering.html
● Aggregate frame stats
https://developer.android.com/training/testing/performance.html
● Many others...
@a_tolstykh
How to measure UI performance?
● FPS
https://github.com/friendlyrobotnyc/TinyDancer
● Profile GPU rendering
https://developer.android.com/studio/profile/dev-options-rendering.html
● Aggregate frame stats
https://developer.android.com/training/testing/performance.html
● Many others...
@a_tolstykh
Sample App - Travel With Us
City guides
Base city info
Data is loaded from server
@a_tolstykh
RecyclerView!
Optimize Cells hierarchies
Avoid deep hierarchy
Save level with <merge> when <include>
Layout matters!
@a_tolstykhhttps://developer.android.com/topic/performance/rendering/optimizing-view-hierarchies.html
android.support.constraint.ConstraintLayout
com.google.android.flexbox.FlexboxLayout
Minimize onBindViewHolder()
Make onBindViewHolder() as cheap as possible
Avoid item instantiations (memory allocations)
Do as much as you can in onCreateViewHolder()
@a_tolstykh
Correct images scale
Make sure their size and compression are optimal.
Scaling images may also affect the performance.
Do not reinvent the wheel.
@a_tolstykh
Picasso, ImageLoader, Fresco, Glide.
Picasso v/s Imageloader v/s Fresco vs Glide [closed]
http://stackoverflow.com/q/29363321/2308720
Nested RecyclerView
Override the LinearLayoutManager#getInitialPrefetchItemCount()
@a_tolstykh
public int getInitialPrefetchItemCount ()
Defines how many inner items should be prefetched when this LayoutManager's
RecyclerView is nested inside another RecyclerView
Binding the inner RecyclerView doesn’t allocate any children.
The prefetching system has to know how many.
Update only the affected items
adapter.notifyItemRemoved(position);
adapter.notifyItemChanged(position);
adapter.notifyItemInserted(position);
* only if you know position of updated item
@a_tolstykh
Profit!
Animations for free!
Optimizations for free!
@a_tolstykh
Remove an item with swipe:
SimpleCallback touchCallback
= new SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
// ...
@Override
public void onSwiped(ViewHolder viewHolder, int swipeDir) {
adapter.remove(viewHolder.getAdapterPosition());
adapter.notifyItemRemoved(viewHolder.getAdapterPosition());
}
};
@a_tolstykh
Items cache
recyclerView.setItemViewCacheSize (int size);
Documentation:
“Set the number of offscreen views
to retain before adding them
to the potentially shared recycled view pool.”
@a_tolstykh
static final int DEFAULT_CACHE_SIZE = 2; // supportVersion = 25.3.0
Stable ids - adapter.setHasStableIds()
adapter.setHasStableIds(true);
// YourAdapter.java
@Override
public long getItemId(int position) {
return items.get(position).hashcode(); //id()
}
@a_tolstykh
Only if item in the data set can be represented with a unique identifier
Avoid unnecessary bindViewHolder() calls if view exists in RecyclerView’s cache
Prefetch
Use latest version of support library to use native prefetch optimizations
supportVersion >= 25.1.0
// (enabled by default)
RecyclerView Prefetch by Chet Haase
https://medium.com/google-developers/c2f269075710
*Lollipop and newer
@a_tolstykh
Advanced prefetch
Override the LinearLayoutManager#getExtraLayoutSpace(RecyclerView.State s)
@a_tolstykh
protected int getExtraLayoutSpace (RecyclerView.State state)
Returns the amount of extra space that should be laid out by LayoutManager.
By default, LinearLayoutManager lays out 1 extra page of items while smooth
scrolling and 0 otherwise. You can override this method to implement your custom
layout pre-cache logic.
Advanced prefetch
@a_tolstykh
public class PreCachingLayoutManager extends LinearLayoutManager {
// ....
public void setExtraLayoutSpace(int extraLayoutSpace) {
this.extraLayoutSpace = extraLayoutSpace;
}
@Override
protected int getExtraLayoutSpace(RecyclerView.State state) {
return extraLayoutSpace > 0 ? extraLayoutSpace : DEFAULT_EXTRA_SPACE;
}
}
BUT...
Expensive if done while the user may change scrolling direction.
Laying out invisible elements generally comes with significant performance cost.
Useless without increasing cache size (setItemViewCacheSize).
@a_tolstykh
BUT it improves USER EXPERIENCE!
Need to find balance.
Maybe 1 extra screen is The Balance?
Even more advanced prefetch...
Prefetch images! (on wi-fi only)
@a_tolstykh
Picasso.with(context).load(url).fetch();
Glide.with(context).load(url).downloadOnly(width, height);
@a_tolstykh
Without
images
prefetch
With
images
prefetch
Images
prefetch
@a_tolstykh
public class CitiesAdapter extends RecyclerView.Adapter<CityViewHolder> {
public void updateData(List<City> cities) {
if (onWifi()) prefetch(cities);
// TODO update adapter data
}
private void prefetch(List<City> cities) {
for (City city : cities) {
PreFetcher prefetcher = city.getPrefetcher();
if (prefetcher != null) {
prefetcher.prefetch(mContext);
}
}
}
}
@a_tolstykh
public class ImagePrefetcher implements PreFetcher {
private final Image[] mImages;
public ImagePrefetcher(Image... images) {
this.mImages = images;
}
@Override
public void prefetch(Context context) {
for (Image image : mImages) {
Picasso.with(context).load(image.url()).fetch();
}
}
}
@a_tolstykh
public class TweetPrefetcher implements PreFetcher {
private final List<String> mIds;
@Override
public void prefetch(Context context) {
TwitterHelper.loadTweets(mIds, new Callback<List<Tweet>>() {
@Override
public void success(Result<List<Tweet>> result) {
for (Tweet tweet : result.data) {
TwitterHelper.preFetchTweetImage(tweet);
}
}
});
}
}
Displayed data pre-calculations
Displayed items are POJOs.
Pre-format date, time, other strings during data parsing.
Pre-calculate immutable values during data parsing.
@a_tolstykh
Profit:
+ Minor performance optimisations.
+ Formatting is done in BG thread.
+ Formatting is done in single place.
Displayed data pre-calculations (formatting)
@WorkerThread
private static Rating createRating(float value, int count) {
String ratingFormatted = String.format(Locale.US, "%.1f", value);
String countFormatted = formatWithMetricPrefix(count);
return Rating.create(value, ratingFormatted, count, countFormatted);
}
@UiThread
public void setRating(Rating rating) {
ratingText.setText(rating.ratingFormatted());
reviewsCount.setText(rating.numberOfReviewsFormatted());
}
@a_tolstykh
DiffUtil
@a_tolstykh
*since support library version 24.2.0
https://developer.android.com/reference/android/support/v7/util/DiffUtil.html
android.support.v7.util.DiffUtil
Documentation:
“DiffUtil is a utility class that can calculate the difference between two
lists and output a list of update operations that converts the first list
into the second one.
@a_tolstykh
It can be used to calculate updates for a RecyclerView Adapter.”
DiffUtil.Callback cb = new YourDiffCallback(oldList, newList);
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(cb);
diffResult.dispatchUpdatesTo(adapter);
DiffUtil.Callback
public class DiffCallback extends DiffUtil.Callback {
public DiffCallback(@NonNull List<City> newList, @NonNull List<City> oldList) {...}
public int getOldListSize() {...}
public int getNewListSize() {...}
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {...}
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {...}
@Nullable
public Object getChangePayload(int oldItemPosition, int newItemPosition) {...}
}
@a_tolstykh
DiffUtil.Callback
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition)
When areItemsTheSame(int, int) returns true for two items and areContentsTheSame(int,
int) returns false for them, DiffUtil calls this method to get a payload about the change.
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition)
Checks whether two items have the same data.You can change its behavior depending on
your UI. This method is called by DiffUtil only if areItemsTheSame returns true.
@a_tolstykh
DiffUtil.Callback
@Override public int getOldListSize() {
return oldCities.size();
}
@Override public int getNewListSize() {
return newCities.size();
}
@Override public boolean areItemsTheSame(int oldPosition, int newPosition) {
return oldCities.get(oldPosition).id() == newCities.get(newPosition).id();
}
@Override public boolean areContentsTheSame(int oldPosition, int newPosition) {
return oldCities.get(oldPosition).equals(newCities.get(newPosition));
}
@a_tolstykh
@a_tolstykh
Without
DiffUtil
With
DiffUtil
Lenovo Vibe X
Android OS, v4.2
1080 x 1920 pixels
Quad-core 1.5 GHz
DiffUtil.Callback
public Object getChangePayload(int oldItemPosition, int newItemPosition)
When areItemsTheSame(int, int) returns true for two items and areContentsTheSame(int,
int) returns false for them, DiffUtil calls this method to get a payload about the change.
@a_tolstykh
For example, if you are using DiffUtil with RecyclerView, you can return
the particular field that changed in the item and your ItemAnimator can
use that information to run the correct animation.
DiffUtil animated values change
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
Bundle diff = new Bundle();
Rating newRating = newCities.get(newItemPosition).rating();
Rating oldRating = oldCities.get(oldItemPosition).rating();
if (newRating.numberOfReviews() != oldRating.numberOfReviews()) {
diff.putString(KEY_NUMBER_OF_REVIEWS, newRating.numberOfReviewsFormatted());
}
if (Float.compare(newRating.rating(), oldRating.rating()) != 0) {
diff.putString(KEY_RATING_FORMATTED, newRating.ratingFormatted());
}
return diff.size() == 0 ? null : diff;
}
@a_tolstykh
public void onBindViewHolder(CityViewHolder holder, int index, List<Object> p) {
if (p.isEmpty()) {
onBindViewHolder(holder, index);
return;
}
Bundle payload = (Bundle) p.get(0);
for (String key : payload.keySet()) {
if (key.equals(CitiesDiffCallback.KEY_NUMBER_OF_REVIEWS)) {
holder.animateReviews(payload.getString(key));
} else if (key.equals(CitiesDiffCallback.KEY_RATING_FORMATTED)) {
holder.animateRating(payload.getString(key));
}
}
}
@a_tolstykh
Use any animation you want!
● android.widget.TextSwitcher
● com.hanks.htextview.HTextView
● custom...
@a_tolstykh
DiffUtil animated values change
DiffUtil - average runtimes
● 100 items and 10 modifications: avg: 0.39 ms, median: 0.35 ms
● 100 items and 100 modifications: 3.82 ms, median: 3.75 ms
● 100 items and 100 modifications without moves: 2.09 ms, median: 2.06 ms
● 1000 items and 50 modifications: avg: 4.67 ms, median: 4.59 ms
● 1000 items and 50 modifications without moves: avg: 3.59 ms, median: 3.50 ms
● 1000 items and 200 modifications: 27.07 ms, median: 26.92 ms
● 1000 items and 200 modifications without moves: 13.54 ms, median: 13.36 ms
tests are run on Nexus 5X with M
@a_tolstykh
DiffUtil
May take significant time for large dataset...
@a_tolstykh
Use it on a background thread!
DiffUtil. Summary
Avoid unnecessary UI updates!
@a_tolstykh
Animated values change!
Android TextView with rich support
of compound drawables.
This is a tiny library which empowers
TextView's compound drawables with:
● size specifying
● vector support
● tinting
@a_tolstykhhttps://github.com/a-tolstykh/textview-rich-drawable
TextViewRichDrawable
<TextView android:id="@+id/reviews_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ImageView android:layout_width="@dimen/icon_small_size"
android:layout_height="@dimen/icon_small_size"
android:layout_marginEnd="@dimen/space_medium"
android:src="@drawable/ic_person" />
<TextView android:id="@+id/rating_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ImageView android:layout_width="@dimen/icon_small_size"
android:layout_height="@dimen/icon_small_size"
android:src="@drawable/ic_star" />
@a_tolstykh
<com.tolstykh.textviewrichdrawable.TextViewRichDrawable
android:id="@+id/reviews_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/space_medium"
android:drawableRight="@drawable/ic_person"
app:compoundDrawableHeight="@dimen/icon_small_size"
app:compoundDrawableWidth="@dimen/icon_small_size" />
<com.tolstykh.textviewrichdrawable.TextViewRichDrawable
android:id="@+id/rating_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableRight="@drawable/ic_star"
app:compoundDrawableHeight="@dimen/icon_small_size"
app:compoundDrawableWidth="@dimen/icon_small_size" />
Before After
https://github.com/a-tolstykh/textview-rich-drawable
Simplify your layout
@a_tolstykh
Before After
https://github.com/a-tolstykh/textview-rich-drawable
4 Views 2 Views
RecyclerView Performance Tuning
Prefetch. Cache. Reuse!
@a_tolstykh
RecyclerView Performance Tuning
Do not over engineer. Keep it simple!
@a_tolstykh
RecyclerView Performance Tuning
Avoid premature optimizations. Think about
optimizations when you need them.
@a_tolstykh
Thanks
@a_tolstykh
@a_tolstykh
@a_tolstykh
+OleksandrTolstykh

More Related Content

What's hot

AngularJS Unit Testing w/Karma and Jasmine
AngularJS Unit Testing w/Karma and JasmineAngularJS Unit Testing w/Karma and Jasmine
AngularJS Unit Testing w/Karma and Jasmine
foxp2code
 
Jasmine BDD for Javascript
Jasmine BDD for JavascriptJasmine BDD for Javascript
Jasmine BDD for Javascript
Luis Alfredo Porras Páez
 
Building scalable applications with angular js
Building scalable applications with angular jsBuilding scalable applications with angular js
Building scalable applications with angular js
Andrew Alpert
 
React table tutorial use filter (part 2)
React table tutorial use filter (part 2)React table tutorial use filter (part 2)
React table tutorial use filter (part 2)
Katy Slemon
 
Workshop 26: React Native - The Native Side
Workshop 26: React Native - The Native SideWorkshop 26: React Native - The Native Side
Workshop 26: React Native - The Native Side
Visual Engineering
 
Unit Testing Express and Koa Middleware in ES2015
Unit Testing Express and Koa Middleware in ES2015Unit Testing Express and Koa Middleware in ES2015
Unit Testing Express and Koa Middleware in ES2015
Morris Singer
 
Jasmine - why JS tests don't smell fishy
Jasmine - why JS tests don't smell fishyJasmine - why JS tests don't smell fishy
Jasmine - why JS tests don't smell fishy
Igor Napierala
 
"Full Stack frameworks or a story about how to reconcile Front (good) and Bac...
"Full Stack frameworks or a story about how to reconcile Front (good) and Bac..."Full Stack frameworks or a story about how to reconcile Front (good) and Bac...
"Full Stack frameworks or a story about how to reconcile Front (good) and Bac...
Fwdays
 
Unit Testing Express Middleware
Unit Testing Express MiddlewareUnit Testing Express Middleware
Unit Testing Express Middleware
Morris Singer
 
Testing in AngularJS
Testing in AngularJSTesting in AngularJS
Testing in AngularJS
Peter Drinnan
 
Implement react pagination with react hooks and react paginate
Implement react pagination with react hooks and react paginateImplement react pagination with react hooks and react paginate
Implement react pagination with react hooks and react paginate
Katy Slemon
 
Test-Driven Development of AngularJS Applications
Test-Driven Development of AngularJS ApplicationsTest-Driven Development of AngularJS Applications
Test-Driven Development of AngularJS Applications
FITC
 
AngularJS Unit Testing
AngularJS Unit TestingAngularJS Unit Testing
AngularJS Unit Testing
Prince Norin
 
[Srijan Wednesday Webinars] Ruling Drupal 8 with #d8rules
[Srijan Wednesday Webinars] Ruling Drupal 8 with #d8rules[Srijan Wednesday Webinars] Ruling Drupal 8 with #d8rules
[Srijan Wednesday Webinars] Ruling Drupal 8 with #d8rules
Srijan Technologies
 
Javascript Testing with Jasmine 101
Javascript Testing with Jasmine 101Javascript Testing with Jasmine 101
Javascript Testing with Jasmine 101
Roy Yu
 
Intro to Unit Testing in AngularJS
Intro to Unit Testing in AngularJSIntro to Unit Testing in AngularJS
Intro to Unit Testing in AngularJS
Jim Lynch
 
JavaOne - The JavaFX Community and Ecosystem
JavaOne - The JavaFX Community and EcosystemJavaOne - The JavaFX Community and Ecosystem
JavaOne - The JavaFX Community and Ecosystem
Alexander Casall
 
KISS Automation.py
KISS Automation.pyKISS Automation.py
KISS Automation.py
Iakiv Kramarenko
 
JavaScript TDD with Jasmine and Karma
JavaScript TDD with Jasmine and KarmaJavaScript TDD with Jasmine and Karma
JavaScript TDD with Jasmine and Karma
Christopher Bartling
 
CakePHP
CakePHPCakePHP

What's hot (20)

AngularJS Unit Testing w/Karma and Jasmine
AngularJS Unit Testing w/Karma and JasmineAngularJS Unit Testing w/Karma and Jasmine
AngularJS Unit Testing w/Karma and Jasmine
 
Jasmine BDD for Javascript
Jasmine BDD for JavascriptJasmine BDD for Javascript
Jasmine BDD for Javascript
 
Building scalable applications with angular js
Building scalable applications with angular jsBuilding scalable applications with angular js
Building scalable applications with angular js
 
React table tutorial use filter (part 2)
React table tutorial use filter (part 2)React table tutorial use filter (part 2)
React table tutorial use filter (part 2)
 
Workshop 26: React Native - The Native Side
Workshop 26: React Native - The Native SideWorkshop 26: React Native - The Native Side
Workshop 26: React Native - The Native Side
 
Unit Testing Express and Koa Middleware in ES2015
Unit Testing Express and Koa Middleware in ES2015Unit Testing Express and Koa Middleware in ES2015
Unit Testing Express and Koa Middleware in ES2015
 
Jasmine - why JS tests don't smell fishy
Jasmine - why JS tests don't smell fishyJasmine - why JS tests don't smell fishy
Jasmine - why JS tests don't smell fishy
 
"Full Stack frameworks or a story about how to reconcile Front (good) and Bac...
"Full Stack frameworks or a story about how to reconcile Front (good) and Bac..."Full Stack frameworks or a story about how to reconcile Front (good) and Bac...
"Full Stack frameworks or a story about how to reconcile Front (good) and Bac...
 
Unit Testing Express Middleware
Unit Testing Express MiddlewareUnit Testing Express Middleware
Unit Testing Express Middleware
 
Testing in AngularJS
Testing in AngularJSTesting in AngularJS
Testing in AngularJS
 
Implement react pagination with react hooks and react paginate
Implement react pagination with react hooks and react paginateImplement react pagination with react hooks and react paginate
Implement react pagination with react hooks and react paginate
 
Test-Driven Development of AngularJS Applications
Test-Driven Development of AngularJS ApplicationsTest-Driven Development of AngularJS Applications
Test-Driven Development of AngularJS Applications
 
AngularJS Unit Testing
AngularJS Unit TestingAngularJS Unit Testing
AngularJS Unit Testing
 
[Srijan Wednesday Webinars] Ruling Drupal 8 with #d8rules
[Srijan Wednesday Webinars] Ruling Drupal 8 with #d8rules[Srijan Wednesday Webinars] Ruling Drupal 8 with #d8rules
[Srijan Wednesday Webinars] Ruling Drupal 8 with #d8rules
 
Javascript Testing with Jasmine 101
Javascript Testing with Jasmine 101Javascript Testing with Jasmine 101
Javascript Testing with Jasmine 101
 
Intro to Unit Testing in AngularJS
Intro to Unit Testing in AngularJSIntro to Unit Testing in AngularJS
Intro to Unit Testing in AngularJS
 
JavaOne - The JavaFX Community and Ecosystem
JavaOne - The JavaFX Community and EcosystemJavaOne - The JavaFX Community and Ecosystem
JavaOne - The JavaFX Community and Ecosystem
 
KISS Automation.py
KISS Automation.pyKISS Automation.py
KISS Automation.py
 
JavaScript TDD with Jasmine and Karma
JavaScript TDD with Jasmine and KarmaJavaScript TDD with Jasmine and Karma
JavaScript TDD with Jasmine and Karma
 
CakePHP
CakePHPCakePHP
CakePHP
 

Similar to Oleksandr Tolstykh

Android workshop
Android workshopAndroid workshop
Android workshop
Michael Galpin
 
Building Modern Apps using Android Architecture Components
Building Modern Apps using Android Architecture ComponentsBuilding Modern Apps using Android Architecture Components
Building Modern Apps using Android Architecture Components
Hassan Abid
 
Android Best Practices
Android Best PracticesAndroid Best Practices
Android Best Practices
Yekmer Simsek
 
droidparts
droidpartsdroidparts
droidparts
Droidcon Berlin
 
Android best practices
Android best practicesAndroid best practices
Android best practices
Jose Manuel Ortega Candel
 
S03 hybrid app_and_gae_datastore_v1.0
S03 hybrid app_and_gae_datastore_v1.0S03 hybrid app_and_gae_datastore_v1.0
S03 hybrid app_and_gae_datastore_v1.0
Sun-Jin Jang
 
比XML更好用的Java Annotation
比XML更好用的Java Annotation比XML更好用的Java Annotation
比XML更好用的Java Annotation
javatwo2011
 
JavaScript Refactoring
JavaScript RefactoringJavaScript Refactoring
JavaScript Refactoring
Krzysztof Szafranek
 
Developer Student Clubs NUK - Flutter for Beginners
Developer Student Clubs NUK - Flutter for BeginnersDeveloper Student Clubs NUK - Flutter for Beginners
Developer Student Clubs NUK - Flutter for Beginners
Jiaxuan Lin
 
Testing persistence in PHP with DbUnit
Testing persistence in PHP with DbUnitTesting persistence in PHP with DbUnit
Testing persistence in PHP with DbUnit
Peter Wilcsinszky
 
From Legacy to Hexagonal (An Unexpected Android Journey)
From Legacy to Hexagonal (An Unexpected Android Journey)From Legacy to Hexagonal (An Unexpected Android Journey)
From Legacy to Hexagonal (An Unexpected Android Journey)
Jose Manuel Pereira Garcia
 
XebiCon'17 : Faites chauffer les neurones de votre Smartphone avec du Deep Le...
XebiCon'17 : Faites chauffer les neurones de votre Smartphone avec du Deep Le...XebiCon'17 : Faites chauffer les neurones de votre Smartphone avec du Deep Le...
XebiCon'17 : Faites chauffer les neurones de votre Smartphone avec du Deep Le...
Publicis Sapient Engineering
 
PyCon India 2010 Building Scalable apps using appengine
PyCon India 2010 Building Scalable apps using appenginePyCon India 2010 Building Scalable apps using appengine
PyCon India 2010 Building Scalable apps using appengine
Pranav Prakash
 
Don't Make Android Bad... Again
Don't Make Android Bad... AgainDon't Make Android Bad... Again
Don't Make Android Bad... Again
Pedro Vicente
 
Architecture components - IT Talk
Architecture components - IT TalkArchitecture components - IT Talk
Architecture components - IT Talk
Constantine Mars
 
Architecture Components
Architecture Components Architecture Components
Architecture Components
DataArt
 
Introducing Struts 2
Introducing Struts 2Introducing Struts 2
Introducing Struts 2
wiradikusuma
 
Тарас Олексин - Sculpt! Your! Tests!
Тарас Олексин  - Sculpt! Your! Tests!Тарас Олексин  - Sculpt! Your! Tests!
Тарас Олексин - Sculpt! Your! Tests!
DataArt
 
A GWT Application with MVP Pattern Deploying to CloudFoundry using Spring Roo
A GWT Application with MVP Pattern Deploying to CloudFoundry using  Spring Roo A GWT Application with MVP Pattern Deploying to CloudFoundry using  Spring Roo
A GWT Application with MVP Pattern Deploying to CloudFoundry using Spring Roo
Ali Parmaksiz
 
Struts 2 + Spring
Struts 2 + SpringStruts 2 + Spring
Struts 2 + Spring
Bryan Hsueh
 

Similar to Oleksandr Tolstykh (20)

Android workshop
Android workshopAndroid workshop
Android workshop
 
Building Modern Apps using Android Architecture Components
Building Modern Apps using Android Architecture ComponentsBuilding Modern Apps using Android Architecture Components
Building Modern Apps using Android Architecture Components
 
Android Best Practices
Android Best PracticesAndroid Best Practices
Android Best Practices
 
droidparts
droidpartsdroidparts
droidparts
 
Android best practices
Android best practicesAndroid best practices
Android best practices
 
S03 hybrid app_and_gae_datastore_v1.0
S03 hybrid app_and_gae_datastore_v1.0S03 hybrid app_and_gae_datastore_v1.0
S03 hybrid app_and_gae_datastore_v1.0
 
比XML更好用的Java Annotation
比XML更好用的Java Annotation比XML更好用的Java Annotation
比XML更好用的Java Annotation
 
JavaScript Refactoring
JavaScript RefactoringJavaScript Refactoring
JavaScript Refactoring
 
Developer Student Clubs NUK - Flutter for Beginners
Developer Student Clubs NUK - Flutter for BeginnersDeveloper Student Clubs NUK - Flutter for Beginners
Developer Student Clubs NUK - Flutter for Beginners
 
Testing persistence in PHP with DbUnit
Testing persistence in PHP with DbUnitTesting persistence in PHP with DbUnit
Testing persistence in PHP with DbUnit
 
From Legacy to Hexagonal (An Unexpected Android Journey)
From Legacy to Hexagonal (An Unexpected Android Journey)From Legacy to Hexagonal (An Unexpected Android Journey)
From Legacy to Hexagonal (An Unexpected Android Journey)
 
XebiCon'17 : Faites chauffer les neurones de votre Smartphone avec du Deep Le...
XebiCon'17 : Faites chauffer les neurones de votre Smartphone avec du Deep Le...XebiCon'17 : Faites chauffer les neurones de votre Smartphone avec du Deep Le...
XebiCon'17 : Faites chauffer les neurones de votre Smartphone avec du Deep Le...
 
PyCon India 2010 Building Scalable apps using appengine
PyCon India 2010 Building Scalable apps using appenginePyCon India 2010 Building Scalable apps using appengine
PyCon India 2010 Building Scalable apps using appengine
 
Don't Make Android Bad... Again
Don't Make Android Bad... AgainDon't Make Android Bad... Again
Don't Make Android Bad... Again
 
Architecture components - IT Talk
Architecture components - IT TalkArchitecture components - IT Talk
Architecture components - IT Talk
 
Architecture Components
Architecture Components Architecture Components
Architecture Components
 
Introducing Struts 2
Introducing Struts 2Introducing Struts 2
Introducing Struts 2
 
Тарас Олексин - Sculpt! Your! Tests!
Тарас Олексин  - Sculpt! Your! Tests!Тарас Олексин  - Sculpt! Your! Tests!
Тарас Олексин - Sculpt! Your! Tests!
 
A GWT Application with MVP Pattern Deploying to CloudFoundry using Spring Roo
A GWT Application with MVP Pattern Deploying to CloudFoundry using  Spring Roo A GWT Application with MVP Pattern Deploying to CloudFoundry using  Spring Roo
A GWT Application with MVP Pattern Deploying to CloudFoundry using Spring Roo
 
Struts 2 + Spring
Struts 2 + SpringStruts 2 + Spring
Struts 2 + Spring
 

More from CodeFest

Alexander Graebe
Alexander GraebeAlexander Graebe
Alexander Graebe
CodeFest
 
Никита Прокопов
Никита ПрокоповНикита Прокопов
Никита Прокопов
CodeFest
 
Денис Баталов
Денис БаталовДенис Баталов
Денис Баталов
CodeFest
 
Елена Гальцина
Елена ГальцинаЕлена Гальцина
Елена Гальцина
CodeFest
 
Александр Калашников
Александр КалашниковАлександр Калашников
Александр Калашников
CodeFest
 
Ирина Иванова
Ирина ИвановаИрина Иванова
Ирина Иванова
CodeFest
 
Marko Berković
Marko BerkovićMarko Berković
Marko Berković
CodeFest
 
Денис Кортунов
Денис КортуновДенис Кортунов
Денис Кортунов
CodeFest
 
Александр Зимин
Александр ЗиминАлександр Зимин
Александр Зимин
CodeFest
 
Сергей Крапивенский
Сергей КрапивенскийСергей Крапивенский
Сергей Крапивенский
CodeFest
 
Сергей Игнатов
Сергей ИгнатовСергей Игнатов
Сергей Игнатов
CodeFest
 
Николай Крапивный
Николай КрапивныйНиколай Крапивный
Николай Крапивный
CodeFest
 
Alexander Graebe
Alexander GraebeAlexander Graebe
Alexander Graebe
CodeFest
 
Вадим Смирнов
Вадим СмирновВадим Смирнов
Вадим Смирнов
CodeFest
 
Константин Осипов
Константин ОсиповКонстантин Осипов
Константин Осипов
CodeFest
 
Raffaele Rialdi
Raffaele RialdiRaffaele Rialdi
Raffaele Rialdi
CodeFest
 
Максим Пугачев
Максим ПугачевМаксим Пугачев
Максим Пугачев
CodeFest
 
Rene Groeschke
Rene GroeschkeRene Groeschke
Rene Groeschke
CodeFest
 
Иван Бондаренко
Иван БондаренкоИван Бондаренко
Иван Бондаренко
CodeFest
 
Mete Atamel
Mete AtamelMete Atamel
Mete Atamel
CodeFest
 

More from CodeFest (20)

Alexander Graebe
Alexander GraebeAlexander Graebe
Alexander Graebe
 
Никита Прокопов
Никита ПрокоповНикита Прокопов
Никита Прокопов
 
Денис Баталов
Денис БаталовДенис Баталов
Денис Баталов
 
Елена Гальцина
Елена ГальцинаЕлена Гальцина
Елена Гальцина
 
Александр Калашников
Александр КалашниковАлександр Калашников
Александр Калашников
 
Ирина Иванова
Ирина ИвановаИрина Иванова
Ирина Иванова
 
Marko Berković
Marko BerkovićMarko Berković
Marko Berković
 
Денис Кортунов
Денис КортуновДенис Кортунов
Денис Кортунов
 
Александр Зимин
Александр ЗиминАлександр Зимин
Александр Зимин
 
Сергей Крапивенский
Сергей КрапивенскийСергей Крапивенский
Сергей Крапивенский
 
Сергей Игнатов
Сергей ИгнатовСергей Игнатов
Сергей Игнатов
 
Николай Крапивный
Николай КрапивныйНиколай Крапивный
Николай Крапивный
 
Alexander Graebe
Alexander GraebeAlexander Graebe
Alexander Graebe
 
Вадим Смирнов
Вадим СмирновВадим Смирнов
Вадим Смирнов
 
Константин Осипов
Константин ОсиповКонстантин Осипов
Константин Осипов
 
Raffaele Rialdi
Raffaele RialdiRaffaele Rialdi
Raffaele Rialdi
 
Максим Пугачев
Максим ПугачевМаксим Пугачев
Максим Пугачев
 
Rene Groeschke
Rene GroeschkeRene Groeschke
Rene Groeschke
 
Иван Бондаренко
Иван БондаренкоИван Бондаренко
Иван Бондаренко
 
Mete Atamel
Mete AtamelMete Atamel
Mete Atamel
 

Oleksandr Tolstykh

  • 2.
  • 3. How to measure UI performance? ● FPS https://github.com/friendlyrobotnyc/TinyDancer ● Profile GPU rendering https://developer.android.com/studio/profile/dev-options-rendering.html ● Aggregate frame stats https://developer.android.com/training/testing/performance.html ● Many others... @a_tolstykh
  • 4. How to measure UI performance? ● FPS https://github.com/friendlyrobotnyc/TinyDancer ● Profile GPU rendering https://developer.android.com/studio/profile/dev-options-rendering.html ● Aggregate frame stats https://developer.android.com/training/testing/performance.html ● Many others... @a_tolstykh
  • 5. Sample App - Travel With Us City guides Base city info Data is loaded from server @a_tolstykh RecyclerView!
  • 6. Optimize Cells hierarchies Avoid deep hierarchy Save level with <merge> when <include> Layout matters! @a_tolstykhhttps://developer.android.com/topic/performance/rendering/optimizing-view-hierarchies.html android.support.constraint.ConstraintLayout com.google.android.flexbox.FlexboxLayout
  • 7. Minimize onBindViewHolder() Make onBindViewHolder() as cheap as possible Avoid item instantiations (memory allocations) Do as much as you can in onCreateViewHolder() @a_tolstykh
  • 8. Correct images scale Make sure their size and compression are optimal. Scaling images may also affect the performance. Do not reinvent the wheel. @a_tolstykh Picasso, ImageLoader, Fresco, Glide. Picasso v/s Imageloader v/s Fresco vs Glide [closed] http://stackoverflow.com/q/29363321/2308720
  • 9. Nested RecyclerView Override the LinearLayoutManager#getInitialPrefetchItemCount() @a_tolstykh public int getInitialPrefetchItemCount () Defines how many inner items should be prefetched when this LayoutManager's RecyclerView is nested inside another RecyclerView Binding the inner RecyclerView doesn’t allocate any children. The prefetching system has to know how many.
  • 10. Update only the affected items adapter.notifyItemRemoved(position); adapter.notifyItemChanged(position); adapter.notifyItemInserted(position); * only if you know position of updated item @a_tolstykh
  • 12. Remove an item with swipe: SimpleCallback touchCallback = new SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) { // ... @Override public void onSwiped(ViewHolder viewHolder, int swipeDir) { adapter.remove(viewHolder.getAdapterPosition()); adapter.notifyItemRemoved(viewHolder.getAdapterPosition()); } }; @a_tolstykh
  • 13. Items cache recyclerView.setItemViewCacheSize (int size); Documentation: “Set the number of offscreen views to retain before adding them to the potentially shared recycled view pool.” @a_tolstykh static final int DEFAULT_CACHE_SIZE = 2; // supportVersion = 25.3.0
  • 14. Stable ids - adapter.setHasStableIds() adapter.setHasStableIds(true); // YourAdapter.java @Override public long getItemId(int position) { return items.get(position).hashcode(); //id() } @a_tolstykh Only if item in the data set can be represented with a unique identifier Avoid unnecessary bindViewHolder() calls if view exists in RecyclerView’s cache
  • 15. Prefetch Use latest version of support library to use native prefetch optimizations supportVersion >= 25.1.0 // (enabled by default) RecyclerView Prefetch by Chet Haase https://medium.com/google-developers/c2f269075710 *Lollipop and newer @a_tolstykh
  • 16. Advanced prefetch Override the LinearLayoutManager#getExtraLayoutSpace(RecyclerView.State s) @a_tolstykh protected int getExtraLayoutSpace (RecyclerView.State state) Returns the amount of extra space that should be laid out by LayoutManager. By default, LinearLayoutManager lays out 1 extra page of items while smooth scrolling and 0 otherwise. You can override this method to implement your custom layout pre-cache logic.
  • 17. Advanced prefetch @a_tolstykh public class PreCachingLayoutManager extends LinearLayoutManager { // .... public void setExtraLayoutSpace(int extraLayoutSpace) { this.extraLayoutSpace = extraLayoutSpace; } @Override protected int getExtraLayoutSpace(RecyclerView.State state) { return extraLayoutSpace > 0 ? extraLayoutSpace : DEFAULT_EXTRA_SPACE; } }
  • 18. BUT... Expensive if done while the user may change scrolling direction. Laying out invisible elements generally comes with significant performance cost. Useless without increasing cache size (setItemViewCacheSize). @a_tolstykh BUT it improves USER EXPERIENCE! Need to find balance. Maybe 1 extra screen is The Balance?
  • 19. Even more advanced prefetch... Prefetch images! (on wi-fi only) @a_tolstykh Picasso.with(context).load(url).fetch(); Glide.with(context).load(url).downloadOnly(width, height);
  • 21. @a_tolstykh public class CitiesAdapter extends RecyclerView.Adapter<CityViewHolder> { public void updateData(List<City> cities) { if (onWifi()) prefetch(cities); // TODO update adapter data } private void prefetch(List<City> cities) { for (City city : cities) { PreFetcher prefetcher = city.getPrefetcher(); if (prefetcher != null) { prefetcher.prefetch(mContext); } } } }
  • 22. @a_tolstykh public class ImagePrefetcher implements PreFetcher { private final Image[] mImages; public ImagePrefetcher(Image... images) { this.mImages = images; } @Override public void prefetch(Context context) { for (Image image : mImages) { Picasso.with(context).load(image.url()).fetch(); } } }
  • 23. @a_tolstykh public class TweetPrefetcher implements PreFetcher { private final List<String> mIds; @Override public void prefetch(Context context) { TwitterHelper.loadTweets(mIds, new Callback<List<Tweet>>() { @Override public void success(Result<List<Tweet>> result) { for (Tweet tweet : result.data) { TwitterHelper.preFetchTweetImage(tweet); } } }); } }
  • 24. Displayed data pre-calculations Displayed items are POJOs. Pre-format date, time, other strings during data parsing. Pre-calculate immutable values during data parsing. @a_tolstykh Profit: + Minor performance optimisations. + Formatting is done in BG thread. + Formatting is done in single place.
  • 25. Displayed data pre-calculations (formatting) @WorkerThread private static Rating createRating(float value, int count) { String ratingFormatted = String.format(Locale.US, "%.1f", value); String countFormatted = formatWithMetricPrefix(count); return Rating.create(value, ratingFormatted, count, countFormatted); } @UiThread public void setRating(Rating rating) { ratingText.setText(rating.ratingFormatted()); reviewsCount.setText(rating.numberOfReviewsFormatted()); } @a_tolstykh
  • 26. DiffUtil @a_tolstykh *since support library version 24.2.0 https://developer.android.com/reference/android/support/v7/util/DiffUtil.html
  • 27. android.support.v7.util.DiffUtil Documentation: “DiffUtil is a utility class that can calculate the difference between two lists and output a list of update operations that converts the first list into the second one. @a_tolstykh It can be used to calculate updates for a RecyclerView Adapter.” DiffUtil.Callback cb = new YourDiffCallback(oldList, newList); DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(cb); diffResult.dispatchUpdatesTo(adapter);
  • 28. DiffUtil.Callback public class DiffCallback extends DiffUtil.Callback { public DiffCallback(@NonNull List<City> newList, @NonNull List<City> oldList) {...} public int getOldListSize() {...} public int getNewListSize() {...} public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {...} public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {...} @Nullable public Object getChangePayload(int oldItemPosition, int newItemPosition) {...} } @a_tolstykh
  • 29. DiffUtil.Callback public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) When areItemsTheSame(int, int) returns true for two items and areContentsTheSame(int, int) returns false for them, DiffUtil calls this method to get a payload about the change. public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) Checks whether two items have the same data.You can change its behavior depending on your UI. This method is called by DiffUtil only if areItemsTheSame returns true. @a_tolstykh
  • 30. DiffUtil.Callback @Override public int getOldListSize() { return oldCities.size(); } @Override public int getNewListSize() { return newCities.size(); } @Override public boolean areItemsTheSame(int oldPosition, int newPosition) { return oldCities.get(oldPosition).id() == newCities.get(newPosition).id(); } @Override public boolean areContentsTheSame(int oldPosition, int newPosition) { return oldCities.get(oldPosition).equals(newCities.get(newPosition)); } @a_tolstykh
  • 31. @a_tolstykh Without DiffUtil With DiffUtil Lenovo Vibe X Android OS, v4.2 1080 x 1920 pixels Quad-core 1.5 GHz
  • 32. DiffUtil.Callback public Object getChangePayload(int oldItemPosition, int newItemPosition) When areItemsTheSame(int, int) returns true for two items and areContentsTheSame(int, int) returns false for them, DiffUtil calls this method to get a payload about the change. @a_tolstykh For example, if you are using DiffUtil with RecyclerView, you can return the particular field that changed in the item and your ItemAnimator can use that information to run the correct animation.
  • 33. DiffUtil animated values change public Object getChangePayload(int oldItemPosition, int newItemPosition) { Bundle diff = new Bundle(); Rating newRating = newCities.get(newItemPosition).rating(); Rating oldRating = oldCities.get(oldItemPosition).rating(); if (newRating.numberOfReviews() != oldRating.numberOfReviews()) { diff.putString(KEY_NUMBER_OF_REVIEWS, newRating.numberOfReviewsFormatted()); } if (Float.compare(newRating.rating(), oldRating.rating()) != 0) { diff.putString(KEY_RATING_FORMATTED, newRating.ratingFormatted()); } return diff.size() == 0 ? null : diff; } @a_tolstykh
  • 34. public void onBindViewHolder(CityViewHolder holder, int index, List<Object> p) { if (p.isEmpty()) { onBindViewHolder(holder, index); return; } Bundle payload = (Bundle) p.get(0); for (String key : payload.keySet()) { if (key.equals(CitiesDiffCallback.KEY_NUMBER_OF_REVIEWS)) { holder.animateReviews(payload.getString(key)); } else if (key.equals(CitiesDiffCallback.KEY_RATING_FORMATTED)) { holder.animateRating(payload.getString(key)); } } } @a_tolstykh
  • 35. Use any animation you want! ● android.widget.TextSwitcher ● com.hanks.htextview.HTextView ● custom... @a_tolstykh DiffUtil animated values change
  • 36. DiffUtil - average runtimes ● 100 items and 10 modifications: avg: 0.39 ms, median: 0.35 ms ● 100 items and 100 modifications: 3.82 ms, median: 3.75 ms ● 100 items and 100 modifications without moves: 2.09 ms, median: 2.06 ms ● 1000 items and 50 modifications: avg: 4.67 ms, median: 4.59 ms ● 1000 items and 50 modifications without moves: avg: 3.59 ms, median: 3.50 ms ● 1000 items and 200 modifications: 27.07 ms, median: 26.92 ms ● 1000 items and 200 modifications without moves: 13.54 ms, median: 13.36 ms tests are run on Nexus 5X with M @a_tolstykh
  • 37. DiffUtil May take significant time for large dataset... @a_tolstykh Use it on a background thread!
  • 38. DiffUtil. Summary Avoid unnecessary UI updates! @a_tolstykh Animated values change!
  • 39. Android TextView with rich support of compound drawables. This is a tiny library which empowers TextView's compound drawables with: ● size specifying ● vector support ● tinting @a_tolstykhhttps://github.com/a-tolstykh/textview-rich-drawable TextViewRichDrawable
  • 40. <TextView android:id="@+id/reviews_count" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <ImageView android:layout_width="@dimen/icon_small_size" android:layout_height="@dimen/icon_small_size" android:layout_marginEnd="@dimen/space_medium" android:src="@drawable/ic_person" /> <TextView android:id="@+id/rating_value" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <ImageView android:layout_width="@dimen/icon_small_size" android:layout_height="@dimen/icon_small_size" android:src="@drawable/ic_star" /> @a_tolstykh <com.tolstykh.textviewrichdrawable.TextViewRichDrawable android:id="@+id/reviews_count" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="@dimen/space_medium" android:drawableRight="@drawable/ic_person" app:compoundDrawableHeight="@dimen/icon_small_size" app:compoundDrawableWidth="@dimen/icon_small_size" /> <com.tolstykh.textviewrichdrawable.TextViewRichDrawable android:id="@+id/rating_value" android:layout_width="wrap_content" android:layout_height="wrap_content" android:drawableRight="@drawable/ic_star" app:compoundDrawableHeight="@dimen/icon_small_size" app:compoundDrawableWidth="@dimen/icon_small_size" /> Before After https://github.com/a-tolstykh/textview-rich-drawable
  • 41. Simplify your layout @a_tolstykh Before After https://github.com/a-tolstykh/textview-rich-drawable 4 Views 2 Views
  • 42. RecyclerView Performance Tuning Prefetch. Cache. Reuse! @a_tolstykh
  • 43. RecyclerView Performance Tuning Do not over engineer. Keep it simple! @a_tolstykh
  • 44. RecyclerView Performance Tuning Avoid premature optimizations. Think about optimizations when you need them. @a_tolstykh