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

Oleksandr Tolstykh

  • 1.
  • 3.
    How to measureUI 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 measureUI 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 Avoiddeep 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 Makesure 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 theLinearLayoutManager#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 theaffected items adapter.notifyItemRemoved(position); adapter.notifyItemChanged(position); adapter.notifyItemInserted(position); * only if you know position of updated item @a_tolstykh
  • 11.
  • 12.
    Remove an itemwith 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 (intsize); 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 versionof 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 theLinearLayoutManager#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 classPreCachingLayoutManager 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 donewhile 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 advancedprefetch... Prefetch images! (on wi-fi only) @a_tolstykh Picasso.with(context).load(url).fetch(); Glide.with(context).load(url).downloadOnly(width, height);
  • 20.
  • 21.
    @a_tolstykh public class CitiesAdapterextends 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 ImagePrefetcherimplements 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 TweetPrefetcherimplements 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 Displayeditems 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 libraryversion 24.2.0 https://developer.android.com/reference/android/support/v7/util/DiffUtil.html
  • 27.
    android.support.v7.util.DiffUtil Documentation: “DiffUtil is autility 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 DiffCallbackextends 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(intoldItemPosition, 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 intgetOldListSize() { 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 AndroidOS, v4.2 1080 x 1920 pixels Quad-core 1.5 GHz
  • 32.
    DiffUtil.Callback public Object getChangePayload(intoldItemPosition, 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 valueschange 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(CityViewHolderholder, 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 animationyou want! ● android.widget.TextSwitcher ● com.hanks.htextview.HTextView ● custom... @a_tolstykh DiffUtil animated values change
  • 36.
    DiffUtil - averageruntimes ● 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 significanttime for large dataset... @a_tolstykh Use it on a background thread!
  • 38.
    DiffUtil. Summary Avoid unnecessaryUI updates! @a_tolstykh Animated values change!
  • 39.
    Android TextView withrich 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" /> <ImageViewandroid: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 BeforeAfter https://github.com/a-tolstykh/textview-rich-drawable 4 Views 2 Views
  • 42.
  • 43.
    RecyclerView Performance Tuning Donot over engineer. Keep it simple! @a_tolstykh
  • 44.
    RecyclerView Performance Tuning Avoidpremature optimizations. Think about optimizations when you need them. @a_tolstykh
  • 45.