Application.Current.MainPage与Navigation.PushAsync()与Navigation.PushModalAsync()的区别

4

我正在开发一个Xamarin Forms App(PCL),不需要返回按钮。该应用程序有三个页面:一个SplashScreenPage用于加载数据,一个LoginPage如果用户需要登录以及一个RootPage,它是一个MasterDetailPage。我想知道在页面之间导航的最佳选项(例如避免内存泄漏):

第一种解决方案:

Application.Current.MainPage = new ContentPage();

第二种解决方案:

Navigation.PushAsync(new NavigationPage(new ContentPage()));

那么

NavigationPage.SetHasNavigationBar(this, false);
ClearNavigationStack();

第三种解决方案

await Navigation.PushModalAsync(new NavigationPage(new ContentPage()));

那么

NavigationPage.SetHasNavigationBar(this, false);
ClearModalStack();

{btsdaf} - Will Decker
{btsdaf} - Nicolas Bodin
{btsdaf} - Will Decker
谢谢!你知道如何清理上一页吗?使用第二种解决方案,我可以使用导航对象的RemovePage方法,但我不知道如何使用第一种解决方案。 - Nicolas Bodin
我被建议使用垃圾回收器的Collect方法来处理不再使用的元素(然后清除之前的页面)。你认为这个解决方案怎么样,@WillDecker? - Nicolas Bodin
2个回答

6

正如@will-decter所描述的那样,如果正确实现,则上述任何解决方案都不会导致内存泄漏。

您可以使用上述任何解决方案。通常情况下,您无需执行任何操作来清除上一页。垃圾回收器会自动为您完成此操作(不是立即进行,而是在某些条件基础上经过一段时间后)。考虑第一个解决方案:

Application.Current.MainPage = new Page1();

现在,如果您像这样分配新页面。
Application.Current.MainPage = new Page2();

作为Page1不再使用,GC在一段时间后会收集Page1对象以回收一些内存。您也可以使用GC.Collect()立即强制GC回收内存,但由于GC.Collect()操作很昂贵,因此建议您不要从代码中调用它,而是优化您的代码,使其不需要调用它。
但是,如果您的页面订阅了事件并且没有取消订阅,则在这种情况下,即使您调用GC.Collect()方法,GC也无法收集该页面。因此,请确保取消订阅任何已订阅的事件,如下所示:
public class MainPage : ContentPage
{
     protected override void OnAppearing()
     {
         base.OnAppearing();
         MyEntry.TextChanged += MyEntry_TextChanged;
     }

     protected override void OnDisappearing()
     {
         base.OnDisappearing();
         MyEntry.TextChanged -= MyEntry_TextChanged;
     }
}

(如果事件是从xaml订阅的,则可以跳过取消订阅,因为在这种情况下,Xamarin Form使用WeakReference)

这将确保在需要时,MainPage被GC收集。

我建议阅读此文章以更好地了解Xamarin中GC的工作原理以及如何提高应用程序的性能。

https://developer.xamarin.com/guides/cross-platform/deployment,_testing,_and_metrics/memory_perf_best_practices/


好的,谢谢!我从我的应用程序中删除了Collect方法的调用,并取消订阅了不同页面订阅的事件。只有最后一件事,你认为这个答案怎么样:https://forums.xamarin.com/discussion/comment/303771#Comment_303771 它正确吗? - Nicolas Bodin
1
不,那不是正确的答案。我已经编辑了代码以便更好地理解。简而言之,GC.Collect()只是尝试在一些时间后自动清理对象。而且GC.Collect()也不能保证它会清理你之前的页面。 - Pranshu Aggarwal
谢谢,你帮了我很多 <3 - Gonçalo Garrido

4

我认为在页面间导航的最佳选项是

在 App.cs 文件中

public App()
{
    var nav = new NavigationPage(new Login());
    MainPage = nav;
}

登录成功后点击:

void OnLoginClicked(object sender, EventArgs e)
{
    Application.Current.MainPage = new NavigationPage(new MasterPage());
}

不需要添加“NavigationPage.SetHasNavigationBar(this, false);”。

我知道使用那个解决方案时不需要添加 NavigationPage.SetHasNavigationBar(this, false); ... 我在想如何清除之前的页面... - Nicolas Bodin
Application.Current.MainPage = new NavigationPage(new MasterPage()); 会重置导航栈。 - Pratik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接