立即显示启动画面
Posted
技术标签:
【中文标题】立即显示启动画面【英文标题】:Show a splash screen at once 【发布时间】:2011-08-10 06:00:44 【问题描述】:我们正在处理 WinForm 应用程序的慢启动问题(它是一个大型应用程序并且有许多控件程序集)。控件程序集是 DevComponents。 Ngen 被用于防止 jit 编译,但加载时间只是减少了一点。
应用程序有一个启动画面,但它仅在应用程序启动后 12 秒内出现。有什么方法可以一次显示启动画面吗?
我们目前的建议是创建一个带有闪屏的轻量级应用,在单独的进程中运行主应用,并在主应用初始化完成后关闭轻量级应用。
【问题讨论】:
如果 NGEN 优化不显着,我建议放弃它并保留所有 JIT 优势。 你如何显示启动画面?能否提供代码示例? 使用单独的表单实现。 【参考方案1】:你永远不会得到一个 .NET 应用程序的启动屏幕来立即显示。 即使您已经对程序集进行了 NGen 以消除 JIT 编译时间,您仍然必须等待所有 .NET Framework DLL 加载到内存中。这是一个相当大的框架,在冷启动时加载需要相当长的时间。你真的无能为力。
Microsoft 已尝试尽可能减轻这种痛苦。 WindowsFormsApplicationBase
class(它在 Microsoft.VisualBasic
命名空间中定义,但不要被吓跑;它完全可以在 C# 应用程序中使用)提供了一种用于显示初始屏幕的内置机制。您所要做的就是将其SplashScreen
property 设置为适当的形式,其他一切都在幕后处理。即使在冷启动情况下,它也已针对最大响应时间进行了大量优化,但它仍然不会即时。
您唯一的其他选择是在非托管代码中编写小型包装器,其唯一目的是尽快抛出初始屏幕,然后调用您的 .NET 应用程序开始自行启动。当然,这里越轻越好。 C++ 是一种选择,但 C 可能是更好的选择。您需要尽量减少必须链接的外部库的数量,因此像 MFC 或 Qt 这样的框架肯定已经过时了:您需要直接针对 Windows API。
Visual Studio 团队在 VS 2010 中做了类似的事情。他们对这个过程有一个非常有趣的解释available on their blog:
尽管 Visual Studio 2010 将 WPF 用于其主窗口,但将 WPF 用于启动屏幕需要我们等待 CLR 和 WPF 初始化,然后才能在屏幕上绘制单个像素。虽然我们在 .Net 4.0 中对 CLR 和 WPF 的启动速度进行了一些巨大的改进,但它仍然无法完全匹配原始 Win32 的性能。因此,我们选择使用原生 C++ 代码和 Win32 作为初始屏幕。
但我不会在这里花太多时间。由于您通常应该为用户提供打开和关闭启动屏幕的选项,并且大多数用户会选择关闭它,所以很多人不太可能一开始就看到它。任何好的优化分析器都会告诉你它不值得优化。
【讨论】:
【参考方案2】:有关使用 C++ 编写并使用本机 Windows API 的轻量级、可重复使用的初始屏幕组件,请参阅 Stefan Olson's splash screen。它采用 Cody Gray 提到的方法,即从一个单独的非 CLR 进程开始,然后加载主 CLR 应用程序。
我必须在上一份工作中实现完全相同的东西,我可以确认该方法效果很好 - 用户单击开始菜单中的程序图标和出现启动屏幕之间的时间只有几毫秒,所以感觉“瞬间”。
【讨论】:
您可以考虑在您的帖子中总结链接内容【参考方案3】:我会说同意你目前的建议。
除非 Winforms 应用程序完全加载到内存中,否则它不会显示,并且由于需要时间,我建议使用一个加载器应用程序来显示启动并执行主要的大型应用程序(作为子进程)。
在巨大的应用程序中,加载时终止父应用程序。顺便说一句,这不会杀死孩子。
【讨论】:
小型 WinForms 应用程序加载表单的速度不太可能明显快于 大型 WinForms 应用程序加载相同的表单。我们的想法是在任何一种情况下首先加载启动屏幕表单,而不是从显示主表单开始,而是让主表单显示启动屏幕。 嗯?除了 CLR 之外,没有任何依赖项的小型 WinForms 应用程序的加载速度比具有大量依赖项的应用程序加载速度要快得多。您是什么意思“首先显示主窗体并让主窗体显示启动画面”?你能详细说明一下吗? 阅读我的 cmets 到 Jason Moore 的回答。在需要之前,CLR 不会加载依赖项。除非您实际使用来自这些外部程序集的代码,否则不会受到任何处罚,无论它们是否被您的应用程序引用。我的意思是,您可以在Main
方法的开头显示一个不依赖任何外部依赖项的简单启动屏幕表单,并且它在空的 WinForms 应用程序中的加载速度与在一个空的 WinForms 应用程序中加载一样快一百个依赖。这些依赖项在您请求它们之前不会被加载,这就是 JIT 编译的魔力。【参考方案4】:
我过去所做的快速而肮脏的方法是拥有两个应用程序,您的主应用程序和您的闪屏应用程序。主应用程序是正常的,包含所有重量级 DLL、控件等。闪屏应用程序只是一个应用程序和一个 Windows 窗体,从项目中删除了所有不必要的引用 DLL - 事实上,您可以更进一步,并且使用更轻量级的 .NET 框架,如紧凑的 .NET 框架或早期版本的 .NET 框架,如 1.X 或 2.0。
您要做的是启动启动画面应用程序并立即显示单个启动画面。有一个 Windows 表单计时器(设置为 100 毫秒)并启用计时器作为表单加载事件的最后一行。当计时器触发时,禁用计时器,然后启动您的真实应用程序。请参阅this discussion 了解如何执行此操作。现在主应用程序将开始启动。一旦应用程序完成初始化并可能加载第一个表单,然后让主应用程序终止启动屏幕应用程序。有关杀死应用程序的更多信息here。
【讨论】:
目前还不清楚这实际上如何改善这种情况。即使您有一个相对较小的 .NET 应用程序,您仍然需要等待 .NET DLL 和 CLR 加载。更重要的是,CLR 足够聪明,不会加载您不使用的程序集/引用的 DLL。如果您的初始屏幕表单不使用在这些参考 DLL 中定义的任何控件,并且这是您在屏幕上显示的第一件事,那么它在“大”应用程序中的速度与在“小”应用程序中的速度一样快. 另外,我非常建议放弃使用计时器的建议。用户不想仅仅因为您向他们展示广告牌而等待更长时间您的应用程序加载。我不反对加载时间很长的应用程序的启动画面,但是添加延迟(即使只有 100 毫秒)完全没有意义,几乎令人反感。 我的意思是,闪屏应用程序不必引用原始帖子中提到的任何大型 DevComponents dll。那并以较小的 .NET 框架为目标将使其更快。我假设只有主应用程序需要引用(并因此加载)所有似乎使原始程序在 12 秒或更长时间内加载的 DevComponents dll。 1) 除非需要,否则不会加载 DevComponents DLL。如果启动屏幕表单不使用它们(它不应该),那么它们不会被加载只是为了显示启动屏幕。它们只会在主窗体加载时加载,希望 在 启动屏幕启动之后。 2) 紧凑的框架适用于移动设备,而不是 Windows 的桌面版本,所以这不是初学者。不可否认,4.0 版比 2.0 版小。不确定 1.0,但要求 两个 版本的固件与您的应用一起安装只是为了启动屏幕没有多大意义。以上是关于立即显示启动画面的主要内容,如果未能解决你的问题,请参考以下文章
android studio项目启动画面无法正常关闭,应用程序运行完美但退出应用程序启动画面显示后,为啥[关闭]