[转载]Android 中文 API (100) —— ScrollView - 农民伯伯 - 博客园

mikel阅读(970)

[转载]Android 中文 API (100) —— ScrollView – 农民伯伯 – 博客园.

前言

春节即至,谨代表Android中文翻译组全体同仁祝大家身体健 康,工作顺利!从第一篇译稿2010年8月27发布至今天2011年1月27整5个月,共发布100篇译文,3个合集,在新的一年里,翻译组仍将坚持 Android相关的翻译工作,秉承开源、合作、共享和坚持的信念打持久战,感谢大家的关心和支持!

本章内容是 android.widget.ScrollView,版本为Android 2.3 r1,翻译来自”pengyouhong”,再次感谢”pengyouhong” !期待你一起参与Android API的翻译,联系我over140@gmail.com。

 

声明

欢迎转载,但请保留文章原始出处:)

博客园:http://www.cnblogs.com/

Android中文翻译组:http://androidbox.sinaapp.com/

 

正文

一、结构

public class ScrollView extends FrameLayout

        

java.lang.Object

android.view.View

         android.view.ViewGroup

                   android.widget.FrameLayout

                            android.widget.ScrollView

 

二、概述

  一种可供用户滚动的层次结构布局容器,允许显示比实际多的内容。ScrollView是一种FrameLayout,意味需要在其上放置有自己滚动内容的子元素。子元素可以是一个复杂的对象的布局管理器。通常用的子元素是垂直方向的LinearLayout,显示在最上层的垂直方向可以让用户滚动的箭头。

  TextView类也有自己的滚动功能,所以不需要使用ScrollView,但是只有两个结合使用,才能保证显示较多内容时候的效率。但只有两者结合使用才可以实现在一个较大的容器中一个文本视图效果。

  ScrollView只支持垂直方向的滚动。

 

三、构造函数

public ScrollView (Context context)

创建一个默认属性的ScrollView实例。

public ScrollView (Context context, AttributeSet attrs)

创建一个带有attrs属性的ScrollView 实例。

public ScrollView (Context context, AttributeSet attrs, int defStyle)

创建一个带有attrs属性,并且指定其默认样式的ScrollView实例。

 

四、公共方法

   public void addView (View child)

         添加子视图。如果事先没有给子视图设置layout参数,会采用当前ViewGroup的默认参数来设置子视图。

                参数

                       child       所添加的子视图

  public void addView (View child, int index)

  添加子视图。如果事先没有给子视图设置layout参数,会采用当前ViewGroup的默认参数来设置子视图。

    参数

   child       所添加的子视图

                index      添加子视图的位置

        

  public void addView (View child, int index, ViewGroup.LayoutParams params)

  根据指定的layout参数添加子视图

  参数

               child       所添加的子视图

               index      添加子视图的位置

               params   为子视图设置的layout参数

 

  public void addView (View child, ViewGroup.LayoutParams params)

  根据指定的layout参数添加子视图。

  参数

               child       所添加的子视图

               params   为子视图设置的layout参数

 

  public boolean arrowScroll (int direction)

  响应点击上下箭头时对滚动条滚动的处理。

                参数

                       direction        按下的箭头所对应的方向

  返回值

  如果我们处理(消耗)了此事件返回true,否则返回false

  public void computeScroll ()

  被父视图调用,用于必要时候对其子视图的值(mScrollX和mScrollY)进行更新。典型的情况如:父视图中某个子视图使用一个Scroller对象来实现滚动操作,会使得此方法被调用。

  public boolean dispatchKeyEvent (KeyEvent event)

         发送一个key事件给当前焦点路径的下一个视图。此焦点路径从视图树的顶层执行直到当前焦点视图。如果此视图为焦点视图,将为自己发送。否则,会为当前焦点路径的下一个节点发送。此方法也会激起一个key监听器。

  参数

                       event     发送的key事件

  返回值

  事件被处理返回true,否则返回false

  public void draw (Canvas canvas)

  手动绘制视图(及其子视图)到指定的画布(Canvas)。这个视图必须在调用这个函数之前做好了整体布局。当实现一个视图时,不需要继承这个方法;相反,你应该实现onDraw(Canvas)方法。

  参数

  canvas    绘制视图的画布

 

  public boolean executeKeyEvent (KeyEvent event)

  当接收到key事件时,用户可以调用此函数来使滚动视图执行滚动,类似于处理由视图体系发送的事件。

  参数

  event     需要执行key的事件

  返回值

  事件被处理返回true,否则返回false

 

  public void fling (int VelocityY)

  滚动视图的滑动(fling)手势。(译者注: 如何监听android的屏幕滑动停止事件

  参数

                       VelocityY      Y方向的初始速率。正值表示手指/光标向屏幕下方滑动,而内容将向上滚动。

 

  public boolean fullScroll (int direction)

  对响应“home/end”短按时响应滚动处理。此方法将视图滚动到顶部或者底部,并且将焦点置于新的可视区域的最顶部/最底部组件。若没有适合的组件做焦点,当前的ScrollView会收回焦点。

  参数

  direction 滚动方向:FOCUS_UP表示视图向上滚动;FOCUS_DOWN表示视图向下滚动

  返回值

  key事件被消耗(consumed)返回true,其他情况返回false

 

  public int getMaxScrollAmount ()

  返回值

  当前滚动视图响应箭头事件能够滚动的最大数。

 

  public boolean isFillViewport ()

  指示当前ScrollView的内容是否被拉伸以填充视图可视范围(译者注:viewport可视范围,参见决定Scrollviewer里面Control的可视范围)。

  返回值

  内容填充视图返回true,否则返回false

 

  public boolean isSmoothScrollingEnabled ()

  返回值

               按箭头方向滚动时,是否显示滚动的平滑效果。

 

  public boolean onInterceptTouchEvent (MotionEvent ev)

  实现此方法是为了拦截所有触摸屏幕时的运动事件。可以像处理发送给子视图的事件一样去监视这些事件,并且获取当前手势在任意点的ownership

  使用此方法时候需要注意,因为它与View.onTouchEvent(MotionEvent)有相当复杂的交互,并且前提需要正确执行View.onTouchEvent(MotionEvent)。事件将按照如下顺序接收到:

  1.    收到down事件

  2.    Down事件或者由视图组的一个子视图处理,或者被用户自己的onTouchEvent()方法处理;此处理意味你应该执行onTouchEvent()时返回true,这样才能继续看到剩下的手势(取代找一个父视图处理)。如果onTouchEvent()返回true时,你不会收到onInterceptTouchEvent()的任何事件并且所有对触摸的处理必须在onTouchEvent()中发生。

  3.    如果此方法返回false,接下来的事件(up to and including the final up)将最先被传递当此,然后是目标的onTouchEvent()

  4.    如果返回true,将不会收到以下任何事件:目标view将收到同样的事件但是会伴随ACTION_CANCEL,并且所有的更进一步的事件将会传递到你自己的onTouchEvent()方法中而不会再在这里出现。

  参数

              ev          体系向下发送的动作事件

  返回值

  如果将运动事件从子视图中截获并且通过onTouchEvent()发送到当前ViewGroup ,返回true。当前目标将会收到ACTION_CANCEL事件,并且不再会有其他消息传递到此。

              (译者注:onInterceptTouchEventonTouchEvent调用时序

 

  public boolean onTouchEvent (MotionEvent ev)

  执行此方法为了处理触摸屏幕的运动事件。

  参数

       ev          运动事件

  返回值

  事件被处理返回true,其它返回false

 

  public boolean pageScroll (int direction)

  响应短按“page up/ down”时候对滚动的处理。此方法将向上或者向下滚动一屏,并且将焦点置于新可视区域的最上/最下。如果没有适合的component作为焦点,当前scrollView将收回焦点。

  参数

       direction        滚动方向:FOCUS_UP表示向上翻一页,FOCUS_DOWN表示向下翻一页。

  返回值

  key事件被消耗(cosumed)返回true,其他返回false

  public void requestChildFocus (View child, View focused)

  当父视图的一个子视图的要获得焦点时,调用此方法。

  参数

       child              要获得焦点的父视图的子视图。此视图包含了焦点视图。如果没有特殊徐要求,此视图实际上就是焦点视图。

                focused        子视图的子孙视图并且此子孙视图是真正的焦点视图

  public boolean requestChildRectangleOnScreen (View child, Rect rectangle, boolean immediate)

  当组里的某个子视图需要被定位在屏幕的某个矩形范围时,调用此方法。重载此方法的ViewGroup可确认以下几点:

      * 子项目将是组里的直系子项

      * 矩形将在子项目的坐标体系中

  重载此方法的ViewGroup应该支持以下几点:

      * 若矩形已经是可见的,则没有东西会改变

      * 为使矩形区域全部可见,视图将可以被滚动显示

  参数

  child        发出请求的子视图

  rectangle    子项目坐标系内的矩形,即此子项目希望在屏幕上的定位

  immediate   设为true,则禁止动画和平滑移动滚动条

        返回值

              进行了滚动操作的这个组(group),是否处理此操作。

  public void requestLayout ()

  当有改变引起当前视图重新布局时,调用此函数。它将规划一个视图树的layout路径。

  public void scrollTo (int x, int y)

  设置当前视图滚动到的位置。此函数会引起对onScrollChanged(int, int, int, int)函数的调用并且会让视图更新。

当前版本取消了在子视图中的滚动。

  参数

       x     滚动到的X位置

                y     滚动到的Y位置

  public void setFillViewport (boolean fillViewport)

  设置当前滚动视图是否将内容高度拉伸以填充视图可视范围(译者注:viewport可视范围,参见决定Scrollviewer里面Control的可视范围)。

  参数

       fillViewport    设置为true表示拉伸内容高度来适应视口边界;其他设为false

  public void setOverScrollMode (int mode)

  为视图设置over-scroll模式。有效的over-scroll模式有OVER_SCROLL_ALWAYS(缺省值),OVER_SCROLL_IF_CONTENT_SCROLLS(只允许当视图内容大过容器时,进行over-scrolling)和OVER_SCROLL_NEVER。只有当视图可以滚动时,此项设置才起作用。

译者注:这个函数是2.3 r1 中新增的,API Level 9关于over-scroll这里译为弹性滚动,即,参见帖子:类似iPhone的弹性ListView滚动

         参数

                   mode       The new over-scroll mode for this view.

  public void setSmoothScrollingEnabled (boolean smoothScrollingEnabled)

  用来设置箭头滚动是否可以引发视图滚动。

  参数

                smoothScrollingEnabled    设置箭头滚动是否可以引起内容的滚动的bool

  public final void smoothScrollBy (int dx, int dy)

  类似于scrollBy(int, int),但是滚动时候是平缓的而不是立即滚动到某处。

  参数

       dx   X方向滚动的像素数

       dy   Y方向滚动的像素数

 

  public final void smoothScrollTo (int x, int y)

  类似于scrollTo(int, int),但是滚动时候是平缓的而不是立即滚动到某处。

  参数

       x     要滚动到位置的X坐标

               y     要滚动到位置的Y坐标

 

五、受保护方法

  protected int computeScrollDeltaToGetChildRectOnScreen (Rect rect)

  计算X方向滚动的总合,以便在屏幕上显示子视图的完整矩形(或者,若矩形宽度超过屏幕宽度,至少要填满第一个屏幕大小)。

  参数

       rect        矩形

  返回值

  滚动差值

  protected int computeVerticalScrollOffset ()

  计算垂直方向滚动条的滑块的偏移。此值用来计算滚动条轨迹的滑块的位置。

  范围可以以任意单位表示,但是必须与computeVerticalScrollRange()computeVerticalScrollExtent()的单位一致。

  缺省的偏移是在当前视图滚动的偏移。

  返回值

  滚动条的滑块垂直方向的偏移。

  protected int computeVerticalScrollRange ()

  滚动视图的可滚动范围是所有子元素的高度。

  返回值

  由垂直方向滚动条代表的所有垂直范围,缺省的范围是当前视图的画图高度。

 

  protected float getBottomFadingEdgeStrength ()

  返回滚动底部的能见度。能见度的值的范围是0.0(没有消失)到1.0(完全消失)之间。缺省的执行返回值为0.0或者1.0,而不是他们中间的某个值。滚动时子类需要重载这个方法来提供一个平缓的渐隐的实现。

  返回值

  滚动底部能见度,值的范围在浮点数0.0f1.0f之间。

 

  protected float getTopFadingEdgeStrength ()

  返回滚动顶部的能见度。能见度的值的范围是0.0(没有消失)到1.0(完全消失)之间。缺省的执行返回值为0.0或者1.0,而不是他们中间的某个值。滚动时子类需要重载这个方法来提供一个平缓的渐隐的实现。

  返回值

  滚动顶部能见度,值的范围在浮点数0.0f1.0f之间。

 

  protected void measureChild (View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec)

  要求当前视图的一个子视图测量自己,同时兼顾到当前视图的MeasureSpec的要求和它的空白。子视图必须有MarginLayoutParams。比较复杂的工作是在getChildMeasureSpec中完成的。

  参数

  child              需要自己测量的子视图

  parentWidthMeasureSpec        当前视图要求的宽度

  parentHeightMeasureSpec              当前视图要求的宽度

 

  protected void measureChildWithMargins (View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed)

  要求当前视图的一个子视图测量自己,同时兼顾到当前视图的MeasureSpec的要求和它的空白和边界。子视图必须有MarginLayoutParams。比较复杂的工作是在getChildMeasureSpec中完成的。

  参数

  child             需要测量的子视图

  parentWidthMeasureSpec       当前视图要求的宽度

  widthUsed    水平方向上由父视图使用的空白 (也可能是视图的其他子视图使用的)

  parentHeightMeasureSpec      当前视图要求的宽度

  heightUsed 垂直方向上由父视图使用的空白 (也可能是视图的其他子视图使用的)

 

  protected void onLayout (boolean changed, int l, int t, int r, int b)

  当前视图需要为子视图分配大小和位置时候调用,子类继承必须要重载此方法并调用自己子视图的layout函数。

  参数

  changed       当前视图的新的大小或者位置

  l     相对父视图,左边界位置

  t     相对父视图,上边界位置

  r     相对父视图,右边界位置

  b    相对父视图,下边界位置

  protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec)

  测量视图以确定其内容宽度和高度。此方法被measure(int, int)调用。需要被子类重写以提供对其内容准确高效的测量。

  约定:当重写此方法时,你必须调用setMeasuredDimension(int, int)来保存当前视图view的宽度和高度。不成功调用此方法将会导致一个IllegalStateException异常,是由measure(int, int)抛出。所以调用父类的onMeasure(int, int)方法是必须的。

  父类的实现是以背景大小为默认大小,除非MeasureSpec(测量细则)允许更大的背景。子类可以重写onMeasure(int,int)以对其内容提供更佳的尺寸。

  如果此方法被重写,那么子类的责任是确认测量高度和测量宽度要大于视图view的最小宽度和最小高度(getSuggestedMinimumHeight() getSuggestedMinimumWidth()),使用这两个方法可以取得最小宽度和最小高度。

  参数

  widthMeasureSpec   受主窗口支配的水平空间要求。这个需求通过 View.MeasureSpec.进行编码。

             heightMeasureSpec  受主窗口支配的垂直空间要求。这个需求通过 View.MeasureSpec.进行编码。

 

protected void onOverScrolled (int scrollX, int scrollY, boolean clampedX, boolean clampedY)

         overScrollBy(int, int, int, int, int, int, int, int, boolean)调用,来对一个over-scroll操作的结果进行响应。(译者注:这个函数是2.3 r1 中新增的,API Level 9

  参数

  scrollX     新的X滚动像素值

  scrollY     新的Y滚动像素值

  clampedX        scrollXover-scroll的边界限制时,值为true

  clampedY        scrollYover-scroll的边界限制时,值为true

  protected boolean onRequestFocusInDescendants (int direction, Rect previouslyFocusedRect)

  当在滚动视图的子视图中查找焦点视图时,需要注意不要将焦点设置在滚动出屏幕外的控件上。此方法会比执行缺省的ViewGroup代价高,否则此行为也会设置为缺省

  参数

               direction 指定下列常量之一:FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT

previouslyFocusedRect 能够给出一个较好的提示的矩形(当前视图的坐标系统)表示焦点从哪里得来。如果没有提示为null

  返回值

  是否取得了焦点

  protected void onSizeChanged (int w, int h, int oldw, int oldh)

布局期间当视图的大小发生改变时调用。如果只是添加到视图,调用时显示的是旧值0。(译者注:也就是添加到视图时,oldwoldh返回的是0)。

  参数

  w     视图当前宽度

  h     视图当前高度

  oldw       视图改变前的宽度

  oldh       视图改变前的高度

 

六、补充

示例代码

下载:Demo_ScrollView.rar

 

七、译者其他译文

android 中文 api (64) —— Scroller

 

八、通知

由于年前事情较多,合集4将推迟到年后发布,时间约为2011年2月9日或10日,请大家谅解 🙂

[转载]京东SDK模板卡盘效果实现代码 - cnblogs/chen - 博客园

mikel阅读(1078)

[转载]京东SDK模板卡盘效果实现代码 – cnblogs/chen – 博客园.

最近在做京东模板,因为是最新平台,好多功能都需要摸索,本人技术一般,摸索出一个建议的卡盘功能 各位童鞋,使用的是分类推荐模块哦!

本着共享的精神,俺将代码放到这儿了,各人请自便。(代码还不够完善,希望完善的童鞋可以发给我,直接放到评论里吧!在这里提前谢过哦!呵呵)

 

本人京东设计师:王靖鋆          设计模板: http://zx.jd.com/designerDetail.html?id=148

HTML代码 如下:

复制代码
#if($!moduleTitle.exists)
    <!--<div class="jTm">
        #if($!moduleTitle.titleExists)
            #if($!moduleTitle.title!='')
                <a href="$!moduleTitle.titleLink">$!moduleTitle.title</a>
            #end 
        #end

        #if($!moduleTitle.moreExists)
            #if($!moduleTitle.more!='')
                <div class="jMore"><a href="$!moduleTitle.moreLink">$!moduleTitle.more</a></div>
            #end
        #end

        #if($!moduleTitle.titleExists)
            #if($!moduleTitle.titleImg!='')
              <a href="$!moduleTitle.titleLink"><img src="$!moduleTitle.titleImg" /></a>
            #end
        #end
        #if($!moduleTitle.moreExists)
            #if($!moduleTitle.moreImg!='')
              <div class="jMore"><a href="$!moduleTitle.moreLink"><img src="$!moduleTitle.moreImg" /></a></div>
            #end
        #end
    </div>-->
#end    
#set($intNum=0)
<div class="j-module" module-function="tabAutoLayout" module-param="{}">
        <div class="jSortContent">
            #foreach($!categoryRec in $!goodsCategoryRecList)
                #foreach($!goods in $!categoryRec.goodsList)
                <ul><li>
                    <div class="jItem">
                        <div class="jPicbig">
                        <div class="jaoHot"></div>
                            <a title="$!goods.wname" href="$!jshopProduct.getBuyUrl($!goods.goodsId)" target="_blank">
                                <img width="350" height="320" src="$!jshopCommon.getImage($!goods.imageurl,1)" alt="$!goods.wname">
                            </a>
                        #if( $!goods.promTag!="")
                        <div class="jPromotionLabel">

                                $!goods.promTag

                        </div>
                        #end
                        </div>
                        <div class="jGoodsInfo">
                            <div class="jTitle"><h1><span style="font-size:17px;">HOT</span> 《热卖商品》</h1></div>
                            <div class="jDesc" title="$!goods.wname $!goods.advertWord">
                                <div class="jWname">
                                <a href="$!jshopProduct.getBuyUrl($!goods.goodsId)" target="_blank">$!goods.wname</a></br>
                                </div>
                                <div style="line-height:30px;">
                                    <span class="jSlogan">$!goods.advertWord</span>
                                </div>
                            </div>                          
                            <div class="jPrice">
                                <div class="jSalePrice">
                                    <span class="jText">参考价:</span>
                                    <span class="jRmb"></span>
                                    $!jshopPrice.getSalePrice($!goods.goodsId)
                                </div>
                                <div class="jdPrice">
                                    <span class="jText">促销价:</span>
                                    <span class="jRmb"></span>
                                    $!jshopPrice.getJdPrice($!goods.goodsId)
                                </div>
                            </div>
                            <div class="jBtnArea">
                                <a  href="$!jshopCommon.addCart($!goods.goodsId)"  target="_blank" >加入购物车</a>
                            </div>
                        </div>
                    </div>
                </li></ul>
                #end
            #end
        </div>

        <div class="jSortTab">
            #foreach($!categoryRec in $!goodsCategoryRecList)
                #foreach($!goods in $!categoryRec.goodsList)
                    <span>
                        <div class="jPic">
                            <a title="$!goods.wname" href="$!jshopProduct.getBuyUrl($!goods.goodsId)" target="_blank">
                                <img width="50" height="50" src="$!jshopCommon.getImage($!goods.imageurl,5)" alt="$!goods.wname">
                            </a>
                        </div>
                    </span>
                #end
            #end
        </div>
        <div class="jSortTabArrow"><b></b></div>
</div>
复制代码

 

CSS代码 如下:

复制代码
.user_PShow{font-family:'microsoft yahei';}
.user_PShow *{font-family:'microsoft yahei';}
.user_PShow .mt{}
.user_PShow .mc{background:#fff; overflow:hidden; position:relative;border-radius:4px;border:5px solid #EDEDED;border-bottom:0px;}
.user_PShow .jSortTab{
    border-top:2px solid #AAAAAA; overflow:hidden; position:relative;
    background-color:#ededed;height:74px;
}
.user_PShow .jSortTab span{float:left; width:158px; padding:7px 0;text-align:center; font-size:14px; font-weight:bold; color:#666;}
.user_PShow .jSortTab span.current{
    color:#FFFFFF;background-color:#FFFFFF;
}
.user_PShow .jSortTabArrow{width:158px; position:absolute; z-index:1; top:25px; left:0; height:7px; overflow:hidden; text-align:center;}
.user_PShow .jSortTabArrow b{display:inline-block; margin-top:-8px; width:0; height:0; border-width:10px; border-color:transparent transparent #E4393C transparent; overflow:hidden; zoom:1; font-size:0;}
.user_PShow .jSortContent{
    overflow:hidden;border-bottom:1px solid #F1F1F1;height:100%;
}
.user_PShow .jSortContent ul{overflow:hidden; display:none;}
.user_PShow .jSortContent ul.current{
    display:block;padding:10px;
}
.user_PShow li{width:157px; overflow:hidden; float:left;}

.user_PShow .jPic{
    padding:5px; text-align:center;background-color:#c3c3c3;width:50px;margin:0px auto;
}
.user_PShow .jDesc{
    height:55%; line-height:1.3; overflow:hidden;margin:10px 0px;
}
.user_PShow .jDesc a{color:#666;font-size:13px;}
.user_PShow .jDesc a:hover{color:#E4393C; text-decoration:underline;}
.user_PShow .jdPrice .jRmb{font-size:12px;}
.user_PShow .jdPrice .jdNum{font-size:15px;}
.user_PShow .jdPrice .jdNumNo{font-size:12px;}
.user_PShow .jMore{
     position:absolute;right:0px;top:0px;text-align:center;
}
.user_PShow .jTm{
    height:auto;
    position:relative;
}

.user_PShow .jPicbig{
    text-align:center;float:left;border:5px solid #ededed;
}
.user_PShow .jGoodsInfo{
    float:left;margin-left:30px;height:330px;width:550px;overflow:hidden;
}
.user_PShow .jSortContent ul li{
    width:100% !important;
}
.user_PShow .jTitle{
    background-color:#E7492E;
    font-size:15px;color:#FFFFFF;padding:5px 15px;
}
.user_PShow .jPrice,.user_PShow .jBtnArea,.user_PShow .jDesc{
    padding:0px 15px;
}
.user_PShow .jPrice{
    color:#c3c3c3;font-weight:bold;line-height:25px;min-height:45px;text-align:right;
}
.user_PShow .jBtnArea{
    color:#FFFFFF;font-weight:bold;line-height:43px;padding:0px;
}
.user_PShow .jBtnArea a,.user_PShow .jBtnArea .btn-coll{
    display:block; background: url("file/loadImage.html?templateId=5&imageName=Bjsale.png") no-repeat scroll 0 0 transparent;
    width:200px;height:40px;margin:0px auto;line-height:40px;text-align:center;color:#FFFFFF;font-size:17px;float:right;
}
.user_PShow .jBtnArea .btn-coll{
    background: url("file/loadImage.html?templateId=5&imageName=Bjsalegz.png") no-repeat scroll 0 0 transparent;float:left;
    cursor: pointer;
    display: block;
    vertical-align: top;font-weight:bold;;width:200px;height:40px;margin:0px;padding:0px;
}
.user_PShow .jAdvertWord{
    font-size:12px;height:30%;margin:10px 0px;
}
.user_PShow .jSalePrice{ text-decoration:line-through; overflow:hidden; clear:both;}
.user_PShow .jdPrice{color:#ff0000; overflow:hidden;font-weight:bold;}
.user_PShow h1{font-weight:bold;}
.user_PShow .jWname{margin:10px 0px;width:100%;text-align:center;border-bottom:2px solid #C3C3C3;}
.user_PShow .jSlogan{color:#565656;font-size:17px;font-weight:bold;}
.user_PShow .jaoHot{
    background: url("file/loadImage.html?templateId=5&imageName=hotjao.png") no-repeat scroll 0 0 transparent;width:84px;height:84px;
    position:absolute;left:6px;top:7px;
}

.user_PShow .jPromotionLabel{padding:15px 0px;color:#FFFFFF;font-size:12px;width:56px; height:55px;text-align:center; background:url("file/loadImage.html?templateId=5&imageName=promTag.png") no-repeat;  _filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='http://img10.360buyimg.com/cms/g8/M02/0F/11/rBEHZ1C0Ma8IAAAAAAAIpkn1m8MAADCTQP_7yMAAAi-795.png'); _background-image:none;  position:absolute; top:4px; left:310px;}
.user_PShow .jPromotionTextArea{width:56px;font-size:12px;overflow:hidden; text-align:center; padding-top:35px;color:#fff;}
.user_PShow .jPromotionText1{}
.user_PShow .jPromotionNum{}
.user_PShow .jPromotionText2{}
.user_PShow .jSortTab span.current .jPic{
    background-color:#FF0000;
}
复制代码

 

[转载]用Python写一个简单的Web框架 - RussellLuo - 博客园

mikel阅读(787)

[转载]用Python写一个简单的Web框架 – RussellLuo – 博客园.

一、概述

在Python中,WSGI(Web Server Gateway Interface)定义了Web服务器与Web应用(或Web框架)之间的标准接口。在WSGI的规范下,各种各样的Web服务器和Web框架都可以很好的交互。

由于WSGI的存在,用Python写一个简单的Web框架也变得非常容易。然而,同很多其他的强大软件一样,要实现一个功能丰富、健壮高效的Web框架并非易事;如果您打算这么做,可能使用一个现成的Web框架(如 DjangoTornadoweb.py 等)会是更合适的选择。

本文尝试写一个类似web.py的Web框架。好吧,我承认我夸大其辞了:首先,web.py并不简单;其次,本文只重点实现了 URL调度(URL dispatch)部分。

二、从demo_app开始

首先,作为一个初步体验,我们可以借助 wsgiref.simple_server 来搭建一个简单无比(trival)的Web应用:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from wsgiref.simple_server import make_server, demo_app

httpd = make_server('', 8086, demo_app)
sa = httpd.socket.getsockname()
print 'http://{0}:{1}/'.format(*sa)

# Respond to requests until process is killed
httpd.serve_forever()

运行脚本:

$ python code.py
http://0.0.0.0:8086/

打开浏览器,输入http://0.0.0.0:8086/后可以看到:一行”Hello world!” 和 众多环境变量值。

三、WSGI中的application

WSGI中规定:application是一个 可调用对象(callable object),它接受 environstart_response 两个参数,并返回一个 字符串迭代对象

其中,可调用对象 包括 函数方法 或者 具有__call__方法的 实例environ 是一个字典对象,包括CGI风格的环境变量(CGI-style environment variables)和 WSGI必需的变量(WSGI-required variables);start_response 是一个可调用对象,它接受两个 常规参数(status,response_headers)和 一个 默认参数(exc_info);字符串迭代对象 可以是 字符串列表生成器函数 或者 具有__iter__方法的可迭代实例。更多细节参考 Specification Details

The Application/Framework Side 中给出了一个典型的application实现:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""application.py"""

def simple_app(environ, start_response):
    """Simplest possible application object"""
    status = '200 OK'
    response_headers = [('Content-type', 'text/plain')]
    start_response(status, response_headers)
    return ['Hello world!\n']

现在用simple_app来替换demo_app:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""code.py"""

from wsgiref.simple_server import make_server
from application import simple_app as app

if __name__ == '__main__':
    httpd = make_server('', 8086, app)
    sa = httpd.socket.getsockname()
    print 'http://{0}:{1}/'.format(*sa)

    # Respond to requests until process is killed
    httpd.serve_forever()

运行脚本code.py后,访问http://0.0.0.0:8086/就可以看到那行熟悉的句子:Hello world!

四、区分URL

倒腾了一阵子后,您会发现不管如何改变URL中的path部分,得到的响应都是一样的。因为simple_app只识别host+port部分。

为了对URL中的path部分进行区分处理,需要修改application.py的实现。

首先,改用 来实现application:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""application.py"""

class my_app:
    def __init__(self, environ, start_response):
        self.environ = environ
        self.start = start_response

    def __iter__(self):
        status = '200 OK'
        response_headers = [('Content-type', 'text/plain')]
        self.start(status, response_headers)
        yield "Hello world!\n"

然后,增加对URL中path部分的区分处理:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""application.py"""

class my_app:
    def __init__(self, environ, start_response):
        self.environ = environ
        self.start = start_response

    def __iter__(self):
        path = self.environ['PATH_INFO']
        if path == "/":
            return self.GET_index()
        elif path == "/hello":
            return self.GET_hello()
        else:
            return self.notfound()

    def GET_index(self):
        status = '200 OK'
        response_headers = [('Content-type', 'text/plain')]
        self.start(status, response_headers)
        yield "Welcome!\n"

    def GET_hello(self):
        status = '200 OK'
        response_headers = [('Content-type', 'text/plain')]
        self.start(status, response_headers)
        yield "Hello world!\n"

    def notfound(self):
        status = '404 Not Found'
        response_headers = [('Content-type', 'text/plain')]
        self.start(status, response_headers)
        yield "Not Found\n"

修改code.py中的from application import simple_app as app,用my_app来替换simple_app后即可体验效果。

五、重构

上面的代码虽然奏效,但是在编码风格和灵活性方面有很多问题,下面逐步对其进行重构。

1、正则匹配URL

消除URL硬编码,增加URL调度的灵活性:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""application.py"""

import re ##########修改点

class my_app:

    urls = (
        ("/", "index"),
        ("/hello/(.*)", "hello"),
    ) ##########修改点

    def __init__(self, environ, start_response):
        self.environ = environ
        self.start = start_response

    def __iter__(self): ##########修改点
        path = self.environ['PATH_INFO']
        method = self.environ['REQUEST_METHOD']

        for pattern, name in self.urls:
            m = re.match('^' + pattern + '$', path)
            if m:
                # pass the matched groups as arguments to the function
                args = m.groups()
                funcname = method.upper() + '_' + name
                if hasattr(self, funcname):
                    func = getattr(self, funcname)
                    return func(*args)

        return self.notfound()

    def GET_index(self):
        status = '200 OK'
        response_headers = [('Content-type', 'text/plain')]
        self.start(status, response_headers)
        yield "Welcome!\n"

    def GET_hello(self, name): ##########修改点
        status = '200 OK'
        response_headers = [('Content-type', 'text/plain')]
        self.start(status, response_headers)
        yield "Hello %s!\n" % name

    def notfound(self):
        status = '404 Not Found'
        response_headers = [('Content-type', 'text/plain')]
        self.start(status, response_headers)
        yield "Not Found\n"

2、DRY

消除GET_*方法中的重复代码,并且允许它们返回字符串:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""application.py"""

import re

class my_app:

    urls = (
        ("/", "index"),
        ("/hello/(.*)", "hello"),
    )

    def __init__(self, environ, start_response): ##########修改点
        self.environ = environ
        self.start = start_response
        self.status = '200 OK'
        self._headers = []

    def __iter__(self): ##########修改点
        result = self.delegate()
        self.start(self.status, self._headers)

        # 将返回值result(字符串 或者 字符串列表)转换为迭代对象
        if isinstance(result, basestring):
            return iter([result])
        else:
            return iter(result)

    def delegate(self): ##########修改点
        path = self.environ['PATH_INFO']
        method = self.environ['REQUEST_METHOD']

        for pattern, name in self.urls:
            m = re.match('^' + pattern + '$', path)
            if m:
                # pass the matched groups as arguments to the function
                args = m.groups()
                funcname = method.upper() + '_' + name
                if hasattr(self, funcname):
                    func = getattr(self, funcname)
                    return func(*args)

        return self.notfound()

    def header(self, name, value): ##########修改点
        self._headers.append((name, value))

    def GET_index(self): ##########修改点
        self.header('Content-type', 'text/plain')
        return "Welcome!\n"

    def GET_hello(self, name): ##########修改点
        self.header('Content-type', 'text/plain')
        return "Hello %s!\n" % name

    def notfound(self): ##########修改点
        self.status = '404 Not Found'
        self.header('Content-type', 'text/plain')
        return "Not Found\n"

3、抽象出框架

为了将类my_app抽象成一个独立的框架,需要作出以下修改:

  • 剥离出其中的具体处理细节:urls配置 和 GET_*方法(改成在多个类中实现相应的GET方法)
  • 把方法header实现为类方法(classmethod),以方便外部作为功能函数调用
  • 改用 具有__call__方法的 实例 来实现application

修改后的application.py(最终版本):

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""application.py"""

import re

class my_app:
    """my simple web framework"""

    headers = []

    def __init__(self, urls=(), fvars={}):
        self._urls = urls
        self._fvars = fvars
        self._status = '200 OK'

    def __call__(self, environ, start_response):
        del self.headers[:] # 在每次作出响应前,清空上一次的headers
        result = self._delegate(environ)
        start_response(self._status, self.headers)

        # 将返回值result(字符串 或者 字符串列表)转换为迭代对象
        if isinstance(result, basestring):
            return iter([result])
        else:
            return iter(result)

    def _delegate(self, environ):
        path = environ['PATH_INFO']
        method = environ['REQUEST_METHOD']

        for pattern, name in self._urls:
            m = re.match('^' + pattern + '$', path)
            if m:
                # pass the matched groups as arguments to the function
                args = m.groups()
                funcname = method.upper() # 方法名大写(如GET、POST)
                klass = self._fvars.get(name) # 根据字符串名称查找类对象
                if hasattr(klass, funcname):
                    func = getattr(klass, funcname)
                    return func(klass(), *args)

        return self._notfound()

    def _notfound(self):
        self.status = '404 Not Found'
        self.header('Content-type', 'text/plain')
        return "Not Found\n"

    @classmethod
    def header(cls, name, value):
        cls.headers.append((name, value))

对应修改后的code.py(最终版本):

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""code.py"""

from wsgiref.simple_server import make_server
from application import my_app

urls = (
    ("/", "index"),
    ("/hello/(.*)", "hello"),
)

class index:
    def GET(self):
        my_app.header('Content-type', 'text/plain')
        return "Welcome!\n"

class hello:
    def GET(self, name):
        my_app.header('Content-type', 'text/plain')
        return "Hello %s!\n" % name

wsgiapp = my_app(urls, globals())

if __name__ == '__main__':
    httpd = make_server('', 8086, wsgiapp)
    sa = httpd.socket.getsockname()
    print 'http://{0}:{1}/'.format(*sa)

    # Respond to requests until process is killed
    httpd.serve_forever()

当然,您还可以在code.py中配置更多的URL映射,并实现相应的类来对请求作出响应。

六、参考

本文主要参考了 How to write a web framework in Python(作者 anandology 是web.py代码的两位维护者之一,另一位则是大名鼎鼎却英年早逝的 Aaron Swartz),在此基础上作了一些调整和修改,并掺杂了自己的一些想法。

如果您还觉得意犹未尽,Why so many Python web frameworks? 也是一篇很好的文章,也许它会让您对Python中Web框架的敬畏之心荡然无存:-)

[转载]SQL Server跨网段(跨机房)FTP复制 - 听风吹雨 - 博客园

mikel阅读(1430)

[转载]SQL Server跨网段(跨机房)FTP复制 – 听风吹雨 – 博客园.

一、 背景

搭建SQL Server复制的时候,如果网络环境是局域网内,通过主机名就可以实现了,但是如果是跨网段、跨机房异地搭建复制的时候就需要注意了,因为SQL Server复制不支持通过IP连接分发服务器,那有什么办法解决跨网段、跨机房的问题呢?

我在SQL Server跨网段(跨机房)复制已经讲到了两种解决方法,如果想用请求订阅模式,共享快照文件权限的配置比较麻烦,更好更安全的方式是通过FTP形式读取快照文件进行初始化;

 

二、 搭建过程

(一) 环境信息

系统环境:Windows Server 2008 + SQL Server 2008

发布服务器:192.168.1.101,1924,服务器名称:USER-H2B2A89PEK

分发服务器:与发布服务器同一台机器

订阅服务器:192.168.1.102,1433,服务器名称:QuZhoushiwei105

发布数据库:Task

订阅数据库:TaskSubscribe

数据库帐号:ReplicationUser/ ReplicationPassword

FTP地址:ftp://192.168.1.101:9721

FTP帐号密码:ftpuser/ftppassword

 

(二) 搭建步骤

开始下面的步骤之前你需要确认你的FTP地址是可用的,确保使用FTP客户端程序可以读取并下载生成的快照文件;

A. 发布服务器配置

下面是设置发布服务器的具体步骤:

wps_clip_image-2226

(Figure1:选择发布数据库)

wps_clip_image-23413

(Figure2:事务发布)

wps_clip_image-6041

(Figure3:选择对象)

wps_clip_image-16851

(Figure4:初始化订阅)

wps_clip_image-29542

(Figure5:设置帐号密码)

wps_clip_image-10587

(Figure6:发布名称)

wps_clip_image-21790

(Figure7:快照)

下图是设置FTP快照的主界面:

wps_clip_image-8162

(Figure8:设置FTP快照)

wps_clip_image-28832

(Figure9:重新发布)

wps_clip_image-22971

(Figure10:发布的快照文件)

wps_clip_image-281

(Figure11:内网FTP列表)

wps_clip_image-17630

(Figure12:外网FTP列表)

 

B. 订阅服务器配置

创建完发布服务器(分发服务器也一起创建了),接下来就可以创建订阅服务器了,在跨网段的情况下,你可以使用host文件或者别名的方式连接到分发服务器,具体可参考:SQL Server 跨网段(跨机房)复制,下面是订阅服务器设置的具体步骤:

wps_clip_image-26141

(Figure13:查找发布服务器)

wps_clip_image-11818

(Figure14:选择发布)

wps_clip_image-15965

(Figure15:请求订阅)

wps_clip_image-20899

(Figure16:选择订阅数据库)

wps_clip_image-5471

(Figure17:设置帐号密码)

wps_clip_image-13807

(Figure18:同步计划)

wps_clip_image-9122

(Figure19:初始化)

wps_clip_image-29782

(Figure20:创建订阅)

wps_clip_image-23811

(Figure21:订阅之前)

wps_clip_image-23050

(Figure22:订阅后)

 

三、 注意事项

1. Windows Server 2008系统中需要在入站规则中开通21端口才能搭建FTP;

2. FTP有主动和被动之分,所以在设置防火墙的时候需要注意;

 

四、 参考文献

SqlServer数据库同步方案详解(包括跨网段)

如何通过 FTP 传递快照

[转载]MS SQL 统计信息浅析上篇 - 潇湘隐者 - 博客园

mikel阅读(966)

[转载]MS SQL 统计信息浅析上篇 – 潇湘隐者 – 博客园.

统计信息概念

    统计信息是一些对象,这些对象包含在表或索引视图中一列或多列中的数据分布有关的统计信息。数据库查询优化器使用这些统计信息来估计查询结果中的基数或行 数。 通过这些基数估计,查询优化器可以生成高质量的执行计划。 例如,查询优化器可以使用基数估计选择索引查找运算符而不是耗费更多资源的索引扫描运算符,从而提高查询性能。[参考MSDN]

    其实如果你以前没有接触过统计信息,你可以将其看做是数据库为了得到最优的执行计划,统计数据库里面表、索引等对象的一些数据,例如表的记录数、所有列的 平均长度、直方图….等一些优化器需要用到的数据信息。SQL查询优化器是一个基于成本的优化器,类似于ORACLE里面的CBO,那么优化器如果要 得到成本最低的执行计划,就需要收集获取其生成执行计划的参考依据(统计信息数据)

如果你对这些概念性的东西比较模糊的话,那么为了让你形象的认识一下统计信息,请看下图:

clip_image002

 

统计信息参数

     数据库的统计信息相关参数有三个: 自动创建统计信息(Auto Create Statistics)、自动更新统计信息(Auto Update Statistics)、自动异步更新统计信息(Auto Update Statistics Asynchronously),它们都是数据库级别的。

自动创建统计信息(Auto Create Statistics

该参数指定数据库是否自动创建缺少的优化统计信息。如果设置为True, 则将在优化过程中自动生成优化查询需要但缺少的所有统计信息。当开启自动创建统计信息(Auto Create Statistics)选项时,查询优化器会对在谓词中使用到的列,如果这些列的统计信息不可用或缺少时,则会单独对每列创建统计信息。这些统计信息对创 建一个查询计划非常必要。它们创建于那些现有统计对象中不存在直方图的列上,名字包括列名和对象ID的十六进制格 式:_WA_Sys_<column_name>_<XXXX>。这些统计信息用于查询优化器决定使用何种优化后的执行计划。

自动更新统计信息(Auto Update Statistics


该参数指定数据库是否自动更新过期的优化统计信息。如果设置为True,则将在优化过程中自动生成优化查询需要但已过期的所有统计信息。否则不自动更新统计信息。

自动异步更新统计信息(Auto Update Statistics Asynchronously

如果设置为 True,则启动过期统计信息的自动更新,查询在编译前不会等待统计信息被更新。后续查询将使用可用的已更新统计信息。如果设置为 False,则启动过期统计信息的自动更新的查询将等待,直到更新的统计信息可在查询优化计划中使用。将该选项设置为 True 不会产生任何影响,除非自动更新统计信息也设置为 True

关于这三个参数,一般建议开启自动创建统计信息、自动更新统计信息选项,关闭自动异步更新统计信息。

    1:对于自动创建统计信息选项,因为统计信息对查询优化器至关重要,没有统计信息,也就失去了它基于成本的优化器的意义,成为无土之木、无源之水。

    2:对于自动更新统计信息选项,有时候过期的统计信息会导致严重的性能问题,我都碰到过好几起因为过时统计信息导致SQL查询性能问题的案例,就像错误的 地图、错误的导航、错误的指路会让你偏离目的地那样,过时的统计信息往往导致查询优化器选择了次优的执行计划,产生糟糕的性能问题,所以这个参数必须开 启。

    3:对于自动异步更新统计信息选项,这个选项在OLTP环境下很有用,但在数据仓库中有负面影响。至于是否开启,我建议是关闭。默认也是关闭的。不知道这种观念是否正确。

下面是MSDN给予的使用同步更新统计信息\异步更新统计信息的参考意见:

————————————————————————————————————————

在以下情况下应考虑使用同步统计信息:

· 您执行会更改数据分布的操作,例如截断表或对很大百分比的行执行大容量更新。如果您在完成该操作后未更新统计信息,则使用同步统计信息将确保对更改的数据执行查询前统计信息是最新的。

在以下情况下,考虑使用异步统计信息来实现可预测性更高的查询响应时间:

· 您的应用程序频繁执行相同的查询、类似的查询或类似的缓存查询计划。与同步统计信息更新相比,使用异步统计信息更新时您的查询响应时间可能具有更高的可预 测性,因为查询优化器可以执行传入的查询而不必等待最新的统计信息。这避免延迟某些查询,而不延迟其他查询。有关查找类似查询的详细信息,请参阅使用查询和查询计划哈希值查找和优化类似查询

· 您的应用程序遇到了客户端请求超时,这些超时是由于一个或多个查询正在等待更新后的统计信息所导致的。在某些情况下,等待同步统计信息可能会导致应用程序因过长超时而失败。

————————————————————————————————————————

那么先看一下如何通过SQL来查看这三个参数的设置值:

Code Snippet
  1. SELECT  name ,
  2.         is_auto_create_stats_on ,
  3.         is_auto_update_stats_async_on ,
  4.         is_auto_close_on
  5. FROM    sys.databases ;
  6. SELECT  CASE WHEN DATABASEPROPERTYEX(‘DBMonitor’, ‘IsAutoCreateStatistics’) = 1
  7.              THEN ‘Yes’
  8.              ELSE ‘No’
  9.         END AS ‘IsAutoCreateStatistics’ ,
  10.         CASE WHEN DATABASEPROPERTYEX(‘DBMonitor’, ‘IsAutoUpdateStatistics’) = 1
  11.              THEN ‘Yes’
  12.              ELSE ‘No’
  13.         END AS ‘IsAutoUpdateStatistics’ ,
  14.         CASE WHEN DATABASEPROPERTYEX(‘DBMonitor’, ‘Is_Auto_Update_stats_async_on’) = 1
  15.              THEN ‘Yes’
  16.              ELSE ‘No’
  17.         END AS ‘IsAutoUpdateStatsaAyncOn’
  18. GO

那么这三个参数的值保存在哪里呢?其实只要你稍微花一点心思去研究一下,就会发现 其实它是保存在系统表[sys].[sysdbreg]里面,[sys].[sysdbreg]是内部表,默认情况下不可查看,一般你可以通过系统视图 sys.database查看和研究其值的出处。

Code Snippet
  1. SET QUOTED_IDENTIFIER ON
  2. SET ANSI_NULLS ON
  3. GO
  4. CREATE VIEW sys.databases AS
  5.  SELECT d.name, d.id AS database_id,
  6.         r.indepid AS source_database_id,
  7.         d.sid AS owner_sid,
  8.         d.crdate AS create_date,
  9.         d.cmptlevel AS compatibility_level,
  10.         convert(sysname, CollationPropertyFromID(p.cid, ‘name’)) AS collation_name,
  11.         p.user_access, ua.name AS user_access_desc,
  12.         sysconv(bit, d.status & 0x400) AS is_read_only,        — DBR_RDONLY
  13.         sysconv(bit, d.status & 1) AS is_auto_close_on,        — DBR_CLOSE_ON_EXIT
  14.         sysconv(bit, d.status & 0x400000) AS is_auto_shrink_on,— DBR_AUTOSHRINK
  15.         p.state, st.name AS state_desc,
  16.         sysconv(bit, d.status & 0x200000) AS is_in_standby,    — DBR_STANDBY
  17.         sysconv(bit, d.status & 0x40000000) AS is_cleanly_shutdown, — DBR_CLEANLY_SHUTDOWN
  18.         sysconv(bit, d.status & 0x80000000) AS is_supplemental_logging_enabled,— DBR_SUPPLEMENT_LOG
  19.         p.snapshot_isolation_state, si.name AS snapshot_isolation_state_desc,
  20.         sysconv(bit, d.status & 0x800000) AS is_read_committed_snapshot_on,— DBR_READCOMMITTED_SNAPSHOT
  21.         p.recovery_model, ro.name AS recovery_model_desc,
  22.         p.page_verify_option, pv.name AS page_verify_option_desc,
  23.         sysconv(bit, d.status2 & 0x1000000) AS is_auto_create_stats_on,        — DBR_AUTOCRTSTATS
  24.         sysconv(bit, d.status2 & 0x40000000) AS is_auto_update_stats_on,       — DBR_AUTOUPDSTATS
  25.         sysconv(bit, d.status2 & 0x80000000) AS is_auto_update_stats_async_on, — DBR_AUTOUPDSTATSASYNC
  26.         sysconv(bit, d.status2 & 0x4000) AS is_ansi_null_default_on,           — DBR_ANSINULLDFLT
  27.         sysconv(bit, d.status2 & 0x4000000) AS is_ansi_nulls_on,               — DBR_ANSINULLS
  28.         sysconv(bit, d.status2 & 0x2000) AS is_ansi_padding_on,                — DBR_ANSIPADDING
  29.         sysconv(bit, d.status2 & 0x10000000) AS is_ansi_warnings_on,           — DBR_ANSIWARNINGS
  30.         sysconv(bit, d.status2 & 0x1000) AS is_arithabort_on,                  — DBR_ARITHABORT
  31.         sysconv(bit, d.status2 & 0x10000) AS is_concat_null_yields_null_on,    — DBR_CATNULL
  32.         sysconv(bit, d.status2 & 0x800) AS is_numeric_roundabort_on,           — DBR_NUMEABORT
  33.         sysconv(bit, d.status2 & 0x800000) AS is_quoted_identifier_on,         — DBR_QUOTEDIDENT
  34.         sysconv(bit, d.status2 & 0x20000) AS is_recursive_triggers_on,         — DBR_RECURTRIG
  35.         sysconv(bit, d.status2 & 0x2000000) AS is_cursor_close_on_commit_on,   — DBR_CURSCLOSEONCOM
  36.         sysconv(bit, d.status2 & 0x100000) AS is_local_cursor_default,         — DBR_DEFLOCALCURS
  37.         sysconv(bit, d.status2 & 0x20000000) AS is_fulltext_enabled,           — DBR_FTENABLED
  38.         sysconv(bit, d.status2 & 0x200) AS is_trustworthy_on,                  — DBR_TRUSTWORTHY
  39.         sysconv(bit, d.status2 & 0x400) AS is_db_chaining_on,                  — DBR_DBCHAINING
  40.         sysconv(bit, d.status2 & 0x08000000) AS is_parameterization_forced,    — DBR_UNIVERSALAUTOPARAM
  41.         sysconv(bit, d.status2 & 64) AS is_master_key_encrypted_by_server,     — DBR_MASTKEY
  42.         sysconv(bit, d.category & 1) AS is_published,
  43.         sysconv(bit, d.category & 2) AS is_subscribed,
  44.         sysconv(bit, d.category & 4) AS is_merge_published,
  45.         sysconv(bit, d.category & 16) AS is_distributor,
  46.         sysconv(bit, d.category & 32) AS is_sync_with_backup,
  47.         d.svcbrkrguid AS service_broker_guid,
  48.         sysconv(bit, case when d.scope = 0 then 1 else 0 end) AS is_broker_enabled,
  49.         p.log_reuse_wait, lr.name AS log_reuse_wait_desc,
  50.         sysconv(bit, d.status2 & 4) AS is_date_correlation_on,         — DBR_DATECORRELATIONOPT
  51.         sysconv(bit, d.category & 64) AS is_cdc_enabled,
  52.         sysconv(bit, d.status2 & 0x100) AS is_encrypted,               — DBR_ENCRYPTION
  53.         sysconv(bit, d.status2 & 0x8) AS is_honor_broker_priority_on   — DBR_HONORBRKPRI
  54.     FROM master.sys.sysdbreg d OUTER APPLY OpenRowset(TABLE DBPROP, d.id) p
  55.     LEFT JOIN sys.syssingleobjrefs r ON r.depid = d.id AND r.class = 96 AND r.depsubid = 0— SRC_VIEWPOINTDB
  56.     LEFT JOIN sys.syspalvalues st ON st.class = ‘DBST’ AND st.value = p.state
  57.     LEFT JOIN sys.syspalvalues ua ON ua.class = ‘DBUA’ AND ua.value = p.user_access
  58.     LEFT JOIN sys.syspalvalues si ON si.class = ‘DBSI’ AND si.value = p.snapshot_isolation_state
  59.     LEFT JOIN sys.syspalvalues ro ON ro.class = ‘DBRO’ AND ro.value = p.recovery_model
  60.     LEFT JOIN sys.syspalvalues pv ON pv.class = ‘DBPV’ AND pv.value = p.page_verify_option
  61.     LEFT JOIN sys.syspalvalues lr ON lr.class = ‘LRWT’ AND lr.value = p.log_reuse_wait
  62.     WHERE d.id < 0x7fff
  63.         AND has_access(‘DB’, d.id) = 1
  64. GO
  65. CREATE TABLE [sys].[sysdbreg]
  66.     (
  67.       [id] [int] NOT NULL ,
  68.       [name] [sys].[sysname] NOT NULL ,
  69.       [sid] [varbinary](85) NULL ,
  70.       [status] [int] NOT NULL ,
  71.       [status2] [int] NOT NULL ,
  72.       [category] [int] NOT NULL ,
  73.       [crdate] [datetime] NOT NULL ,
  74.       [modified] [datetime] NOT NULL ,
  75.       [svcbrkrguid] [uniqueidentifier] NOT NULL ,
  76.       [scope] [int] NOT NULL ,
  77.       [cmptlevel] [tinyint] NOT NULL
  78.     )
  79.   ON[PRIMARY]
  80. GO

修改统计信息参数

方法1

—-关闭数据库DBMonitor自动创建统计信息功能

USE [master]

GO

ALTER DATABASE [DBMonitor] SET AUTO_CREATE_STATISTICS OFF WITH NO_WAIT

GO

–开启数据库DBMonitor自动创建统计信息功能

USE [master]

GO

ALTER DATABASE [DBMonitor] SET AUTO_CREATE_STATISTICS ON WITH NO_WAIT

GO

–关闭数据库DBMonitor自动更新统计信息功能

USE [master]

GO

ALTER DATABASE [DBMonitor] SET AUTO_UPDATE_STATISTICS OFF WITH NO_WAIT

GO

–启用数据库DBMonitor自动更新统计信息功能

USE [master]

GO

ALTER DATABASE [DBMonitor] SET AUTO_UPDATE_STATISTICS ON WITH NO_WAIT

GO

–关闭数据库DBMonitor自动异步更新统计信息功能

ALTER DATABASE [DBMonitor] SET AUTO_UPDATE_STATISTICS_ASYNC OFF WITH NO_WAIT

GO

 

–启用数据库DBMonitor自动异步更新统计信息功能

ALTER DATABASE [DBMonitor] SET AUTO_UPDATE_STATISTICS_ASYNC ON WITH NO_WAIT

GO

方法2:使用SP_DBOPTION来启用或禁用。

 

SP_DBOPTION DBMonitor, ‘auto update statistics’, ‘ON’;

SP_DBOPTION DBMonitor, ‘auto update statistics’, ‘OFF;

 

方法3:图形化方法启用或禁用

对应的图像化操作:选择所要修改的数据库,单击右键选项”属性“,选择左侧的”选项“,则能看到这三个参数

clip_image004

在 AUTO_UPDATE_STATISTICS 为 ON 时,您可以覆盖数据库范围的统计信息更新行为,并且根据您的应用程序的要求为单独的表、索引或列将自动统计信息更新设为关闭。在 AUTO_UPDATE_STATISTICS 为 ON 时,您可以通过以下方式为表、索引或列禁用和重新启用自动统计信息更新:

· 使用 SP_AUTOSTATS 系统存储过程。这可以禁用或重新启用表或索引的统计信息更新。

· 对于 UPDATE STATISTICS 语句指定 NORECOMPUTE 选项。若要重新启用统计信息更新,请重新运行 UPDATE STATISTICS,但不使用

· NORECOMPUTE 选项。

· 对于 CREATE STATISTICS 语句指定 NORECOMPUTE 选项。若要重新启用统计信息更新,请使用 DROP STATISTICS 删除统计信息,然后运行 CREATE STATISTICS 但不使用 NORECOMPUTE 选项。

· 对 CREATE INDEX 语句指定 STATISTICS_NORECOMPUTE 选项。若要重新启用统计信息更新,您可以运行 ALTER INDEX 且 STATISTICS_NORECOMPUTE = OFF。

· 在 AUTO_UPDATE_STATISTICS 为 OFF 时,不能为单独的表、索引或列将自动更新设为打开。重新启用自动统计信息更新将还原AUTO_UPDATE_STATISTICS 选项指定的行为。如果 AUTO_UPDATE_STATISTICS 选项为OFF,统计信息更新将不会发生。

 

创建统计信息

如何创建统计信息呢,如果选项自动创建统计信息(Auto Create Statistics)开启了,那么数据库会自动创建某些统计信息,另外你也可以通过CREATE STATISTICS 语句手动创建某些统计信息。先看下面的例子:

USE [DBMonitor]

GO

IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Test]’) AND type in (N’U’))

DROP TABLE [dbo].[Test]

GO

SELECT * INTO Test FROM sys.objects;

此时你会看到表Test根本没有统计信息,那么我们在表Test上创建一个索引IDX_TEST_OBJECT_ID

CREATE INDEX IDX_TEST_OBJECT_ID ON dbo.Test(object_id);

此时,你刷新一下表Test的统计信息,你会发现多了一个名为“IDX_TEST_OBJECT_ID”的统计信息,你新建多少条索引,在统计信息下就会创建与索引同名的统计信息。

SELECT DISTINCT TYPE FROM dbo.Test ;

执行上面面脚本后,我刷新统计信息,会发现多了一个名为“_WA_Sys_00000006_023D5A04”的统计信息,截图如下:

clip_image006

那么自动创建统计信息的规律或规则是啥呢?或者理解为:什么时候数据库创建统计信息,其实创建统计信息的规则如下:

    1:在索引创建时,SQL SERVER 会自动地在索引所在的列上创建统计信息

    2:当SQL SERVER想要使用某些列上的统计信息,发现没有时,SQL SERVER会自动创建统计信息(前提是要开启自动创建统计信息)。例如上面统计DISTINCT TYPE。

    3:手工使用CREATE STATISTICS之类的语句手工创建需要的统计信息。

关于CREATE STATISTICS的语法,大家可以参考MSDN,这里不做阐述了。需要注意的是统计信息可以通过全表扫描或随机抽样应读取的数据百分比或指定的数据行数,收集统计信息

更新统计信息

随着数据库的DML操作,数据的变更会导致统计信息过期,那么这时就需要更新统计信息。通常数据库通过两种方式更新统计信息:

    1:如果开启了自动更新统计信息(Auto Update Statistics)或自动异步更新统计信息选项,那么数据库会自动更新统计信息。

    2:手动更新统计信息

更新统计信息确保查询使用最新的统计信息编译。不过,更新统计信息会导致查询重新编译。我们建议不要太频繁地更新统计信息,因为需要在改进查询计划和重新编译查询所用时间之间权衡性能。此类特定的性能权衡取决于您的应用程序。

那么数据库什么时候、什么条件下才会更新统计信息呢?

1、 在一个空表中有数据的改动。

2、 当统计信息创建时,表的行数只有500或以下,且后来统计对象中的引导列的更改次数大于500.

3、 当表的统计信息收集时,超过了500行,且统计对象的引导列后来更改次数超过500+表总行数的20%时。

4、 在Tempdb中的表,少于6行且最少有6行被更改

注意:数据库不会为表变量收集统计信息,所以数据量比较大时尽量不要使用表变量。

一般建议不要太频繁地更新统计信息,因为需要在改进查询计划和重新编译查询所用时间之间权衡性能。 这种特定的性能权衡取决于您的应用程序。

手工更新统计信息,一般使用 UPDATE STATISTICS 或存储过程 sp_updatestats 来更新统计信息。

sp_autostats:显示或更改特定索引或统计信息的自动 UPDATE STATISTICS 设置,或者显示和更改当前数据库中指定表或索引视图的所有索引和统计信息的自动 UPDATE STATISTICS 设置

  显示表的所有索引的当前状态

    USE DBMonitor;

    GO

    EXEC sp_autostats ‘dbo.test’;

  启用表的所有索引的自动统计信息

    USE DBMonitor;

    GO

    EXEC sp_autostats ‘dbo.test’,’ON’

  禁用特定索引的自动统计信息

    USE DBMonitor;

    GO

    EXEC sp_autostats ‘dbo.test’,’OFF’, ‘IDX_TEST_OBJECT_ID’;

 

查看统计信息

如果要了解具体的统计信息内容,那么我们首先要知道如何查看具体的统计信息,统计信息保存在那些系统视图里面,如果能很好的回答这两个问题,那么我想你也就能知道统计信息的具体内容是那些了。关于第二个问题,后面章节部分再做探讨。

查看统计信息,我们先由浅入深,由简单到复杂。

sp_helpstats :返回指定表中列和索引的统计信息。

USE DBMonitor;

GO

EXEC sp_helpstats ‘test’ ,’ALL’

下面我们看看sys.sp_helpstats的脚本

View Code

 

2:查看统计信息一般用DBCC SHOW_STATISTICS命令,如下所示

复制代码
DBCC SHOW_STATISTICS('TEST', 'IDX_TEST_OBJECT_ID'); 

Name    Updated     Rows   Rows Sampled  Steps  Density     Average key length String Index Filter Expression           Unfiltered Rows
------------------------------------------------- ------------------
IDX_TEST_OBJECT_ID   09 24 2013  9:06PM   59                   59                   41     1             4                  NO           NULL   59

(1 行受影响)

All density   Average Length Columns
------------- -------------- ---------------------------------------
0.01694915    4              object_id

(1 行受影响)

RANGE_HI_KEY RANGE_ROWS    EQ_ROWS       DISTINCT_RANGE_ROWS  AVG_RANGE_ROWS
------------ ------------- ------------- -------------------- --------------
          0             1             0                    1
          1             1             1                    1
          0             1             0                    1
          0             1             0                    1
          0             1             0                    1
          0             1             0                    1
          1             1             1                    1
          1             1             1                    1
          0             1             0                    1
          0             1             0                    1
          0             1             0                    1
          0             1             0                    1
          0             1             0                    1
          1             1             1                    1
          0             1             0                    1
          0             1             0                    1
          0             1             0                    1
          1             1             1                    1
          0             1             0                    1
          0             1             0                    1
          2             1             2                    1
          2             1             2                    1
          1             1             1                    1
          0             1             0                    1
          0             1             0                    1
          0             1             0                    1
          0             1             0                    1
          1             1             1                    1
          1             1             1                    1
          1             1             1                    1
          1             1             1                    1
          0             1             0                    1
          0             1             0                    1
          1             1             1                    1
          0             1             0                    1
          1             1             1                    1
          1             1             1                    1
          1             1             1                    1
          0             1             0                    1
          0             1             0                    1
          0             1             0                    1

(41 行受影响)

DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。
复制代码

 

虽然查看统计信息很容易,但是要读懂并能读取一些信息那们就不是那么简单的了。

列名 描述说明
Name 统计信息对象名称
Update 上一次更新统计信息的日期和时间
Rows 在目标索引、统计信息或列中的总行数。如果筛选索引或统计信息,此行数可能小于表的行数。
Rows Sampled 用于统计信息计算的抽样总行数。
Steps 统计信息对象第一个键列的直方图中的值范围数。每个步骤包括在直方图结果中定义的 RANGE_ROWS 和 EQ_ROWS。
Density 查询优化器不使用此值。显示此值的唯一目的是为了向后兼容。密度的计算公式为 1 / distinct rows,其中 distinct rows 是直方图输出中所有步骤的 DISTINCT_RANGE_ROWS 之和。如果对行进行抽样,distinct rows 则基于抽样行的直方图值。
Average Key Length 统计信息对象的键列中,所有抽样值中的每个值的平均字节数
String Index 如果为“是”,则统计信息中包含字符串摘要索引,以支持为 LIKE 条件估算结果集大小。仅当第一个键列的数据类型为charvarcharncharnvarcharvarchar(max)nvarchar(max)textntext 时,才会对此键列创建字符串索引。
Filter Expression 包含在统计信息对象中的表行子集的表达式。NULL = 未筛选的统计信息。有关详细信息,请参阅筛选统计信息。
Unfiltered Rows 应用筛选器表达式前表中的总行数。如果 Filter Expression 为 NULL,Unfiltered Rows 等于行标题值。

下表对指定 DENSITY_VECTOR 时结果集中所返回的列进行了说明。

列名 说明
All Density 针对统计信息对象中的列的每个前缀计算密度(1/ distinct_rows)。 密度包含所有抽样行中的非重复行,包括带有直方图边界点的行。结果为每个密度显示一行。例如,如果统计信息对象包含键列 (A, B, C),结果将报告 (A)、(A,B) 以及 (A, B, C) 的密度。非重复行具有一个不同的列值向量。对于列 (A,B,C),两个不同的向量值的示例为 (4,5,6) 和 (4,5,7)。对于 (A,B),相同的两行具有一个不同的向量值 (4,5)。对于 (A),存在一个不同的值 (4)。
Average Length 每个列前缀的列值向量的平均长度(按字节计)。例如,如果列前缀为列 A 和 B,则长度为列 A 和列 B 的字节之和。
Columns 为其显示 All densityAverage length 的前缀中的列的名称。

下表对指定 HISTOGRAM 选项时结果集中所返回的列进行了说明。

列名

说明
RANGE_HI_KEY 直方图步骤的上限值。
RANGE_ROWS 表中位于直方图步骤内(不包括上限)的行的估算数目。
EQ_ROWS 表中值与直方图步骤的上限值相等的行的估算数目。
DISTINCT_RANGE_ROWS 直方图步骤内(不包括上限)非重复值的估算数目。
AVG_RANGE_ROWS 直方图步骤内(不包括上限)重复值的频率或平均数目(如果 DISTINCT_RANGE_ROWS > 0,则为 RANGE_ROWS / DISTINCT_RANGE_ROWS)。

统计直方图用作在查询执行计划中查询优化器的选择依据

图形化查看

image

删除统计信息

DROP STATISTICS 删除当前数据库的指定表中的多个集合的统计信息。

    DROP STATISTICS Test._WA_Sys_00000006_023D5A04;

需要注意的是不能用DROP STATISTICS 删除有关索引的统计信息。统计信息的保留时间与索引存在的时间相同,当你删除索引时,对应的统计信息也自动删除。如下所示:

DROP STATISTICS Test.IDX_TEST_OBJECT_ID

消息 3739,级别 11,状态 1,第 1 行

无法对索引 ‘Test.IDX_TEST_OBJECT_ID’ 执行 DROP,因为该索引不是统计信息集合。

 

参考资料:

  http://technet.microsoft.com/zh-cn/library/ms190397(v=sql.105).aspx

http://technet.microsoft.com/zh-cn/library/ms187348.aspx
http://blog.csdn.net/dba_huangzj/article/details/8041267
http://blog.csdn.net/zhaowenzhong/article/details/6276878

[转载]微信公众平台Bee.WeiXin开发介绍 - 蜂 - 博客园

mikel阅读(990)

[转载]微信公众平台Bee.WeiXin开发介绍 – 蜂 – 博客园.

我们来看一下如何通过Bee.WeiXin开发微信公众平台。关于微信公众平台的一般性介绍, 这里不做展开。 园里找一找就可以了。 本文主要是介绍Bee.WeXin, 代码已发布到https://beeweixin.codeplex.com/上了。

BeeWeiXin 是针对腾讯的微信公众平台开发一个开发框架。  该项目是基于Bee OPOA Platform 开发的。

包含了以下功能:

1. 基于调用树的微信答复模型。 调用树支持多级(有上下文)响应,  可以通过 文本, 图文, 自定义三种方式响应。 其中自定义可以回复微信公众平台API所提供的三个方式(文本, 图文, 音乐)。

2. 提供了同步微信关注用户列表的功能。

3. 提供了菜单管理功能。

4. 提供了图文管理功能。

5. 原生集成Bee OPOA Platform上的所有功能, 主要是权限管理。

关于调试, 推荐采用园友提供的工具 微信公众帐号开发调试工具发布

预览

配置项说明

配置项均在web.config中【appSettings】配置.

【WeiXinToken】就是公众平台API方式的Token;

Debug】是指当前是否处于调试模式。 调试模式与非调试模式的区别就在于是否对发起的调用进行验证。

【WeiXinUserName】与【WeiXinPassword】 是菜单【微信关注用户】中【重新同步所有用户】所需要的, 将模拟网页登入到微信公众平台中, 然后获取用户列表信息。 若不需要, 可以忽略, 也可以隐藏掉该菜单。

【WeiXinAppId】与【WeiXinAppSec】是菜单【微信菜单管理】中所需要的, 可以管理微信公众账号的菜单。 当然该工具【微信公众帐号开发调试工具发布 】也有此功能, 比较方便。 若不需要, 可以忽略, 也可以隐藏掉该菜单。

【WeiXinController】是默认的系统响应Controller, 具体逻辑请参看代码。

调用链方式的应答

调用链是基于上下文的, 在配置项中有个选项【是否进入调用链】若是的话, 则将此次应答加入调用链中, 以形成上下文。 如【菜单1】是要进入调用链的, 不然该项下面的子项将无法响应。

先看以下图片:

由预览图中, 可以直观的了解调用链可以提供 文本、图文、自定义三种响应。

文本就是一般性的文本应答。 如在Bee.WeiXinDemo项目中已配置了【菜单1】是以文本响应, 节点值是V1001_M1_1。

回复1,test1
回复2,test2
回复3,test3

回复得到如下:

图文就是微信公众平台API中提供的图文信息, 在调用链配置中只要按照顺序输入指定的文章号【该号码是微信文章管理中的Id】就可以了(以逗号分隔)。

如【菜单1】下的test2是图文格式。 进入test2需要先触发菜单1, 然后再触发test2. 如下图:

自定义是融合到Bee OPOA Platform框架中MVC实现的。 所以配置的也是通过Controller, Action来实现。

如【菜单1】下的test3是自定义方式。进入test3需要先触发菜单1, 然后再触发test3. 如下图:

 

不知道各位了解了没有? 建议大家下载源码, 调试下以加深理解, 最好是有空看看代码, 以帮助完善该项目, 呵呵。

【菜单1】下面只有键值【1】,【2】,【3】, 若用户输入其他怎么响应呢?这个时候只要配置键值【*】, 作为其他的配置项应答。

理解调用链上下文

在这种基于调用链的应答中, 上下文是必要的。 怎么理解呢?举例说明吧:我要查询某个产品的某个国家的最近3个月的销售情况。 由于微信平台的特性, 我们不可能像其他应用给用户以直观的查询。

我们只有采用引导式的方式去引导用户得到她想要的结果。 设计的菜单如预览图中【多级菜单】项所示。具体的配置项如下图:

当用户点击菜单按钮触发了【V1001_M1_2】的事件, 系统响应提示文本【上图的1】; 用户根据提示, 选择产品, 输入1, 系统提示输入国家【上图的2】。 由于国家太多, 不太可能配置所有国家的选项, 所有采用了一个通配符的方案【上图的3】, 并采用自定义的方式应答。  代码如下:

复制代码
public WeiXinTextResult CheckCountry()
        {
            string content = ViewData.TryGetValue<string>("content", string.Empty);

            // 验证输入的国家是否合法
            bool validFlag = content.IndexOf("CN") >= 0;

            if (validFlag)
            {
                return WeiXinText("1 最近3个月销售统计\r\n2 最近6个月销售统计");
            }
            else
            {
                // 由于调用链有上下文, 用户输入错误, 需要将当前应答取消。Current.MessageStack.Pop();
                Current.MessageStack.Pop();
                return WeiXinText("国家不合法,请重新输入国家。");
            }
        }
复制代码

由以上代码可以知道上下文可以通过Current.MessageStack获取。

用户输入正确的国家代码, 则引导用户做下一个选择.

自定义MVC响应

这个基本上跟Bee OPOA Platform的方式一模一样。 如【菜单1】下的【Other】项的响应就是采用该方式的。代码如下:

public ActionResult Other()
        {
            return View();
        }

View代码如下:

复制代码
<%@ Page Language="C#" AutoEventWireup="false" Inherits="Bee.Web.BeePageView" %>

<%@ Import Namespace="Bee.Web" %>
<%@ Import Namespace="Bee" %>
<%@ Import Namespace="System.Collections.Generic" %>
<xml>
 <ToUserName><![CDATA[<%=ViewData["FromUserName"] %>]]></ToUserName>
 <FromUserName><![CDATA[<%=ViewData["ToUserName"] %>]]></FromUserName>
 <CreateTime><%=Bee.WeiXin.DateTimeUtil.GetWeixinDateTime(DateTime.Now) %></CreateTime>
 <MsgType><![CDATA]></MsgType>
 <Content>回复1 联系方式 
 回复2 在线咨询</Content>
</xml>
复制代码

框架基类 Bee.WeiXin.Controller.WeiXinControllerBase提供了微信公众平台API所对应的三种响应的ActionResult, 具体请查看源码.

总结

着重解释了调用链的方式。 其他的功能就不说明了, 下载源码, 很容易知道他的用途。 Enjoy!

[转载]可在广域网部署运行的QQ高仿版 -- GG叽叽V2.0,增加网盘和远程磁盘功能(源码) - GG叽叽 - 博客园

mikel阅读(942)

[转载]可在广域网部署运行的QQ高仿版 — GG叽叽V2.0,增加网盘和远程磁盘功能(源码) – GG叽叽 – 博客园.

尽力2~3周发布一个版本,我这次也没有失言。这段时间内,我仿照QQ的微云功能,在GG中增加了网盘的功能,而且,我还自创了一个QQ没有的新的功能:远程磁盘。正如远程桌面一样,远程磁盘允许我们像访问本地磁盘一样来访问在线的其它用户的磁盘。

一.GG V2.0 新增功能展现

(1)网盘:在服务端为每个用户分配一个网盘,用户通过客户端可以访问自己的网盘。就像QQ的微云一样。

(2)远程磁盘:任何一个在线用户,都可以访问其它在线用户的磁盘(先要经过对方的同意)。

(3)无论是网盘、还是远程磁盘,都支持:上传/下载/删除/复制/剪切/粘贴 文件、新建文件夹、重命名。

(4)在GG的实现中,网盘和远程磁盘公用的是同一个组件NDiskBrowser。

废话不多说,还是先上图。新功能入口按钮:

上图中有三处标记:1.按钮可进入 我的网盘;2.按钮可进入 好友的远程磁盘;3.显示和好友之间的P2P通道的状态

下图是网盘截图:

磁盘访问请求截图:

进入远程磁盘(就像打开自己本地硬盘一样):

远程磁盘操作:

 

二.实现思路

虽然提供了源代码,但是,我还是想将主要的思路列一下,这样,大家理解起源码来,会节省更多的时间。

1. 该版本增加了一个新的项目GGLib。

我打算将GGLib作为一个可复用的类库,这样它就可以在GG以外其它的项目中使用。目前,GGLib已经包含了修改后的文件传输显示控件FileTransferingViewer以及网盘组件。

2. 网盘组件:位于GGLib项目的NDisk文件夹下,虽然网盘组件的底层仍然基于ESFramework提供的文件传输功能,但是,其实现还是比较复杂的。

(1)通过网盘组件,既可以访问网盘,也可以访问另一个在线用户的磁盘。

(2)目前版本的GG服务端提供的是一个简单的网盘功能,其在运行目录下的NetworkDisk文件夹下,为需要的用户新建一个文件夹(以UserID命名)作为用户的网盘。

(3)如果需要,我们可以重新实现GGLib.NDisk.Server.INDiskPathManager接口,比如,我们可以使用分布式文件系统,作为网盘的后端存储。

(4)普通文件传送和网盘文件传送,都使用了ESFramework的文件传送功能,在程序中,我是通过TransferingProject的Comment属性来区分二者的(普通文件的Comment为null,网盘则不为null)。

(5)普通文件和网盘文件,也都使用了FileTransferingViewer来显示文件传送进度,我改写了原始的FileTransferingViewer,为其Initialize增加了一个filter参数,使其可以过滤掉无关的文件传送事件。

(6)进入远程磁盘时,默认进入的是“我的电脑”的内容 — 列出所有分区、包括光盘、U盘等,我们可以通过修改程序来改变这一行为,比如,磁盘的主人可以控制自己只允许来访者访问哪一个分区或目录。

(7)基于(6),换个方向,我们就可以实现磁盘共享或目录共享的功能。

3. 数据库:有很多朋友问数据库怎么弄,实际上GG的目前版本还没有用到数据库(后续高级版本会增加),所有的信息都只是在内存中,所以,目前版本的GG做了一些假设:

(1)用户登录帐号随意,但必须为数字组;密码可随意输入。

(2)所有的在线用户都是好友。

4. 语音视频:也有很多朋友问语音视频设备的工作怎么不正常,这个可以直接参考OMCS官方文档:摄像头、麦克风、扬声器设备测试

5. GG使用了最新版本的SkinForm,如果有关于SkinForm的问题,可以直接联系我的好友 威廉乔克斯_汀

 

三.新的想法

有了远程磁盘点子之后,我又产生了一个新的点子,这个新点子的孕育过程是这样的:

我经常晚上在家加班,所以,下班的时候,我就用U盘把项目文档拷回去,在家里的电脑上工作,完毕后,再把更新的文档拷回U盘,第二天再带到公司覆盖 公司电脑上旧的文档。相信有些朋友也有类似的经历,用U盘拷来拷去很麻烦。当然,如果不是保密性的要求,我们也可以使用网盘。无论是U盘还是网盘,都需要 经过“中间站”转一道,不够直接。

如果下面的情形能够实现该多好:下班的时候,我在公司的电脑上能够直接访问家里电脑的磁盘,把需要的文档传送到指定的目录,晚上回去就可以继续工作,完毕后,在家里的电脑上又可以直接访问公司电脑的磁盘,把更新后的文档再传送回去。这样就够直接了,也不需要U盘了。

我想在GG中增加这样的功能,那么,该如何实现了?我 的初步方案如下:增加一个新的项目叫GG受控端,受控端不需要主界面,只要一个托盘,表示其在运行。在家里的电脑上,我用帐号514330登录GG受控 端,到公司后,用514330正常登录GG,在GG的主界面上,就可以列出所有在线的受控端,点击其中一个,就可以访问其磁盘,这就是已有的GG的远程磁 盘的功能了。当前,前提是受控端电脑必需是开启的并且是联网的。

关于这个功能,大家有什么好的想法,可以留言告诉我。

 

四.GG V2.0 源码下载

GG V2.0 源码

 

特别说明:为了减少压缩包的大小(博客园最大上传文件只能为10M,这个限制有点过了),我把Debug目录清空了,大家重新编译生产后,请将Dlls文件夹下的三个dll(VideoEngine.dll,VideoEngineCore.dll,AudioEngineCore.dll)拷贝到运行那个目录下,才可正常启动程序的。

 

我会继续努力争取2~3个星期发布一个新版本,使GG慢慢成熟起来。

大家有什么问题和建议,可以留言,也可以发送email到我邮箱:ggim2013@163.com。

如果你觉得还不错,请粉我,顺便再顶一下啊,呵呵

转载Java中toArray的用法探究java数组与list转换 - 杜红光 - 博客园

mikel阅读(1009)

转载(转)Java中toArray的用法探究java数组与list转换 – 杜红光 – 博客园.

一.             Incident

import java.util.ArrayList;

import java.util.List;

 

public class Test {

 

public static void main(String[] args) {

List<String> list = new ArrayList<String>();

list.add(“1”);

list.add(“2”);

String[] tt =(String[]) list.toArray(new String[0]);

 

}

 

}

 

这段代码是没问题的,但我们看到String[] tt =(String[]) list.toArray(new String[0]) 中的参数很奇怪,然而去掉这个参数new String[0]却在运行时报错。。。

 

二.             Root Cause Analysis

经研究发现toArray有两个方法:

public Object[] toArray() {

Object[] result = new Object[size];

System.arraycopy(elementData, 0, result, 0, size);

return result;

}

不带参数的toArray方法,是构造的一个Object数组,然后进行数据拷贝,此时进行转型就会产生ClassCastException,这也就是上述问题的root cause了。

 

 

public Object[] toArray(Object a[]) {

if (a.length < size)

a = (Object[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);       System.arraycopy(elementData, 0, a, 0, size);

if (a.length > size)

a[size] = null;

return a;

}

而带参数的toArray方法,则是根据参数数组的类型,构造了一个对应类型的,长度跟ArrayList的size一致的空数组,虽然方法本身还 是以 Object数组的形式返回结果,不过由于构造数组使用的ComponentType跟需要转型的ComponentType一致,就不会产生转型 异常。

 

三.             Solutions

因此在使用toArray的时候可以参考以下三种方式

1. Long[] l = new Long[<total size>];

list.toArray(l);

 

2. Long[] l = (Long[]) list.toArray(new Long[0]);

 

3. Long[] a = new Long[<total size>];

Long[] l = (Long[]) list.toArray(a);

 

四.Further Consideration

该容器中的元素已经用泛型限制了,那里面的元素就应该被当作泛型类型的来看了,然而在目前的java中却不是的,当直接String[] tt =(String[]) list.toArray()时,运行报错。回想一下,应该是java中的强制类型转换只是针对单个对象的,想要偷懒,将整个数组转换成另外一种类型的数 组是不行的,这和数组初始化时需要一个个来也是类似的。

 

以上From:    http://hi.baidu.com/%B4%CB%D6%D0%D3%D0%D5%E6%B5%C0/blog/item/8add93ec812dc9deb31cb1b0.html

 

[转载]Android高效加载大图、多图解决方案,有效避免程序OOM - guolin的专栏 - 博客频道 - CSDN.NET

mikel阅读(848)

[转载]Android高效加载大图、多图解决方案,有效避免程序OOM – guolin的专栏 – 博客频道 – CSDN.NET.

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9316683

 

本篇文章主要内容来自于Android Doc,我翻译之后又做了些加工,英文好的朋友也可以直接去读原文。

 

http://developer.android.com/training/displaying-bitmaps/index.html

 

高效加载大图片

我们在编写Android程序的时候经常要用到许多图片,不同图片总是会有不同的形状、不同的大小,但在大多数情况下,这些图片都会大于我们程序所需要的 大小。比如说系统图片库里展示的图片大都是用手机摄像头拍出来的,这些图片的分辨率会比我们手机屏幕的分辨率高得多。大家应该知道,我们编写的应用程序都 是有一定内存限制的,程序占用了过高的内存就容易出现OOM(OutOfMemory)异常。我们可以通过下面的代码看出每个应用程序最高可用内存是多 少。

  1. int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);  
  2. Log.d(“TAG”“Max memory is “ + maxMemory + “KB”);  

因 此在展示高分辨率图片的时候,最好先将图片进行压缩。压缩后的图片大小应该和用来展示它的控件大小相近,在一个很小的ImageView上显示一张超大的 图片不会带来任何视觉上的好处,但却会占用我们相当多宝贵的内存,而且在性能上还可能会带来负面影响。下面我们就来看一看,如何对一张大图片进行适当的压 缩,让它能够以最佳大小显示的同时,还能防止OOM的出现。

BitmapFactory这个类提供了多个解析方法(decodeByteArray, decodeFile, decodeResource等)用于创建Bitmap对象,我们应该根据图片的来源选择合适的方法。比如SD卡中的图片可以使用decodeFile方 法,网络上的图片可以使用decodeStream方法,资源文件中的图片可以使用decodeResource方法。这些方法会尝试为已经构建的 bitmap分配内存,这时就会很容易导致OOM出现。为此每一种解析方法都提供了一个可选的BitmapFactory.Options参数,将这个参 数的inJustDecodeBounds属性设置为true就可以让解析方法禁止为bitmap分配内存,返回值也不再是一个Bitmap对象,而是 null。虽然Bitmap是null了,但是BitmapFactory.Options的outWidth、outHeight和 outMimeType属性都会被赋值。这个技巧让我们可以在加载图片之前就获取到图片的长宽值和MIME类型,从而根据情况对图片进行压缩。如下代码所 示:

 

  1. BitmapFactory.Options options = new BitmapFactory.Options();  
  2. options.inJustDecodeBounds = true;  
  3. BitmapFactory.decodeResource(getResources(), R.id.myimage, options);  
  4. int imageHeight = options.outHeight;  
  5. int imageWidth = options.outWidth;  
  6. String imageType = options.outMimeType;  

为了避免OOM异常,最好在解析每张图片的时候都先检查一下图片的大小,除非你非常信任图片的来源,保证这些图片都不会超出你程序的可用内存。

现在图片的大小已经知道了,我们就可以决定是把整张图片加载到内存中还是加载一个压缩版的图片到内存中。以下几个因素是我们需要考虑的:

 

  • 预估一下加载整张图片所需占用的内存。
  • 为了加载这一张图片你所愿意提供多少内存。
  • 用于展示这张图片的控件的实际大小。
  • 当前设备的屏幕尺寸和分辨率。

 

比如,你的ImageView只有128*96像素的大小,只是为了显示一张缩略图,这时候把一张1024*768像素的图片完全加载到内存中显然是不值得的。

 

那我们怎样才能对图片进行压缩呢?通过设置BitmapFactory.Options中inSampleSize的值就可以实现。比如我们有一张 2048*1536像素的图片,将inSampleSize的值设置为4,就可以把这张图片压缩成512*384像素。原本加载这张图片需要占用13M的 内存,压缩后就只需要占用0.75M了(假设图片是ARGB_8888类型,即每个像素点占用4个字节)。下面的方法可以根据传入的宽和高,计算出合适的 inSampleSize值:

 

  1. public static int calculateInSampleSize(BitmapFactory.Options options,  
  2.         int reqWidth, int reqHeight) {  
  3.     // 源图片的高度和宽度  
  4.     final int height = options.outHeight;  
  5.     final int width = options.outWidth;  
  6.     int inSampleSize = 1;  
  7.     if (height > reqHeight || width > reqWidth) {  
  8.         // 计算出实际宽高和目标宽高的比率  
  9.         final int heightRatio = Math.round((float) height / (float) reqHeight);  
  10.         final int widthRatio = Math.round((float) width / (float) reqWidth);  
  11.         // 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高  
  12.         // 一定都会大于等于目标的宽和高。  
  13.         inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;  
  14.     }  
  15.     return inSampleSize;  
  16. }  

使 用这个方法,首先你要将BitmapFactory.Options的inJustDecodeBounds属性设置为true,解析一次图片。然后将 BitmapFactory.Options连同期望的宽度和高度一起传递到到calculateInSampleSize方法中,就可以得到合适的 inSampleSize值了。之后再解析一次图片,使用新获取到的inSampleSize值,并把inJustDecodeBounds设置为 false,就可以得到压缩后的图片了。

  1. public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,  
  2.         int reqWidth, int reqHeight) {  
  3.     // 第一次解析将inJustDecodeBounds设置为true,来获取图片大小  
  4.     final BitmapFactory.Options options = new BitmapFactory.Options();  
  5.     options.inJustDecodeBounds = true;  
  6.     BitmapFactory.decodeResource(res, resId, options);  
  7.     // 调用上面定义的方法计算inSampleSize值  
  8.     options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);  
  9.     // 使用获取到的inSampleSize值再次解析图片  
  10.     options.inJustDecodeBounds = false;  
  11.     return BitmapFactory.decodeResource(res, resId, options);  
  12. }  

下面的代码非常简单地将任意一张图片压缩成100*100的缩略图,并在ImageView上展示。

  1. mImageView.setImageBitmap(  
  2.     decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100100));  

 

使用图片缓存技术

在你应用程序的UI界面加载一张图片是一件很简单的事情,但是当你需要在界面上加载一大堆图片的时候,情况就变得复杂起来。在很多情况下,(比如使用 ListView, GridView 或者 ViewPager 这样的组件),屏幕上显示的图片可以通过滑动屏幕等事件不断地增加,最终导致OOM。

为了保证内存的使用始终维持在一个合理的范围,通常会把被移除屏幕的图片进行回收处理。此时垃圾回收器也会认为你不再持有这些图片的引用,从而对这 些图片进行GC操作。用这种思路来解决问题是非常好的,可是为了能让程序快速运行,在界面上迅速地加载图片,你又必须要考虑到某些图片被回收之后,用户又 将它重新滑入屏幕这种情况。这时重新去加载一遍刚刚加载过的图片无疑是性能的瓶颈,你需要想办法去避免这个情况的发生。

 

这个时候,使用内存缓存技术可以很好的解决这个问题,它可以让组件快速地重新加载和处理图片。下面我们就来看一看如何使用内存缓存技术来对图片进行缓存,从而让你的应用程序在加载很多图片的时候可以提高响应速度和流畅性。

 

内存缓存技术对那些大量占用应用程序宝贵内存的图片提供了快速访问的方法。其中最核心的类是LruCache (此类在Android-support-v4的包中提供) 。这个类非常适合用来缓存图片,它的主要算法原理是把最近使用的对象用强引用存储在 LinkedHashMap 中,并且把最近最少使用的对象在缓存值达到预设定值之前从内存中移除。

 

在过去,我们经常会使用一种非常流行的内存缓存技术的实现,即软引用或弱引用 (SoftReference or WeakReference)。但是现在已经不再推荐使用这种方式了,因为从 Android 2.3 (API Level 9)开始,垃圾回收器会更倾向于回收持有软引用或弱引用的对象,这让软引用和弱引用变得不再可靠。另外,Android 3.0 (API Level 11)中,图片的数据会存储在本地的内存当中,因而无法用一种可预见的方式将其释放,这就有潜在的风险造成应用程序的内存溢出并崩溃。

 

为了能够选择一个合适的缓存大小给LruCache, 有以下多个因素应该放入考虑范围内,例如:

 

  • 你的设备可以为每个应用程序分配多大的内存?
  • 设备屏幕上一次最多能显示多少张图片?有多少图片需要进行预加载,因为有可能很快也会显示在屏幕上?
  • 你的设备的屏幕大小和分辨率分别是多少?一个超高分辨率的设备(例如 Galaxy Nexus) 比起一个较低分辨率的设备(例如 Nexus S),在持有相同数量图片的时候,需要更大的缓存空间。
  • 图片的尺寸和大小,还有每张图片会占据多少内存空间。
  • 图片被访问的频率有多高?会不会有一些图片的访问频率比其它图片要高?如果有的话,你也许应该让一些图片常驻在内存当中,或者使用多个LruCache 对象来区分不同组的图片。
  • 你能维持好数量和质量之间的平衡吗?有些时候,存储多个低像素的图片,而在后台去开线程加载高像素的图片会更加的有效。

 

并没有一个指定的缓存大小可以满足所有的应用程序,这是由你决定的。你应该去分析程序内存的使用情况,然后制定出一个合适的解决方案。一个太小的缓 存空间,有可能造成图片频繁地被释放和重新加载,这并没有好处。而一个太大的缓存空间,则有可能还是会引起 java.lang.OutOfMemory 的异常。

 

下面是一个使用 LruCache 来缓存图片的例子:

 

  1. private LruCache<String, Bitmap> mMemoryCache;  
  2.   
  3. @Override  
  4. protected void onCreate(Bundle savedInstanceState) {  
  5.     // 获取到可用内存的最大值,使用内存超出这个值会引起OutOfMemory异常。  
  6.     // LruCache通过构造函数传入缓存值,以KB为单位。  
  7.     int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);  
  8.     // 使用最大可用内存值的1/8作为缓存的大小。  
  9.     int cacheSize = maxMemory / 8;  
  10.     mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {  
  11.         @Override  
  12.         protected int sizeOf(String key, Bitmap bitmap) {  
  13.             // 重写此方法来衡量每张图片的大小,默认返回图片数量。  
  14.             return bitmap.getByteCount() / 1024;  
  15.         }  
  16.     };  
  17. }  
  18.   
  19. public void addBitmapToMemoryCache(String key, Bitmap bitmap) {  
  20.     if (getBitmapFromMemCache(key) == null) {  
  21.         mMemoryCache.put(key, bitmap);  
  22.     }  
  23. }  
  24.   
  25. public Bitmap getBitmapFromMemCache(String key) {  
  26.     return mMemoryCache.get(key);  
  27. }  

在 这个例子当中,使用了系统分配给应用程序的八分之一内存来作为缓存大小。在中高配置的手机当中,这大概会有4兆(32/8)的缓存空间。一个全屏幕的 GridView 使用4张 800×480分辨率的图片来填充,则大概会占用1.5兆的空间(800*480*4)。因此,这个缓存大小可以存储2.5页的图片。
当向 ImageView 中加载一张图片时,首先会在 LruCache 的缓存中进行检查。如果找到了相应的键值,则会立刻更新ImageView ,否则开启一个后台线程来加载这张图片。

  1. public void loadBitmap(int resId, ImageView imageView) {  
  2.     final String imageKey = String.valueOf(resId);  
  3.     final Bitmap bitmap = getBitmapFromMemCache(imageKey);  
  4.     if (bitmap != null) {  
  5.         imageView.setImageBitmap(bitmap);  
  6.     } else {  
  7.         imageView.setImageResource(R.drawable.image_placeholder);  
  8.         BitmapWorkerTask task = new BitmapWorkerTask(imageView);  
  9.         task.execute(resId);  
  10.     }  
  11. }  

BitmapWorkerTask 还要把新加载的图片的键值对放到缓存中。

  1. class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {  
  2.     // 在后台加载图片。  
  3.     @Override  
  4.     protected Bitmap doInBackground(Integer… params) {  
  5.         final Bitmap bitmap = decodeSampledBitmapFromResource(  
  6.                 getResources(), params[0], 100100);  
  7.         addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);  
  8.         return bitmap;  
  9.     }  
  10. }  

掌握了以上两种方法,不管是要在程序中加载超大图片,还是要加载大量图片,都不用担心OOM的问题了!不过仅仅是理论地介绍不知道大家能不能完全理解,在后面的文章中我会演示如何在实际程序中灵活运用上述技巧来避免程序OOM,感兴趣的朋友请继续阅读 Android照片墙应用实现,再多的图片也不怕崩溃 。

解决Eclipse中文乱码

mikel阅读(943)

.

使用Eclipse编辑文件经常出现中文乱码或者文件中有中文不能保存的问题,Eclipse提供了灵活的设置文件编码格式的选项,我们可以通过设置编码 格式解决乱码问题。在Eclipse可以从几个层面设置编码格式:Workspace、Project、Content Type、File
本文以Eclipse 3.3(英文)为例加以说明:
1. 设置Workspace的编码格式:
Windows->Preferences… 打开”首选项”窗口,点击左侧导航树到General->Workspace,在右侧视图中找到“Text file encoding”选项设置,一种是默认(Default),另一种是从下拉列表中选择(Other)。Eclipse Workspace默认的编码方式是操作系统的编码格式,这跟操作系统的设置有关系;另外我们可以选择Other单选按钮,然后从按钮右侧的下拉列表中选 择需要的编码格式(GBK、ISO-8859-1、UTF-16、UFT-16 etc.)。
2. 设置Project的编码格式:
在 Workspace中新建的项目默认继承Workspace的编码设置,我们也可以单独更改某个项目的编码格式。右键点击工程,选择 Properties,打开项目属性设置窗口,左侧导航树选择Resource,在右侧视图中找到“Text file encoding”,两种设置项目的编码格式,默认选中的是“Inherited from container (XXX)”(注:XXX为Workspace设置的编码),我们也可以选择其他的编码格式,设置方式同Workspace。
3. 设置Content Type的编码格式:
有 时我们想使整个Workspace某种类型的文件保持同一种编码格式,这就需要用到Content Type设置来达到目的,具体方式如下:Windows->Preferences…打开”首选项”窗口,左侧导航树选择 General->Content Types,在右侧视图中选择Text->Java Source File,在最下侧有Default encoding输入框,手动输入编码格式,点击Update按钮使设置生效(切记啊!)。有两点需要注意一下:
a. 这个设置使Workspace所有项目下的相同类型的文件有相同的编码格式,改变项目的编码设置不影响项目中文件类型的编码设置;
b. Default encoding是手动输入的,输入的编码名称要准确,否则在打开此类型文件时会显示Unsupported Character Encoding。
4. 设置File的编码格式:
我 们还可以单独设置某个文件的编码格式,一种是通过在文件中设置编码格(如:’charset=UTF-8’),还可以通过文件属性设置。右键点击某一文 件,选择Properties,打开文件属性设置对话框,右侧导航树选择Resource,在右侧视图中通过“Text file encoding”选择设置文件的编码格式。