Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android - Shell flyout navigation is lagging when the navigation page has many controls /custom controls #10713

Open
supershopping opened this issue Oct 16, 2022 · 25 comments
Labels
area-controls-shell Shell Navigation, Routes, Tabs, Flyout delighter-sc legacy-area-perf Startup / Runtime performance p/2 Work that is important, but is currently not scheduled for release platform/android 🤖 s/triaged Issue has been reviewed s/verified Verified / Reproducible Issue ready for Engineering Triage t/bug Something isn't working t/perf The issue affects performance (runtime speed, memory usage, startup time, etc.)

Comments

@supershopping
Copy link

supershopping commented Oct 16, 2022

Description

Shell flyout item navigation is lagging when the navigation page has many controls /custom controls. The lagging situation gets worse when the page has multiple custom controls and/or more complex layout.

There is an open issue on XF too and it still has not been resolved for a long time. See below link.

xamarin/Xamarin.Forms#7521

The issue is still exist with MAUI. I think it's fair to bring it up here again.

Steps to Reproduce

  1. Create a MAUI app with default template.
  2. Add a test page with ten entries on viewmodel bindings.
  3. Add a flyout item in AppShell.
  4. When first time navigate to TestPage from shell flyout item, there is a lagging pause before opening the Test Page. The lagging situation gets worse when you have multiple custom controls and/or more complex layout.

Here is the view and viewmodel for Test Page.

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ShellFlyoutLagging.Views.TestPage"            
             xmlns:local="clr-namespace:ShellFlyoutLagging.ViewModels"
             Title="TestPage">

    <ContentPage.BindingContext>
        <local:TestPageViewModel />
    </ContentPage.BindingContext>
    <VerticalStackLayout Spacing="10" Padding="30">
        <Label 
            Text="Welcome to .NET MAUI!"
            VerticalOptions="Center" 
            HorizontalOptions="Center" />

        <Entry Text="{Binding EntryNumber1}"/>
        <Entry Text="{Binding EntryNumber2}"/>
        <Entry Text="{Binding EntryNumber3}"/>
        <Entry Text="{Binding EntryNumber4}"/>
        <Entry Text="{Binding EntryNumber5}"/>
        <Entry Text="{Binding EntryNumber6}"/>
        <Entry Text="{Binding EntryNumber7}"/>
        <Entry Text="{Binding EntryNumber8}"/>
        <Entry Text="{Binding EntryNumber9}"/>
        <Entry Text="{Binding EntryNumber10}"/>
        
    </VerticalStackLayout>
</ContentPage>
    public partial class TestPageViewModel:ObservableObject
    {
        [ObservableProperty]
        public int _EntryNumber1;

        [ObservableProperty]
        public int _EntryNumber2;

        [ObservableProperty]
        public int _EntryNumber3;

        [ObservableProperty]
        public int _EntryNumber4;

        [ObservableProperty]
        public int _EntryNumber5;

        [ObservableProperty]
        public int _EntryNumber6;

        [ObservableProperty]
        public int _EntryNumber7;

        [ObservableProperty]
        public int _EntryNumber8;

        [ObservableProperty]
        public int _EntryNumber9;

        [ObservableProperty]
        public int _EntryNumber10;
    }

Link to public reproduction project repository

N/A

Version with bug

Unknown/Other (please specify)

Last version that worked well

Unknown/Other

Affected platforms

Android

Affected platform versions

Android 31

Did you find any workaround?

No.

Relevant log output

No response

@supershopping supershopping added the t/bug Something isn't working label Oct 16, 2022
@jsuarezruiz jsuarezruiz added legacy-area-perf Startup / Runtime performance area-controls-shell Shell Navigation, Routes, Tabs, Flyout labels Oct 17, 2022
@rmarinho rmarinho added the s/needs-repro Attach a solution or code which reproduces the issue label Oct 17, 2022
@ghost
Copy link

ghost commented Oct 17, 2022

Hi @supershopping. We have added the "s/needs-repro" label to this issue, which indicates that we require steps and sample code to reproduce the issue before we can take further action. Please try to create a minimal sample project/solution or code samples which reproduce the issue, ideally as a GitHub repo that we can clone. See more details about creating repros here: https://github.com/dotnet/maui/blob/main/.github/repro.md

This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

@PureWeen PureWeen removed the area-controls-shell Shell Navigation, Routes, Tabs, Flyout label Oct 17, 2022
@supershopping
Copy link
Author

https://github.com/supershopping/ShellFlyoutLagging

I have created a sample project here to reproduce the issue.

@ghost ghost added s/needs-attention Issue has more information and needs another look and removed s/needs-repro Attach a solution or code which reproduces the issue labels Oct 17, 2022
@PureWeen PureWeen added this to the Backlog milestone Oct 18, 2022
@ghost
Copy link

ghost commented Oct 18, 2022

We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.

@PureWeen PureWeen added platform/android 🤖 and removed s/needs-attention Issue has more information and needs another look labels Oct 18, 2022
@PureWeen
Copy link
Member

@supershopping can you try NET7? Give me more details about the device you are testing on? And maybe attach a video? I'd like to quantify "lagging" here.

@supershopping
Copy link
Author

The lagging is not very obvious when using the original entry control. I just updated the sample project with a custom outlined entry control.
I recorded a video from physical device Samsung S10 Plus. I haven't tried NET7 yet.

Screen_Record_Samsung_S10_Plus.mp4

@mattleibow
Copy link
Member

I think I see the lag when the second page is loaded for the first time... We should be able to run this with the profiler and see what is causing the issues - but we did a whole bunch of work in net7 so testing that will also show different (and hopefully better) results.

@supershopping
Copy link
Author

I did another video based on .NET 7. Could not visually see any improvement on the lagging issue.

Samsung_S10_Plus_NET7.mp4

@angelru
Copy link

angelru commented Nov 30, 2022

I'm having the same problem, this also happens when navigating to a page "with a complex layout" with GoToAysnc.

@angelru
Copy link

angelru commented Dec 8, 2022

@supershopping have you solved it?

@RomanPoschl
Copy link

We are facing same issue.

@dgerding
Copy link

dgerding commented May 5, 2023

Me too.

@XamlTest XamlTest added s/verified Verified / Reproducible Issue ready for Engineering Triage s/triaged Issue has been reviewed labels May 31, 2023
@XamlTest
Copy link

Verified this on Visual Studio Enterprise 17.7.0 Preview 1.0. Repro on Android 13.0-API33 with sample Project:
ShellFlyoutLagging.zip(.NET 7)

Flyout

@007vel
Copy link

007vel commented Jun 2, 2023

Any update on this issue?

@jonathanpeppers
Copy link
Member

I recorded a .speedscope of this app: shellflyout.zip

But looking at the results, it does not appear to be a problem with Shell. There doesn't appear to be anything that accidentally happens twice, etc.

This looks like it is just as slow as it takes to create the new page and add it to the screen. I see about 700ms (number is likely inflated by the attached profiler) of destroying a fragment, creating a new fragment (and all the MAUI controls), and a layout pass:

image

The time take here seems like it is large enough to notice something visually lag.

I do see something we can improve with Entry on Android, but this seems completely unrelated to Shell.

@supershopping (or anyone else) do you think you have similar performance if you just make this page the "main page"?

@supershopping
Copy link
Author

I don't think there is an issue when making it the "main page" since an splash screen can be implemented.
The issue is concerned only when navigating to a complex page from main page or shell flyout. The visual lag is obvious.

@jonathanpeppers
Copy link
Member

@supershopping, right I'm asking if you think this is specific to Shell -- or a general performance issue for all MAUI pages on Android.

jonathanpeppers added a commit to jonathanpeppers/maui that referenced this issue Jun 13, 2023
Context: dotnet#10713

When investigating a customer sample:

* Navigate from a Shell flyout

* To a new page with several `Entry` controls

When profiling on a Pixel 5, it seems one hot path is `Entry.MaxLength`:

    18.52ms (0.22%) microsoft.maui!Microsoft.Maui.Platform.EditTextExtensions.UpdateMaxLength(Android.Widget.EditText,Microsoft.Maui.IEntry)
    16.03ms (0.19%) microsoft.maui!Microsoft.Maui.Platform.EditTextExtensions.UpdateMaxLength(Android.Widget.EditText,int)
    12.16ms (0.14%) microsoft.maui!Microsoft.Maui.Platform.EditTextExtensions.SetLengthFilter(Android.Widget.EditText,int)

* `EditTextExtensions.UpdateMaxLength()` calls
    * `EditText.Text` getter and setter
    * `EditTextExtensions.SetLengthFilter()` calls
        * `EditText.Get/SetFilters()`

What happens is we end up marshaling strings and `IInputFilter[]` back
and forth between C# and Java for every `Entry` on the page.

This seems like a prime candidate to move code from C# to Java. I
tried to just port the code as-is without changing logic -- so we
*should* get the exact same behavior as before.

Since all `Entry`s go through this code path (even ones with a default
value for `MaxLength`), this improves the performance of all `Entry`
and `SearchBar` on Android. With these changes in place, the calls to
`EditTextExtensions.UpdateMaxLength()` are now so fast they are
missing from the trace now, saving ~19ms when navigating to this page.

One note, is I found this was the recommended way to create a mutable
`List<T>` from an array in Java:

    List<InputFilter> currentFilters = new ArrayList<>(Arrays.asList(editText.getFilters()));

https://stackoverflow.com/a/11659198

Makes me appreciate C#! :)
@angelru
Copy link

angelru commented Jun 14, 2023

@jonathanpeppers
The floating menu has a mini delay when any menu item is first touched. I also noticed lags/crashes when navigating to a page with many controls, the animation hangs momentarily until complete. This does not happen if there is a tag on the landing page.

@jonathanpeppers
Copy link
Member

In the trace above, there is about ~230ms of time spent when the menu is touched:

image

Time spent in:

  • microsoft.maui.controls!Microsoft.Maui.Controls.Platform.Compatibility.ShellFlyoutTemplatedContentRenderer.UpdateFlyoutContent()
  • microsoft.maui.controls!Microsoft.Maui.Controls.Platform.Compatibility.ShellFlyoutRecyclerAdapter.OnCreateViewHolder(Android.Views.ViewGroup,int)
  • microsoft.maui.controls!Microsoft.Maui.Controls.Platform.Compatibility.ShellFlyoutRecyclerAdapter.OnBindViewHolder(AndroidX.RecyclerView.Widget.RecyclerView/ViewHolder,int)
  • microsoft.maui.controls!Microsoft.Maui.Controls.Platform.Compatibility.ContainerView.OnMeasure(int,int)

Maybe it could also be improved, but the new page seems to be the worse offender in this example.

jonathanpeppers added a commit to jonathanpeppers/maui that referenced this issue Jun 19, 2023
Context: dotnet#10713
Context: https://github.com/supershopping/ShellFlyoutLagging

In reviewing the above customer sample, there is a noticeable delay on
some devices when you open a Shell flyout for the first time.

This was pretty close to a default Shell setup:

    <Shell Shell.FlyoutBehavior="Flyout">
        <FlyoutItem Title="Main Page">
            <ShellContent Title="Main Page"  Route="MainPage" ContentTemplate="{DataTemplate local:MainPage}" />
        </FlyoutItem>
        <FlyoutItem Title="Test Page">
            <ShellContent Title="Test Page"  Route="TestPage" ContentTemplate="{DataTemplate local:TestPage}" />
        </FlyoutItem>
    </Shell>

Reviewing the code, there was an oddity where we create & replace
a `RecyclerView`'s `LayoutManager`:

    SetLayoutManager(_layoutManager = new ScrollLayoutManager(context, (int)Orientation.Vertical, false));
    SetLayoutManager(new LinearLayoutManager(context, (int)Orientation.Vertical, false));

We think this is likely something that went wrong during a git merge, etc.

In addition to removing code, I create a new `ShellRecyclerView` in Java
that reduces the amount of interop calls from C# to Java. We can do
all the work now in the `ShellRecyclerView`'s constructor:

    var adapter = new ShellFlyoutRecyclerAdapter(ShellContext, OnElementSelected);
    var recyclerView = new ShellRecyclerView(context, adapter);

`dotnet-trace` output shows these changes have a reasonable impact on
a Pixel 5 device:

    Before:
    35.34ms microsoft.maui.controls!Microsoft.Maui.Controls.Platform.Compatibility.ShellFlyoutTemplatedContentRenderer.CreateFlyoutContent()
    After:
    17.64ms microsoft.maui.controls!Microsoft.Maui.Controls.Platform.Compatibility.ShellFlyoutTemplatedContentRenderer.CreateFlyoutContent()

Saving about ~17.7ms of time opening the Shell flyout on Android.
jonathanpeppers added a commit to jonathanpeppers/maui that referenced this issue Jun 20, 2023
Context: dotnet#10713
Context: https://github.com/supershopping/ShellFlyoutLagging

In reviewing the above customer sample, I noticed time spent in:

    79.52ms microsoft.maui!Microsoft.Maui.Handlers.LabelHandler.MapFont(Microsoft.Maui.Handlers.ILabelHandler,Microsoft.Maui.ILabel)
    20.71ms microsoft.extensions.logging.abstractions!Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(Microsoft.Extensions

What warning are we trying to log? It seems like it is impacting the
time it takes for the first Shell flyout to open.

Adding more logging, I found:

    06-19 15:31:19.834  5706  5706 I DOTNET  : Unable to load font 'sans-serif-medium' from assets.
    06-19 15:31:19.840  5706  5706 I DOTNET  : Unable to load font 'sans-serif-medium.ttf' from assets.
    06-19 15:31:19.840  5706  5706 I DOTNET  : Unable to load font 'Fonts/sans-serif-medium.ttf' from assets.
    06-19 15:31:19.841  5706  5706 I DOTNET  : Unable to load font 'fonts/sans-serif-medium.ttf' from assets.
    06-19 15:31:19.841  5706  5706 I DOTNET  : Unable to load font 'sans-serif-medium.otf' from assets.
    06-19 15:31:19.842  5706  5706 I DOTNET  : Unable to load font 'Fonts/sans-serif-medium.otf' from assets.
    06-19 15:31:19.842  5706  5706 I DOTNET  : Unable to load font 'fonts/sans-serif-medium.otf' from assets.

Which is likely just trying to load one of:

* https://github.com/dotnet/maui/blob/0543e03cbe7331ae2c106c67a9eaefc575f75945/src/Controls/src/Core/Shell/BaseShellItem.cs#L517
* https://github.com/dotnet/maui/blob/0543e03cbe7331ae2c106c67a9eaefc575f75945/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellItemRenderer.cs#L248

In 2d354d1, we added support for certain Android system fonts. I think
we should just expand this further to support various Android fonts
like:

    sans-serif-black
    sans-serif-condensed
    sans-serif-condensed-light
    sans-serif-light
    sans-serif-medium

You can use these in Android such as:

    android:fontFamily="sans-serif-medium"

We can load this `Typeface` from C# such as:

    Typeface.Create("sans-serif-medium", TypefaceStyle.Normal)

See: https://stackoverflow.com/a/14344132

In MAUI apps, customers are likely using a `Fonts/` folder, so if they
use these names, they are likely trying to load a system font.

After these changes, I see this result on a Pixel 5:

    Before:
    79.52ms microsoft.maui!Microsoft.Maui.Handlers.LabelHandler.MapFont(Microsoft.Maui.Handlers.ILabelHandler,Microsoft.Maui.ILabel)
    After:
    26.91ms microsoft.maui!Microsoft.Maui.Handlers.LabelHandler.MapFont(Microsoft.Maui.Handlers.ILabelHandler,Microsoft.Maui.ILabel)

So, I think this saves ~52ms of time for the first Shell flyout opening
in this sample.
mattleibow pushed a commit that referenced this issue Jun 20, 2023
Context: #10713
Context: https://github.com/supershopping/ShellFlyoutLagging

In reviewing the above customer sample, I noticed time spent in:

    79.52ms microsoft.maui!Microsoft.Maui.Handlers.LabelHandler.MapFont(Microsoft.Maui.Handlers.ILabelHandler,Microsoft.Maui.ILabel)
    20.71ms microsoft.extensions.logging.abstractions!Microsoft.Extensions.Logging.LoggerExtensions.LogWarning(Microsoft.Extensions

What warning are we trying to log? It seems like it is impacting the
time it takes for the first Shell flyout to open.

Adding more logging, I found:

    06-19 15:31:19.834  5706  5706 I DOTNET  : Unable to load font 'sans-serif-medium' from assets.
    06-19 15:31:19.840  5706  5706 I DOTNET  : Unable to load font 'sans-serif-medium.ttf' from assets.
    06-19 15:31:19.840  5706  5706 I DOTNET  : Unable to load font 'Fonts/sans-serif-medium.ttf' from assets.
    06-19 15:31:19.841  5706  5706 I DOTNET  : Unable to load font 'fonts/sans-serif-medium.ttf' from assets.
    06-19 15:31:19.841  5706  5706 I DOTNET  : Unable to load font 'sans-serif-medium.otf' from assets.
    06-19 15:31:19.842  5706  5706 I DOTNET  : Unable to load font 'Fonts/sans-serif-medium.otf' from assets.
    06-19 15:31:19.842  5706  5706 I DOTNET  : Unable to load font 'fonts/sans-serif-medium.otf' from assets.

Which is likely just trying to load one of:

* https://github.com/dotnet/maui/blob/0543e03cbe7331ae2c106c67a9eaefc575f75945/src/Controls/src/Core/Shell/BaseShellItem.cs#L517
* https://github.com/dotnet/maui/blob/0543e03cbe7331ae2c106c67a9eaefc575f75945/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellItemRenderer.cs#L248

In 2d354d1, we added support for certain Android system fonts. I think
we should just expand this further to support various Android fonts
like:

    sans-serif-black
    sans-serif-condensed
    sans-serif-condensed-light
    sans-serif-light
    sans-serif-medium

You can use these in Android such as:

    android:fontFamily="sans-serif-medium"

We can load this `Typeface` from C# such as:

    Typeface.Create("sans-serif-medium", TypefaceStyle.Normal)

See: https://stackoverflow.com/a/14344132

In MAUI apps, customers are likely using a `Fonts/` folder, so if they
use these names, they are likely trying to load a system font.

After these changes, I see this result on a Pixel 5:

    Before:
    79.52ms microsoft.maui!Microsoft.Maui.Handlers.LabelHandler.MapFont(Microsoft.Maui.Handlers.ILabelHandler,Microsoft.Maui.ILabel)
    After:
    26.91ms microsoft.maui!Microsoft.Maui.Handlers.LabelHandler.MapFont(Microsoft.Maui.Handlers.ILabelHandler,Microsoft.Maui.ILabel)

So, I think this saves ~52ms of time for the first Shell flyout opening
in this sample.
@Dreamescaper
Copy link

@jonathanpeppers
Is is possible to move that work before (or after) the flyout starts closing? I think this lag would be much less noticeable if it wouldn't interrupt the animation.

@jonathanpeppers
Copy link
Member

I think some of this stuff used to happen at startup (.NET 6), and we noticed Shell impacted startup a lot. So now some of the work happens when you open the flyout in .NET 7. It looks like it is the last possible time to do this work.

The PRs I sent so far, save about ~70ms of the first flyout opening (out of about 230ms) on a Pixel 5. Maybe I can just keep looking for more.

@Dreamescaper
Copy link

Dreamescaper commented Jun 22, 2023

I think some of this stuff used to happen at startup (.NET 6), and we noticed Shell impacted startup a lot. So now some of the work happens when you open the flyout in .NET 7. It looks like it is the last possible time to do this work.

Well, that's not really what I meant. Is it possible to "move" that lag right after the click, but before the flyout starts moving? Or right after the flyout stops moving? I.e. 0.1 second earlier or later? So it wouldn't interrupt the animation.

@jonathanpeppers
Copy link
Member

I was looking at both places. When pushing a new page, the slowness appears to be how long that page takes, in general.

If we add a delay there, you would see a white page instead as the flyout hides. @PureWeen have we thought about doing something like that in the past?

jonathanpeppers added a commit to jonathanpeppers/maui that referenced this issue Jun 27, 2023
Context: dotnet#10713

When investigating a customer sample:

* Navigate from a Shell flyout

* To a new page with several `Entry` controls

When profiling on a Pixel 5, it seems one hot path is `Entry.MaxLength`:

    18.52ms (0.22%) microsoft.maui!Microsoft.Maui.Platform.EditTextExtensions.UpdateMaxLength(Android.Widget.EditText,Microsoft.Maui.IEntry)
    16.03ms (0.19%) microsoft.maui!Microsoft.Maui.Platform.EditTextExtensions.UpdateMaxLength(Android.Widget.EditText,int)
    12.16ms (0.14%) microsoft.maui!Microsoft.Maui.Platform.EditTextExtensions.SetLengthFilter(Android.Widget.EditText,int)

* `EditTextExtensions.UpdateMaxLength()` calls
    * `EditText.Text` getter and setter
    * `EditTextExtensions.SetLengthFilter()` calls
        * `EditText.Get/SetFilters()`

What happens is we end up marshaling strings and `IInputFilter[]` back
and forth between C# and Java for every `Entry` on the page.

This seems like a prime candidate to move code from C# to Java. I
tried to just port the code as-is without changing logic -- so we
*should* get the exact same behavior as before.

Since all `Entry`s go through this code path (even ones with a default
value for `MaxLength`), this improves the performance of all `Entry`
and `SearchBar` on Android. With these changes in place, the calls to
`EditTextExtensions.UpdateMaxLength()` are now so fast they are
missing from the trace now, saving ~19ms when navigating to this page.

One note, is I found this was the recommended way to create a mutable
`List<T>` from an array in Java:

    List<InputFilter> currentFilters = new ArrayList<>(Arrays.asList(editText.getFilters()));

https://stackoverflow.com/a/11659198

Makes me appreciate C#! :)

Other changes:

* All platform to update empty strings

In 85b5699, it doesn't say why this check was added, but it was causing
test failures when MaxLength is set to 0.
jonathanpeppers added a commit to jonathanpeppers/maui that referenced this issue Jun 27, 2023
Context: dotnet#10713
Context: https://github.com/supershopping/ShellFlyoutLagging

In reviewing the above customer sample, there is a noticeable delay on
some devices when you open a Shell flyout for the first time.

This was pretty close to a default Shell setup:

    <Shell Shell.FlyoutBehavior="Flyout">
        <FlyoutItem Title="Main Page">
            <ShellContent Title="Main Page"  Route="MainPage" ContentTemplate="{DataTemplate local:MainPage}" />
        </FlyoutItem>
        <FlyoutItem Title="Test Page">
            <ShellContent Title="Test Page"  Route="TestPage" ContentTemplate="{DataTemplate local:TestPage}" />
        </FlyoutItem>
    </Shell>

Reviewing the code, there was an oddity where we create & replace
a `RecyclerView`'s `LayoutManager`:

    SetLayoutManager(_layoutManager = new ScrollLayoutManager(context, (int)Orientation.Vertical, false));
    SetLayoutManager(new LinearLayoutManager(context, (int)Orientation.Vertical, false));

We think this is likely something that went wrong during a git merge, etc.

In addition to removing code, I create a new `ShellRecyclerView` in Java
that reduces the amount of interop calls from C# to Java. We can do
all the work now in the `ShellRecyclerView`'s constructor:

    var adapter = new ShellFlyoutRecyclerAdapter(ShellContext, OnElementSelected);
    var recyclerView = new ShellRecyclerView(context, adapter);

`dotnet-trace` output shows these changes have a reasonable impact on
a Pixel 5 device:

    Before:
    35.34ms microsoft.maui.controls!Microsoft.Maui.Controls.Platform.Compatibility.ShellFlyoutTemplatedContentRenderer.CreateFlyoutContent()
    After:
    17.64ms microsoft.maui.controls!Microsoft.Maui.Controls.Platform.Compatibility.ShellFlyoutTemplatedContentRenderer.CreateFlyoutContent()

Saving about ~17.7ms of time opening the Shell flyout on Android.
mattleibow pushed a commit that referenced this issue Jul 5, 2023
Context: #10713

When investigating a customer sample:

* Navigate from a Shell flyout

* To a new page with several `Entry` controls

When profiling on a Pixel 5, it seems one hot path is `Entry.MaxLength`:

    18.52ms (0.22%) microsoft.maui!Microsoft.Maui.Platform.EditTextExtensions.UpdateMaxLength(Android.Widget.EditText,Microsoft.Maui.IEntry)
    16.03ms (0.19%) microsoft.maui!Microsoft.Maui.Platform.EditTextExtensions.UpdateMaxLength(Android.Widget.EditText,int)
    12.16ms (0.14%) microsoft.maui!Microsoft.Maui.Platform.EditTextExtensions.SetLengthFilter(Android.Widget.EditText,int)

* `EditTextExtensions.UpdateMaxLength()` calls
    * `EditText.Text` getter and setter
    * `EditTextExtensions.SetLengthFilter()` calls
        * `EditText.Get/SetFilters()`

What happens is we end up marshaling strings and `IInputFilter[]` back
and forth between C# and Java for every `Entry` on the page.

This seems like a prime candidate to move code from C# to Java. I
tried to just port the code as-is without changing logic -- so we
*should* get the exact same behavior as before.

Since all `Entry`s go through this code path (even ones with a default
value for `MaxLength`), this improves the performance of all `Entry`
and `SearchBar` on Android. With these changes in place, the calls to
`EditTextExtensions.UpdateMaxLength()` are now so fast they are
missing from the trace now, saving ~19ms when navigating to this page.

One note, is I found this was the recommended way to create a mutable
`List<T>` from an array in Java:

    List<InputFilter> currentFilters = new ArrayList<>(Arrays.asList(editText.getFilters()));

https://stackoverflow.com/a/11659198

Makes me appreciate C#! :)

Other changes:

* All platform to update empty strings

In 85b5699, it doesn't say why this check was added, but it was causing
test failures when MaxLength is set to 0.
jonathanpeppers added a commit to jonathanpeppers/maui that referenced this issue Jul 8, 2023
Context: dotnet#10713
Context: https://github.com/supershopping/ShellFlyoutLagging

In reviewing the above customer sample, there is a noticeable delay on
some devices when you open a Shell flyout for the first time.

This was pretty close to a default Shell setup:

    <Shell Shell.FlyoutBehavior="Flyout">
        <FlyoutItem Title="Main Page">
            <ShellContent Title="Main Page"  Route="MainPage" ContentTemplate="{DataTemplate local:MainPage}" />
        </FlyoutItem>
        <FlyoutItem Title="Test Page">
            <ShellContent Title="Test Page"  Route="TestPage" ContentTemplate="{DataTemplate local:TestPage}" />
        </FlyoutItem>
    </Shell>

Reviewing the code, there was an oddity where we create & replace
a `RecyclerView`'s `LayoutManager`:

    SetLayoutManager(_layoutManager = new ScrollLayoutManager(context, (int)Orientation.Vertical, false));
    SetLayoutManager(new LinearLayoutManager(context, (int)Orientation.Vertical, false));

We think this is likely something that went wrong during a git merge, etc.

In addition to removing code, I create a new `ShellRecyclerView` in Java
that reduces the amount of interop calls from C# to Java. We can do
all the work now in the `ShellRecyclerView`'s constructor:

    var adapter = new ShellFlyoutRecyclerAdapter(ShellContext, OnElementSelected);
    var recyclerView = new ShellRecyclerView(context, adapter);

`dotnet-trace` output shows these changes have a reasonable impact on
a Pixel 5 device:

    Before:
    35.34ms microsoft.maui.controls!Microsoft.Maui.Controls.Platform.Compatibility.ShellFlyoutTemplatedContentRenderer.CreateFlyoutContent()
    After:
    17.64ms microsoft.maui.controls!Microsoft.Maui.Controls.Platform.Compatibility.ShellFlyoutTemplatedContentRenderer.CreateFlyoutContent()

Saving about ~17.7ms of time opening the Shell flyout on Android.
jonathanpeppers added a commit to jonathanpeppers/maui that referenced this issue Sep 14, 2023
Context: dotnet#10713
Context: https://github.com/supershopping/ShellFlyoutLagging

In reviewing the above customer sample, there is a noticeable delay on
some devices when you open a Shell flyout for the first time.

This was pretty close to a default Shell setup:

    <Shell Shell.FlyoutBehavior="Flyout">
        <FlyoutItem Title="Main Page">
            <ShellContent Title="Main Page"  Route="MainPage" ContentTemplate="{DataTemplate local:MainPage}" />
        </FlyoutItem>
        <FlyoutItem Title="Test Page">
            <ShellContent Title="Test Page"  Route="TestPage" ContentTemplate="{DataTemplate local:TestPage}" />
        </FlyoutItem>
    </Shell>

Reviewing the code, there was an oddity where we create & replace
a `RecyclerView`'s `LayoutManager`:

    SetLayoutManager(_layoutManager = new ScrollLayoutManager(context, (int)Orientation.Vertical, false));
    SetLayoutManager(new LinearLayoutManager(context, (int)Orientation.Vertical, false));

We think this is likely something that went wrong during a git merge, etc.

In addition to removing code, I create a new `ShellRecyclerView` in Java
that reduces the amount of interop calls from C# to Java. We can do
all the work now in the `ShellRecyclerView`'s constructor:

    var adapter = new ShellFlyoutRecyclerAdapter(ShellContext, OnElementSelected);
    var recyclerView = new ShellRecyclerView(context, adapter);

`dotnet-trace` output shows these changes have a reasonable impact on
a Pixel 5 device:

    Before:
    35.34ms microsoft.maui.controls!Microsoft.Maui.Controls.Platform.Compatibility.ShellFlyoutTemplatedContentRenderer.CreateFlyoutContent()
    After:
    17.64ms microsoft.maui.controls!Microsoft.Maui.Controls.Platform.Compatibility.ShellFlyoutTemplatedContentRenderer.CreateFlyoutContent()

Saving about ~17.7ms of time opening the Shell flyout on Android.
jonathanpeppers added a commit to jonathanpeppers/maui that referenced this issue Jan 11, 2024
Context: dotnet#10713
Context: https://github.com/supershopping/ShellFlyoutLagging

In reviewing the above customer sample, there is a noticeable delay on
some devices when you open a Shell flyout for the first time.

This was pretty close to a default Shell setup:

    <Shell Shell.FlyoutBehavior="Flyout">
        <FlyoutItem Title="Main Page">
            <ShellContent Title="Main Page"  Route="MainPage" ContentTemplate="{DataTemplate local:MainPage}" />
        </FlyoutItem>
        <FlyoutItem Title="Test Page">
            <ShellContent Title="Test Page"  Route="TestPage" ContentTemplate="{DataTemplate local:TestPage}" />
        </FlyoutItem>
    </Shell>

Reviewing the code, there was an oddity where we create & replace
a `RecyclerView`'s `LayoutManager`:

    SetLayoutManager(_layoutManager = new ScrollLayoutManager(context, (int)Orientation.Vertical, false));
    SetLayoutManager(new LinearLayoutManager(context, (int)Orientation.Vertical, false));

We think this is likely something that went wrong during a git merge, etc.

In addition to removing code, I create a new `ShellRecyclerView` in Java
that reduces the amount of interop calls from C# to Java. We can do
all the work now in the `ShellRecyclerView`'s constructor:

    var adapter = new ShellFlyoutRecyclerAdapter(ShellContext, OnElementSelected);
    var recyclerView = new ShellRecyclerView(context, adapter);

`dotnet-trace` output shows these changes have a reasonable impact on
a Pixel 5 device:

    Before:
    35.34ms microsoft.maui.controls!Microsoft.Maui.Controls.Platform.Compatibility.ShellFlyoutTemplatedContentRenderer.CreateFlyoutContent()
    After:
    17.64ms microsoft.maui.controls!Microsoft.Maui.Controls.Platform.Compatibility.ShellFlyoutTemplatedContentRenderer.CreateFlyoutContent()

Saving about ~17.7ms of time opening the Shell flyout on Android.
@Lug16
Copy link

Lug16 commented Mar 7, 2024

Hi guys... I'm also facing the same lag with MAUI .net 8 (Android and iOS devices). Any considerations for this?

@Hardikzinzala
Copy link

Hi guys... I'm also facing the same lag with MAUI .net 8 (Android and iOS devices). If anyone have any update regarding this issue so inform me.

@Eilon Eilon added the t/perf The issue affects performance (runtime speed, memory usage, startup time, etc.) label May 10, 2024
@PureWeen PureWeen added the area-controls-shell Shell Navigation, Routes, Tabs, Flyout label May 31, 2024
@PureWeen PureWeen added the p/2 Work that is important, but is currently not scheduled for release label Jun 19, 2024
@PureWeen PureWeen modified the milestones: Backlog, .NET 10 Planning Sep 6, 2024
@PureWeen
Copy link
Member

PureWeen commented Sep 6, 2024

Does this happen when you use a FlyoutPAge vs Shell?

FlyoutPage is a bit of a newer build than Shell and we'd like to convert Shell over to FlyoutPage at some point

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-controls-shell Shell Navigation, Routes, Tabs, Flyout delighter-sc legacy-area-perf Startup / Runtime performance p/2 Work that is important, but is currently not scheduled for release platform/android 🤖 s/triaged Issue has been reviewed s/verified Verified / Reproducible Issue ready for Engineering Triage t/bug Something isn't working t/perf The issue affects performance (runtime speed, memory usage, startup time, etc.)
Projects
None yet
Development

No branches or pull requests