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

前的公司做 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. Labeled Faces in the Wild 人脸识别数据集 部分训练数据

    development training set Note: images displayed are original (non-aligned/funneled) images. match pa ...

  2. HDU1230 火星A+B

    火星A+B Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Subm ...

  3. SQL中truncate table和delete的区别

    truncate table table_name delete from table_name drop table table_name truncate table在功能上与不带where子句的 ...

  4. OpenCV中图像融合

         准备2副背景图像,注意图像黑色的部分,是作为mask用的,我们会用灰度图的方式打开它们,这时黑色的部分值为0,则图像融合时候,可以把第二幅图像在黑色的部分显示出来. 代码非常简单,注意就是图 ...

  5. Tensorflow之调试(Debug) && tf.py_func()

    Tensorflow之调试(Debug)及打印变量 tensorflow调试tfdbg 几种常用方法: 1.通过Session.run()获取变量的值 2.利用Tensorboard查看一些可视化统计 ...

  6. 缩进与对齐——正确地使用Tab和空格

    写代码时大家都会使用缩进(indentation)和对齐(alignment),这是两个相关而又不同的概念,两者都是为了提高代码的可读性.缩进出现在一行的最左边,目的是明显地区分开包含与被包含的代码: ...

  7. Java Web学习总结-文件下载

    参考资料:https://www.cnblogs.com/xdp-gacl/p/4200090.html 在Web应用系统开发中,文件上传和下载功能是非常常用的功能,今天来讲一下JavaWeb中的文件 ...

  8. 关于docker的15个小tip

    1. 获取最近运行容器的id 这是我们经常会用到的一个操作,按照官方示例,你可以这样做(环境ubuntu): $ ID=$(docker run ubuntu echo hello world) he ...

  9. 【React】初识React

    React是什么 React是如今(2015年)最热门的前端技术. 在React中.一切皆组件. A JavaScript library for building user interfaces R ...

  10. Linux中基于hadoop安装hive(CentOS7+hadoop2.8.0+hive2.1.1)

    http://blog.csdn.net/pucao_cug/article/details/71773665