Windows Phone零距离开发(Composite Thread组合线程)

简洁流畅,快速响应是Windows Phone的特点也是他的买点,我们在开发App时候,也要在手机有限的硬件性能上最大限度做到UI快速响应,微软在优化手机快速响应这块做了很多底层优化工作,其中有一个就是我们要讨论的Composite Thread。

Composite Thread这个中文叫组合线程也有翻译叫构图线程。很多人对Composite Thread都并不了解,也不知道这是干什么的,因为毕竟有关Composite Thread的有关资料确实很少,而且Composite Thread最早不是在WPF/Silverlight上实现的,而是首先在Silverlight For Windows Phone 7上实现的,等到Silverlight5才引进Composite Thread。

说了那么多,那Composite Thread到底是可以用来干嘛用的,一言以蔽之就是,可以把动画剥离开UI线程到组合线程上执行

在WP7开发中,我们如果用让用户等待一个耗时操作的完成,会用一个ProgressBar控件来提示让用户等待流程的完成。但ProgressBar有一个很不好的地方就是他是在UI线程上执行的,如果这个时候UI线程大量计算操作时候,ProgressBar就会卡成白痴(注:WP8的ProgressBar没有这个问题)。这个时候,一个叫做PerformanceProgressBar控件就闪亮登场出来救场了,PerformanceProgressBar很对得起它的Performance的Title,因为他可以做到不卡UI线程。

可能大家很好奇,因为我们知道一般更改UI操作只能是在UI线程上进行,而动画也是在更改UI,但PerformanceProgressBar却可以在非UI线程上执行动画。PerformanceProgressBar是Silverlight For WP Toolkit下一个开源控件,我们可以直接通过源码来看到他的具体实现。

通过源码我们可以看到PerformanceProgressBar仅仅只是在ProgressBar的基础上重新定制了他的Style

<PerformanceProgressBar 
                x:Name="customIndeterminateProgressBar"
                IsIndeterminate="true"
                Style="{StaticResource CustomIndeterminateProgressBar}"
            />
<Style x:Key="CustomIndeterminateProgressBar" TargetType="ProgressBar">
            <Setter Property="Foreground" Value="{StaticResource PhoneAccentBrush}"/>
            <Setter Property="Background" Value="{StaticResource PhoneAccentBrush}"/>
            <Setter Property="Maximum" Value="100"/>
            <Setter Property="IsHitTestVisible" Value="False"/>
            <Setter Property="Padding" Value="{StaticResource PhoneHorizontalMargin}"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ProgressBar">
                        <unsupported:RelativeAnimatingContentControl HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
                            <unsupported:RelativeAnimatingContentControl.Resources>
                                <ExponentialEase EasingMode="EaseOut" Exponent="1" x:Key="ProgressBarEaseOut"/>
                                <ExponentialEase EasingMode="EaseOut" Exponent="1" x:Key="ProgressBarEaseIn"/>
                            </unsupported:RelativeAnimatingContentControl.Resources>
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="CommonStates">
                                    <VisualState x:Name="Determinate"/>
                                    <VisualState x:Name="Indeterminate">
                                        <Storyboard RepeatBehavior="Forever" Duration="00:00:04.4">
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="IndeterminateRoot">
                                                <DiscreteObjectKeyFrame KeyTime="0">
                                                    <DiscreteObjectKeyFrame.Value>
                                                        <Visibility>Visible</Visibility>
                                                    </DiscreteObjectKeyFrame.Value>
                                                </DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="DeterminateRoot">
                                                <DiscreteObjectKeyFrame KeyTime="0">
                                                    <DiscreteObjectKeyFrame.Value>
                                                        <Visibility>Collapsed</Visibility>
                                                    </DiscreteObjectKeyFrame.Value>
                                                </DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames>
                                            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00.0" Storyboard.TargetProperty="X" Storyboard.TargetName="R1TT">
                                                <LinearDoubleKeyFrame KeyTime="00:00:00.0" Value="0.1"/>
                                                <EasingDoubleKeyFrame KeyTime="00:00:00.5" Value="33.1" EasingFunction="{StaticResource ProgressBarEaseOut}"/>
                                                <LinearDoubleKeyFrame KeyTime="00:00:02.0" Value="66.1"/>
                                                <EasingDoubleKeyFrame KeyTime="00:00:02.5" Value="100.1" EasingFunction="{StaticResource ProgressBarEaseIn}"/>
                                            </DoubleAnimationUsingKeyFrames>
                                            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00.2" Storyboard.TargetProperty="X" Storyboard.TargetName="R2TT">
                                                <LinearDoubleKeyFrame KeyTime="00:00:00.0" Value="0.1"/>
                                                <EasingDoubleKeyFrame KeyTime="00:00:00.5" Value="33.1" EasingFunction="{StaticResource ProgressBarEaseOut}"/>
                                                <LinearDoubleKeyFrame KeyTime="00:00:02.0" Value="66.1"/>
                                                <EasingDoubleKeyFrame KeyTime="00:00:02.5" Value="100.1" EasingFunction="{StaticResource ProgressBarEaseIn}"/>
                                            </DoubleAnimationUsingKeyFrames>
                                            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00.4" Storyboard.TargetProperty="X" Storyboard.TargetName="R3TT">
                                                <LinearDoubleKeyFrame KeyTime="00:00:00.0" Value="0.1"/>
                                                <EasingDoubleKeyFrame KeyTime="00:00:00.5" Value="33.1" EasingFunction="{StaticResource ProgressBarEaseOut}"/>
                                                <LinearDoubleKeyFrame KeyTime="00:00:02.0" Value="66.1"/>
                                                <EasingDoubleKeyFrame KeyTime="00:00:02.5" Value="100.1" EasingFunction="{StaticResource ProgressBarEaseIn}"/>
                                            </DoubleAnimationUsingKeyFrames>
                                            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00.6" Storyboard.TargetProperty="X" Storyboard.TargetName="R4TT">
                                                <LinearDoubleKeyFrame KeyTime="00:00:00.0" Value="0.1"/>
                                                <EasingDoubleKeyFrame KeyTime="00:00:00.5" Value="33.1" EasingFunction="{StaticResource ProgressBarEaseOut}"/>
                                                <LinearDoubleKeyFrame KeyTime="00:00:02.0" Value="66.1"/>
                                                <EasingDoubleKeyFrame KeyTime="00:00:02.5" Value="100.1" EasingFunction="{StaticResource ProgressBarEaseIn}"/>
                                            </DoubleAnimationUsingKeyFrames>
                                            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00.8" Storyboard.TargetProperty="X" Storyboard.TargetName="R5TT">
                                                <LinearDoubleKeyFrame KeyTime="00:00:00.0" Value="0.1"/>
                                                <EasingDoubleKeyFrame KeyTime="00:00:00.5" Value="33.1" EasingFunction="{StaticResource ProgressBarEaseOut}"/>
                                                <LinearDoubleKeyFrame KeyTime="00:00:02.0" Value="66.1"/>
                                                <EasingDoubleKeyFrame KeyTime="00:00:02.5" Value="100.1" EasingFunction="{StaticResource ProgressBarEaseIn}"/>
                                            </DoubleAnimationUsingKeyFrames>
                                            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00.0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="R1">
                                                <DiscreteDoubleKeyFrame KeyTime="0" Value="1"/>
                                                <DiscreteDoubleKeyFrame KeyTime="00:00:02.5" Value="0"/>
                                            </DoubleAnimationUsingKeyFrames>
                                            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00.2" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="R2">
                                                <DiscreteDoubleKeyFrame KeyTime="0" Value="1"/>
                                                <DiscreteDoubleKeyFrame KeyTime="00:00:02.5" Value="0"/>
                                            </DoubleAnimationUsingKeyFrames>
                                            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00.4" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="R3">
                                                <DiscreteDoubleKeyFrame KeyTime="0" Value="1"/>
                                                <DiscreteDoubleKeyFrame KeyTime="00:00:02.5" Value="0"/>
                                            </DoubleAnimationUsingKeyFrames>
                                            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00.6" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="R4">
                                                <DiscreteDoubleKeyFrame KeyTime="0" Value="1"/>
                                                <DiscreteDoubleKeyFrame KeyTime="00:00:02.5" Value="0"/>
                                            </DoubleAnimationUsingKeyFrames>
                                            <DoubleAnimationUsingKeyFrames BeginTime="00:00:00.8" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="R5">
                                                <DiscreteDoubleKeyFrame KeyTime="0" Value="1"/>
                                                <DiscreteDoubleKeyFrame KeyTime="00:00:02.5" Value="0"/>
                                            </DoubleAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                            <Grid>
                                <Grid x:Name="DeterminateRoot" Margin="{TemplateBinding Padding}" Visibility="Visible">
                                    <Rectangle x:Name="ProgressBarTrack" Fill="{TemplateBinding Background}" Height="4" Opacity="0.1"/>
                                    <Rectangle x:Name="ProgressBarIndicator" Fill="{TemplateBinding Foreground}" HorizontalAlignment="Left" Height="4"/>
                                </Grid>
                                <Border x:Name="IndeterminateRoot" Margin="{TemplateBinding Padding}" Visibility="Collapsed">
                                    <Grid HorizontalAlignment="Left">
                                        <Rectangle Fill="{TemplateBinding Foreground}" Height="4" IsHitTestVisible="False" Width="4" x:Name="R1" Opacity="0" CacheMode="BitmapCache">
                                            <Rectangle.RenderTransform>
                                                <TranslateTransform x:Name="R1TT"/>
                                            </Rectangle.RenderTransform>
                                        </Rectangle>
                                        <Rectangle Fill="{TemplateBinding Foreground}" Height="4" IsHitTestVisible="False" Width="4" x:Name="R2" Opacity="0" CacheMode="BitmapCache">
                                            <Rectangle.RenderTransform>
                                                <TranslateTransform x:Name="R2TT"/>
                                            </Rectangle.RenderTransform>
                                        </Rectangle>
                                        <Rectangle Fill="{TemplateBinding Foreground}" Height="4" IsHitTestVisible="False" Width="4" x:Name="R3" Opacity="0" CacheMode="BitmapCache">
                                            <Rectangle.RenderTransform>
                                                <TranslateTransform x:Name="R3TT"/>
                                            </Rectangle.RenderTransform>
                                        </Rectangle>
                                        <Rectangle Fill="{TemplateBinding Foreground}" Height="4" IsHitTestVisible="False" Width="4" x:Name="R4" Opacity="0" CacheMode="BitmapCache">
                                            <Rectangle.RenderTransform>
                                                <TranslateTransform x:Name="R4TT"/>
                                            </Rectangle.RenderTransform>
                                        </Rectangle>
                                        <Rectangle Fill="{TemplateBinding Foreground}" Height="4" IsHitTestVisible="False" Width="4" x:Name="R5" Opacity="0" CacheMode="BitmapCache">
                                            <Rectangle.RenderTransform>
                                                <TranslateTransform x:Name="R5TT"/>
                                            </Rectangle.RenderTransform>
                                        </Rectangle>
                                    </Grid>
                                </Border>
                            </Grid>
                        </unsupported:RelativeAnimatingContentControl>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

在上面的Style里,ProgressBar的进度动画被封装在VisualStateGroup里,动画类型是DoubleAnimation,凡是DoubleAnimation的动画都会自动在Composite Thread上执行,从而做到不卡UI线程,Silverlight的动画就是在一个时间轴上不停改变依赖属性的值,也就是只要是动画Double类型的依赖属性(比如动画TranslateTransform X Y的值,动画UI的Opacity透明度),那么这些动画都会转到Composite Thread上执行。

总结

Composite Thread的存在是为了从UI线程剥离DoubleAnimation动画出来,提高UI快速响应能力。

DoubleAnima类型的动画会自动在Composite Thread上运行。

开发WP7时候,要用Toolkit下的PerformanceProgressBar替代系统的自带的ProgressBar来获得更好的UI体验。

原文地址:https://www.cnblogs.com/xinbaby/p/xinbaby_WindowsPhone_Composite_Thread.html