Skip to content

Commit eea91d3

Browse files
[android] avoid View.Context during ContextView scrolling (#8243)
Context: #8012 Context: https://github.com/Kalyxt/Test_CollectionView We had some reports of poor `CollectionView` performance while scrolling on an older Android device. Reviewing `dotnet trace` output, I did find some issues similar to #8001: 317.42ms (1.1%) mono.android!Android.Views.View.get_Context() 1% of the time is spent in repeated calls to `View.Context` inside the `ItemContentView` class. Making a new overload for `ContextExtensions.FromPixel()`, I was able to remove all of these calls. This results in only a couple `View.Context` calls on startup now, much better: 1.30ms (0.01%) mono.android!Android.Views.View.get_Context() Using the "janky frames" metric from the latest profiler in Android Studio Dolphin: https://developer.android.com/studio/profile/jank-detection With my slowest Android 12+ device, a Pixel 4a, I could actually see a few "janky frames" while scrolling the sample. With these changes in place, I only see 1 "janky frame" now. I also compared the before and after with the visual GPU profiler: https://developer.android.com/topic/performance/rendering/inspect-gpu-rendering It appears at a glance that these changes are better. I am unsure at what point the performance will be good enough to close #8012, but this helps!
1 parent 0b6976e commit eea91d3

File tree

3 files changed

+25
-15
lines changed

3 files changed

+25
-15
lines changed

src/Controls/src/Core/Handlers/Items/Android/ItemContentView.cs

+9-9
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ protected override void OnLayout(bool changed, int l, int t, int r, int b)
6565
return;
6666
}
6767

68-
var size = Context.FromPixels(r - l, b - t);
68+
var size = this.FromPixels(r - l, b - t);
6969

7070
//TODO: RUI Is this the best way?
7171
//View.Arrange(new Rectangle(Point.Zero, size));
@@ -99,23 +99,23 @@ protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
9999

100100
var width = MeasureSpec.GetMode(widthMeasureSpec) == MeasureSpecMode.Unspecified
101101
? double.PositiveInfinity
102-
: Context.FromPixels(pixelWidth);
102+
: this.FromPixels(pixelWidth);
103103

104104
var height = MeasureSpec.GetMode(heightMeasureSpec) == MeasureSpecMode.Unspecified
105105
? double.PositiveInfinity
106-
: Context.FromPixels(pixelHeight);
106+
: this.FromPixels(pixelHeight);
107107

108108

109109
var measure = View.Measure(width, height);
110110

111111
if (pixelWidth == 0)
112112
{
113-
pixelWidth = (int)Context.ToPixels(measure.Width);
113+
pixelWidth = (int)this.ToPixels(measure.Width);
114114
}
115115

116116
if (pixelHeight == 0)
117117
{
118-
pixelHeight = (int)Context.ToPixels(measure.Height);
118+
pixelHeight = (int)this.ToPixels(measure.Height);
119119
}
120120

121121
_reportMeasure?.Invoke(new Size(pixelWidth, pixelHeight));
@@ -144,10 +144,10 @@ void UpdateContentLayout()
144144
if (mauiControlsView == null || aview == null)
145145
return;
146146

147-
var x = (int)Context.ToPixels(mauiControlsView.X);
148-
var y = (int)Context.ToPixels(mauiControlsView.Y);
149-
var width = Math.Max(0, (int)Context.ToPixels(mauiControlsView.Width));
150-
var height = Math.Max(0, (int)Context.ToPixels(mauiControlsView.Height));
147+
var x = (int)this.ToPixels(mauiControlsView.X);
148+
var y = (int)this.ToPixels(mauiControlsView.Y);
149+
var width = Math.Max(0, (int)this.ToPixels(mauiControlsView.Width));
150+
var height = Math.Max(0, (int)this.ToPixels(mauiControlsView.Height));
151151

152152
aview.Layout(x, y, width, height);
153153

src/Controls/src/Core/Handlers/Items/Android/RecyclerViewScrollListener.cs

+4-6
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,12 @@ public override void OnScrolled(RecyclerView recyclerView, int dx, int dy)
4141
_verticalOffset += dy;
4242

4343
var (First, Center, Last) = GetVisibleItemsIndex(recyclerView);
44-
45-
var context = recyclerView.Context;
4644
var itemsViewScrolledEventArgs = new ItemsViewScrolledEventArgs
4745
{
48-
HorizontalDelta = context.FromPixels(dx),
49-
VerticalDelta = context.FromPixels(dy),
50-
HorizontalOffset = context.FromPixels(_horizontalOffset),
51-
VerticalOffset = context.FromPixels(_verticalOffset),
46+
HorizontalDelta = recyclerView.FromPixels(dx),
47+
VerticalDelta = recyclerView.FromPixels(dy),
48+
HorizontalOffset = recyclerView.FromPixels(_horizontalOffset),
49+
VerticalOffset = recyclerView.FromPixels(_verticalOffset),
5250
FirstVisibleItemIndex = First,
5351
CenterItemIndex = Center,
5452
LastVisibleItemIndex = Last

src/Core/src/Platform/Android/ContextExtensions.cs

+12
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,25 @@ public static class ContextExtensions
3232
// TODO FromPixels/ToPixels is both not terribly descriptive and also possibly sort of inaccurate?
3333
// These need better names. It's really To/From Device-Independent, but that doesn't exactly roll off the tongue.
3434

35+
internal static double FromPixels(this View view, double pixels)
36+
{
37+
if (s_displayDensity != float.MinValue)
38+
return pixels / s_displayDensity;
39+
return view.Context.FromPixels(pixels);
40+
}
41+
3542
public static double FromPixels(this Context? self, double pixels)
3643
{
3744
EnsureMetrics(self);
3845

3946
return pixels / s_displayDensity;
4047
}
4148

49+
internal static Size FromPixels(this View view, double width, double height)
50+
{
51+
return new Size(view.FromPixels(width), view.FromPixels(height));
52+
}
53+
4254
public static Size FromPixels(this Context context, double width, double height)
4355
{
4456
return new Size(context.FromPixels(width), context.FromPixels(height));

0 commit comments

Comments
 (0)