在实际的项目开发过程中,应用的性能优化是一个永恒的话题,也是开发者群里最常讨论的话题之一,我在之

前的公司做 wp项目时,也遇到过性能的瓶颈。当页面中加载的内容越来越多时,内存涨幅非常明显(特别是

一些壁纸类的应用,当用户向下滑动列表加载更多),当内存超过 120MB 有些机型的发热明显,如果内存继

续上涨,发热事小,内存泄露后,系统会直接关闭应用。

在 wp 系统中自带的 ListBox 等控件也提供内存虚拟化,但是如果用得不好,可能会破坏虚拟化。

微软 MSDNWindows Phone 的应用性能注意事项

MSDN 部分摘抄:

在Silverlight中,为了将数据显示给用户,我们需要加载数据和绑定数据,但是哪个会导致性能问题呢?答案是:根据你的数据类型以及界面(UI)的复杂性而定。

通常,加载数据可以在UI线程或者后台线程中实现,数据存在的形式也不经相同,有的序列化为二进制数据,有的序列化为XML文件,有的则是图片形式存在等等。而数据绑定又有三种不同的绑定形式:一次绑定(One  Time)、单向绑定(One Way)和双向绑定(Two  Way)。

这里简单介绍下什么是VSP(VirtualizingStackPanel)

将内容排列和虚拟化在一行上,方向为水平或垂直。“虚拟化”是指一种技术,通过该技术,可根据屏幕上所显示的项来从大量数据项中生成user  interface (UI) 元素的子集。仅当 StackPanel 中包含的项控件创建自己的项容器时,才会在该面板中发生虚拟化。 可以使用数据绑定来确保发生这一过程。 如果创建项容器并将其添加到项控件中,则与 StackPanel 相比,VirtualizingStackPanel 不能提供任何性能优势。

VirtualizingStackPanel 是 ListBox 元素的默认项宿主。 默认情况下,IsVirtualizing 属性设置为 true。当 IsVirtualizing 设置为 false 时,VirtualizingStackPanel 的行为与普通 StackPanel 一样。

我们可以将VSP理解为当需要时,VSP会生成容器对象,而当对象不在可视范围内时,VSP就把这些对象从内存中移除。当ListBox很想当大数据量的项目时,我们不需要将不在可视范围中的对象加载到内存中,从而解决了内存的问题。另外VSP有一个属性CacheMode设置缓存表示形式,默认设为Standard。当我们需要循环显示,可以将其设置为Recycling。

在ListBox中使用VSP来进行数据虚拟化时,我们需要注意以下几点:

确保在DataTemplate 中的容器(如Grid)大小固定

在数据对象可以提供相应值时,尽量避免使用复杂的转换器(Converter)

不要在ListBox中内嵌ListBox

加入动画验证 ListBox 项的动态创建和删除

为了验证 ListBox 在列表部分内容滑入、滑出屏幕可视区域时,内容是动态创建和删除的,我在 ListBox 的

ItemTemplate 模版中给每个项加入动画,并且通过  <EventTrigger RoutedEvent="StackPanel.Loaded">

进行触发,当滑动列表时,运行效果:

当加载 200条数据时,看到内存检测才 22MB,实际如果没有虚拟化,内存可达150MB 以上。

 Demo 的部分代码介绍(在接下来的 文章二 的列表加载是相似的逻辑)

1)首先自定义一个 News 类,包含两个字段,一个 Title ,一个 Picture:

 public class News : System.ComponentModel.INotifyPropertyChanged
{
string title;
public string Title
{
get
{
return title;
}
set
{
if (value != title)
{
title = value;
NotifyPropertyChanged("Titlte");
}
}
} string photo;
public string Photo
{
get
{
return photo;
}
set
{
if (value != photo)
{
photo = value;
NotifyPropertyChanged("Photo");
}
}
} public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
}

2)在工程的根目录下创建一个 Image 文件夹,里面放 10张示例新闻配图。

3)MainPage 中只需要关注两个控件,一个是 页面顶部显示内存的:

  <TextBlock x:Name="txtMemory" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/>

第二个是显示新闻列表的:
它的默认 ItemsPanelTemplate 是 VirtulizingStackPanel。在有些交换中,需要去掉 ListBox

的虚拟化功能,就可以把这个 VirtulizingStackPanel 换成 StackPanel 。

<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>

在 ListBox 的 ItemTemplate 中放一个触发器,当 StackPanel 触发 Loaded 事件的时候,播放预定义动画(在 Blend 中设计的动画)。

从而可以判断每次当 ListBox 的 Item 创建完成后,就会触发一次这个动画。StackPanel 中放一个 TextBlock 和一个 Image,用来

显示 News 的 Title 和 Picture 字段。

 <ListBox.ItemTemplate>
<DataTemplate>
<StackPanel x:Name="stack" Orientation="Horizontal" Margin="10,30,0,0">
<StackPanel.Triggers>
<EventTrigger RoutedEvent="StackPanel.Loaded">
<BeginStoryboard>
<Storyboard x:Name="Storyboard1">
<!--略.....-->
</StackPanel>
</DataTemplate>

ListBox 的完整 xaml:

            <ListBox x:Name="listbox" ItemsSource="{Binding}" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel x:Name="stack" Orientation="Horizontal" Margin="10,30,0,0">
<StackPanel.Triggers>
<EventTrigger RoutedEvent="StackPanel.Loaded">
<BeginStoryboard>
<Storyboard x:Name="Storyboard1">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationX)" Storyboard.TargetName="stack">
<EasingDoubleKeyFrame KeyTime="0" Value="-180"/>
<EasingDoubleKeyFrame KeyTime="0:0:3" Value="0">
<EasingDoubleKeyFrame.EasingFunction>
<QuinticEase EasingMode="EaseOut"/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationY)" Storyboard.TargetName="stack">
<EasingDoubleKeyFrame KeyTime="0" Value="106"/>
<EasingDoubleKeyFrame KeyTime="0:0:3" Value="0">
<EasingDoubleKeyFrame.EasingFunction>
<QuinticEase EasingMode="EaseOut"/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationZ)" Storyboard.TargetName="stack">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:3" Value="0">
<EasingDoubleKeyFrame.EasingFunction>
<QuinticEase EasingMode="EaseOut"/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateX)" Storyboard.TargetName="stack">
<EasingDoubleKeyFrame KeyTime="0" Value="246"/>
<EasingDoubleKeyFrame KeyTime="0:0:3" Value="0">
<EasingDoubleKeyFrame.EasingFunction>
<QuinticEase EasingMode="EaseOut"/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleX)" Storyboard.TargetName="stack">
<EasingDoubleKeyFrame KeyTime="0" Value="0.4"/>
<EasingDoubleKeyFrame KeyTime="0:0:3" Value="1">
<EasingDoubleKeyFrame.EasingFunction>
<QuinticEase EasingMode="EaseOut"/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleY)" Storyboard.TargetName="stack">
<EasingDoubleKeyFrame KeyTime="0" Value="0.4"/>
<EasingDoubleKeyFrame KeyTime="0:0:3" Value="1">
<EasingDoubleKeyFrame.EasingFunction>
<QuinticEase EasingMode="EaseOut"/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</StackPanel.Triggers>
<StackPanel.Resources> </StackPanel.Resources> <StackPanel.RenderTransform>
<CompositeTransform/>
</StackPanel.RenderTransform>
<StackPanel.Projection>
<PlaneProjection/>
</StackPanel.Projection>
<Image VerticalAlignment="Top" Source="{Binding Photo}" Width="150"/>
<TextBlock Text="{Binding Title}" Width="250" Foreground="Wheat" FontSize="25" Margin="10,0,0,0" TextWrapping="Wrap"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

4)创建示例新闻,通过 Random 类控制每条新闻的 标题长度和 配图是 随机的:

        #region 示例数据源
Random rd = new Random();
void LoadNews(int Length)
{
for (int i = ; i < Length; i++)
{
NewsList.Add(new News
{
Title = "不过需要注意的是——为了彰显自己对Kevin Kelly多年追随,而非跟风所为,
你最好能够熟记百度百科上有关他生平的介绍,如果记不全也没关系,知道《黑客帝国》
主创人员都被要求看《失控》这件事,就足以应付一干人等了。".Substring(, rd.Next(,)),
Photo = "/Images/0" + rd.Next(, ) + ".png"
});
};
}
#endregion

在 MainPage 中自定义一个 DispathcerTimer 对象,每隔两秒,把当前应用所占的内存打印到顶部:

       #region 内存使用情况

        static System.Windows.Threading.DispatcherTimer dispacherTimer;
void CheckMemory()
{
dispacherTimer = new System.Windows.Threading.DispatcherTimer();
dispacherTimer.Interval = TimeSpan.FromSeconds();
dispacherTimer.Tick += new EventHandler(dispacherTimer_Tick);
dispacherTimer.Start();
} static string total = "DeviceTotalMemory";
static string current = "ApplicationCurrentMemoryUsage";
static string peak = "ApplicationPeakMemoryUsage"; static long totlaBytes;
static long currentBytes;
static long peakBytes; void dispacherTimer_Tick(object sender, EventArgs e)
{
// 获取设备的总内存
totlaBytes = (long)Microsoft.Phone.Info.DeviceExtendedProperties.GetValue(total); // 获取应用当前占用内存
currentBytes = (long)Microsoft.Phone.Info.DeviceExtendedProperties.GetValue(current); // 获取内存占用的峰值
peakBytes = (long)Microsoft.Phone.Info.DeviceExtendedProperties.GetValue(peak); txtMemory.Text = string.Format("当前:{0:F2}MB; 峰值:{1:F2}MB; 总:{2:F2}MB;",
currentBytes / ( * 1024.0), peakBytes / ( * 1024.0), totlaBytes / ( * 1024.0));
}
#endregion

5)初始化 MainPage 中的 列表等操作:

        ObservableCollection<News> NewsList = new ObservableCollection<News>();//{ get; set; }
// 构造函数
public MainPage()
{
InitializeComponent(); this.Loaded += MainPage_Loaded;
} void MainPage_Loaded(object sender, RoutedEventArgs e)
{ // 给 NewsList 加载两百条新闻
LoadNews(); // 设置当前页面的上下文
this.DataContext = NewsList; // 开始打印内存
CheckMemory();
}

运行上面的代码,看到顶部的内存占用很少。当把 VirtualizingStackPanel 换成 StackPanel 时:

<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>

变成:

 <ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>

运行工程,靠,内存直接上 200MB,是之前的约 20倍,如果在 512MB 的设备上,会直接被系统杀掉。

并且当滑动时,也不会触发 Loaded 的动画:

当然,如果 ListBox 使用不当也会破坏它的虚拟化,比如有的项目中,把 ListBox 放在 一个 ScrollViewer

中,虚拟化就不起作用了,确实有些这种情况,并且开发者并没有注意到这个问题所在。比如有的朋友在

ScrollViewer 里,上面放一个 幻灯片,下面放一个 ListBox(或者 ItemsControl 控件):

因为 ListBox 的虚拟化功能不被破坏是需要一定条件的,在后面的文章会介绍如何如何模拟 ListBox 实现虚拟化功能,

其实原理很简单,就是在列表中的项,不在屏幕的可视区域内时,动态的隐藏或者删除,当滑动回来时,再重新

显示或创建。

本文 demo 下载

Windows Phone 性能优化(一)的更多相关文章

  1. Windows Phone 性能优化(二)

    这篇文章的 demo 是在 (一)的基础上进行的调整,逻辑基本相似.本文只列和 上一篇出不同的代码. 为了实现自定义的虚拟化,把上一篇文章的 ListBox 换成 ScrollViewer + Ite ...

  2. Windows 7 性能优化

    1."计算机" 2.右键>"属性" 3."高级系统设置">"高级" 4."性能"> ...

  3. Windows Phone性能优化建议

    使用background thread解码图片 在Windows Phone中支持的图片格式有jpg和png,微软建议使用jpg格式的图片,因为jpg格式的图片在解码速度上要比png快.那么我们怎么来 ...

  4. Windows phone应用开发[21]-图片性能优化

    在windows phone 中常在列表中会常包含比较丰富文字和图片混排数据信息. 针对列表数据中除了谈到listbox等控件自身数据虚拟化问题外.虽然wp硬件设备随着SDK 8.0 发布得到应用可使 ...

  5. windows服务器下iis的性能优化 服务器

    IIS性能优化 1.调整IIS高速缓存 HKEY_LOCAL_MACHINE SystemCurrentControlSetServicesInetInfoParametersMemoryCacheS ...

  6. Windows Server2008 R2性能优化方法

    经常使用的是Windows 2008R2企业版的服务器,简单总结一下性能优化的方法 ========================================================== ...

  7. Web性能优化:What? Why? How?

    为什么要提升web性能? Web性能黄金准则:只有10%~20%的最终用户响应时间花在了下载html文档上,其余的80%~90%时间花在了下载页面组件上. web性能对于用户体验有及其重要的影响,根据 ...

  8. Web性能优化:图片优化

    程序员都是懒孩子,想直接看自动优化的点:传送门 我自己的Blog:http://cabbit.me/web-image-optimization/ HTTP Archieve有个统计,图片内容已经占到 ...

  9. 02.SQLServer性能优化之---牛逼的OSQL----大数据导入

    汇总篇:http://www.cnblogs.com/dunitian/p/4822808.html#tsql 上一篇:01.SQLServer性能优化之----强大的文件组----分盘存储 http ...

随机推荐

  1. 完全理解Gson(2):Gson序列化

    通过调用 Gson API 可以把 Java 对象转换为 JSON 格式的字符串(项目主页).在这篇文章中,我们将会讲到如何通过 Gson 默认实现和自定义实现方式,将 Java  对象转换为 JSO ...

  2. 世界围棋人机大战、顶峰对决第二战:围棋世界冠军Lee Sedol(李世石,围棋职业九段)对战Google DeepMind AlphaGo围棋程序,AlphaGo再次胜出!

    感觉在哔哩哔哩(bilibili)上看比赛直播比较好,一直可以看到比赛的直播画面,还能听到英文解说和中文主持人的解说.YouTube上是不错,但是一方面爬梯子比较卡,另一方面只能听到英文解说. 韩国著 ...

  3. 神经网络可以拟合任意函数的视觉证明A visual proof that neural nets can compute any function

    One of the most striking facts about neural networks is that they can compute any function at all. T ...

  4. Navicat工具里的empty table和truncate table的区别

    如图: 相同点:都会清空数据表里的所有数据 不同点:empty table是清空表里的数据:truncate table是删除表,然后再创建这张表 意义:对于主索引自动增加的情况,empty清表后,新 ...

  5. 高性能WEB开发:DOM编程

    我们知道,DOM是用于操作XML和HTML文档的应用程序接口,用脚本进行DOM操作的代价很昂贵.有个贴切的比喻,把DOM和Javascript(这里指ECMscript)各自想象为一个岛屿,它们之间用 ...

  6. 【论文笔记】Leveraging Datasets with Varying Annotations for Face Alignment via Deep Regression Network

    參考文献: Zhang J, Kan M, Shan S, et al. Leveraging Datasets With Varying Annotations for Face Alignment ...

  7. dTree无限级文件夹树和JQuery同步Ajax请求

    曾经都是用JQuery对树的支持来实现文件夹树的,近来闲来无事就弄了下dTree,感觉其无限级文件夹还是挺好的,并且它的使用也比較方便,基本上就是先把要用的js文件即dtree.js和css文件dtr ...

  8. 自己定义View步骤

     概述 Android已经为我们提供了大量的View供我们使用,可是可能有时候这些组件不能满足我们的需求,这时候就须要自己定义控件了.自己定义控件对于刚開始学习的人总是感觉是一种复杂的技术. 由于 ...

  9. java使用链栈实现数制转换

    java实现链栈在前面有所介绍:http://www.cnblogs.com/lixiaolun/p/4644141.html 将前面java实现链栈的代码稍作修改: package linkedst ...

  10. 自己主动化測试使用mybatis更新数据库信息实例

    代码例如以下: mybatis配置文件: <? xml version="1.0" encoding="UTF-8"?> <!DOCTYPE ...