常常我们需要对模板中字段的值进行非空判断,根据是否为空的结果进行相应的显示,于是从网上搜了下相关的方法,结合网站模板进行了操作总结出如下方法可以实现判断功能,代码如下:
{dede:field.zufang_tel php=yes}
if(@me==""||empty(@me))
@me="<p>无</p>";
{/dede:field.zufang_tel}
注释:
@me:就是当前字段的值,你可以在if中给其赋值,然后判断结束后它的值就变成你赋的值,打印出来
常常我们需要对模板中字段的值进行非空判断,根据是否为空的结果进行相应的显示,于是从网上搜了下相关的方法,结合网站模板进行了操作总结出如下方法可以实现判断功能,代码如下:
{dede:field.zufang_tel php=yes}
if(@me==""||empty(@me))
@me="<p>无</p>";
{/dede:field.zufang_tel}
注释:
@me:就是当前字段的值,你可以在if中给其赋值,然后判断结束后它的值就变成你赋的值,打印出来
很多国内的网友做英文站都选择了织梦,其实织梦确实很强大,不过个人还是建议使用国外比较成熟的CMS或者wp都可以的,不过部分用户已经习惯了织梦,而且采集比较顺手,所以就分享了这篇关于用
织梦也就是dedecms做英文站需要改动的地方。
首先就是编码了:
就是安装utf-8的dede……然后在dede论坛里找个英文模板,
安装好后,看看前台,已经成了英文版的了
开始做模板,可以仿照英文站来做。
模板制作要注意几点:
1.字符集问题:charset=utf-8
2.字体用Verdana, Arial, Helvetica, sans-serif,这样的字体显示英文更好看
3,所有页面上不要出现中文字符,比如全角的空格
4.页面布局,细节等要符合老外的习惯
如:时间格式为“月-日-年”,对应的dede标签为:[field:pubdate function=strftime(‘%m-%d-%Y’,@me)/]
5.SEO细节
(1)文章的url处理
大家应该都清楚,google对于url地址的重视程度很高,同一篇标题为old food new tricks的文章,如果url显示为 http://www.xxx.com/list1/20070928/5104.html 显然没有http://www.xxx.com/list1/old-food-new-tricks.html 的权重高。dede默认的生成的页面地址为前者,怎么改成后者显示类型呢?
进入后台,修改栏目的文章命名规则,默认的为:{typedir}/{Y}{M}{D}/{aid}.html
修改为:{typedir}/{pinyin}.html 我去掉了{Y}{M}{D},这样减少文章的层级,有利于引擎收录。
具体的修改方法,参考如下:
如题,使用dede让文章标题页显示路径为标题拼音.html,例如:文章标题为:站友网是什么啊,文章路径显示:zhanyouwangshishenmea.html,这样显示的好处是什么?靠,这个还用我说?
方法:
选择:网站栏目管理,修改栏目的高级选项,文章命名规则:{typedir}/{Y}{M}{D}/{aid}.html 这是默认的状态
修改为:{typedir}/{pinyin}.html 我觉得{Y}{M}{D}没什么用,所以也去掉了。
重新更新一下,看看是否有效果?
别着急,现在显示出拼音了,zhanyouwangshishenmea_1.html,但是后面还多了个_1,怎么去掉这个呢?
修改include里面的inc_channel_unit_function文件,找到$articleRule = str_replace(“{pinyin}”,GetPinyin($title).”_”.$aid,$articleRule); 把.”_”.$aid去掉,就可以了!
如果文章标题是英文的,会在单词之间加_,但是单词中间加_对gg来说没什么意义,必须改成“-”才可以,修改方法如下
打开 include/inc/inc_fun_funAdmin.php
for($i=0;$i<$slen;$i++){
if(ord($str[$i])>0x80)
{
$c = $str[$i].$str[$i+1];
$i++;
if(isset($pinyins[$c])){
if($ishead==0) $restr .= $pinyins[$c];
else $restr .= $pinyins[$c][0];
}else $restr .= “-“;
}else if( eregi(“[a-z0-9]”,$str[$i]) ){ $restr .= $str[$i]; }
else{ $restr .= “-“; }
}
改
$restr .= “-“; 这些东西
拼音中间加“-”
修改include\inc\inc_fun_funAdmin.php
for($i=0;$i<$slen;$i++){
if(ord($str[$i])>0x80)
{
$c = $str[$i].$str[$i+1];
$i++;
if(isset($pinyins[$c])){
if($ishead==0) $restr .= $pinyins[$c].”-“ ;
if($isclose==0) unset($pinyins);
if(substr($restr,-1)==”-“) $restr = substr($restr,0,strlen($restr)-1);
return $restr;
红色为新加的
(2)meta的处理
我修改article_article.htm的meta为:
<meta name=”description” content=”{dede:field name=”title”/} {dede:field name=’description’ /}”>
<meta name=”keywords” content=”{dede:field name=’keywords’ /} {dede:field name=”title”/}”>
注意标签之间必须有一个空格,不然后面的就不起作用了
这样修改后,每篇文章的meta都是不同的
list_article.htm的meta我想实现为
<meta name=”description” content=”{dede:field name=’description’ /}”>
<meta name=”keywords” content=”{dede:field name=’keywords’ /}”>
[转载]php实现验证码的识别(中级篇) – ugg的专栏 – CSDN博客.
在上篇文章 <php实现验证码的识别 (初级篇 ) > 中,讲了如何识别简单的验证,这里的简单只的是验证码有数字和字母组成,格式统一,每次 出现位置固定。这篇文章将继续深入研究识别验证码,这次识别的目标是,验证码有字符和数字组成,验证码存在旋转(可能左右都旋转),位置不固定,存在字符 与字符之间的粘连,且验证码有更强的干扰素。这篇文章讲解的方法,并不是万能的解决方案,并且提供代码不能直接解决你的问题,这里仅仅是方法,具体需求读 者自己解决,需要说明的是,识别验证码与具体的编程语言无关,这里只是使用 php 语言实现,使用这里介绍的方法,你可以使用任何语言实现。
这篇文章逐步讲解识别验证码过程中的各个步骤。
如上图,随后的讲解我们都围绕此图展开。
一:拿到一个验证码的,第一眼我们首先要做的工作是,二值化。把验证码的部分用 1 表示,背景部分用 0 表示出来,识别方法很简单,我们打印出验证码正张图片的 RGB ,然后分析其规律即可,通过 RGB 码,我们很容易分辨出上面这张图片的 R 值大于 120 , G 和 B 的值小于 80 ,所以依据这个规则我们很容易把上面的图片二值化。再看初级篇中识别的两张图
刚看上去,感觉很复杂。验证码的图片每次背景色都不相同,且不是单色,各个验证码数字的颜色每次也各不相同。貌似很难二值化,其实我们打印出其 RGB 值很容易就发现。无论验证数字颜色如何变化,该数字的 RGB 值总有一个值小于 125 ,所以通过如下判断
$rgbarray[‘red’] < 125 || $rgbarray[‘green’]<125|| $rgbarray[‘blue’] < 125
我们就很容易分辨出哪里是数字,哪里是背景。
我们能够找到这些规律的因素是,在制作验证码的干扰素时,为了使干扰素不影响数字的显示效果,必须使用干扰素的 RGB 和数字 RGB 相互独立,互不干扰。只要懂得这个规律,我们就很容易实现二值化。
我们找到的 120 , 80 , 125 等阈值,可能和实际的 RGB 有出入,所以,有时二值化后,会有部分地方出现 1 ,对于验证码上固定位置显示数字,这种干扰没有太大意义。但是对于验证码位置不确定的图片来说,在我们切割字符时,很可能造成干扰。所以,在二值化后要进行去噪出来。
二:接下来我们进行第二个步骤,出噪。出燥的原理很简单,就是把孤立的有效的值去掉,如果噪点比较高,要求的效率也比较高的话,这里面也有很多工作要做。幸好这里我们不要求这么高深,我们使用最简单的方法就可以,如果一个点为 1 则判断这个点的上下左右上左上右下左下右 8 个方位上数字是否为 1 ,如果不为 1 ,就认为是一个燥点,直接设置为 1 即可。
如上图所示,我们使用此方法很容易发现红色方框部分的 1 为燥点,直接设置为 1 即可。
在判断时我们使用了一个技巧,有时候的噪点可能是两个连续的 1 ,所以我们
$num = 0;
if($data[$i][$j] == 1)
{
// 上
if(isset($data[$i-1][$j])){
$num = $num + $data[$i-1][$j];
}
// 下
if(isset($data[$i+1][$j])){
$num = $num + $data[$i+1][$j];
}
// 左
if(isset($data[$i][$j-1])){
$num = $num + $data[$i][$j-1];
}
// 右
if(isset($data[$i][$j+1])){
$num = $num + $data[$i][$j+1];
}
// 上左
if(isset($data[$i-1][$j-1])){
$num = $num + $data[$i-1][$j-1];
}
// 上右
if(isset($data[$i-1][$j+1])){
$num = $num + $data[$i-1][$j+1];
}
// 下左
if(isset($data[$i+1][$j-1])){
$num = $num + $data[$i+1][$j-1];
}
// 下右
if(isset($data[$i+1][$j+1])){
$num = $num + $data[$i+1][$j+1];
}
}
if($num == 0){
$data[$i][$j] = 0;
}
我们计算这个点的 8 个方向上的值之和,最后我们判断他们的和是否小于特定的阈值
三:经过去噪后,我们就得到干净的二值化的数据,接下来要做的就是切割字符了。切割字符的方法有很多种,这里我采用最简单的一种,先垂直方向切割成为字符,然后在水平方向去掉多于的 0000 ,如下图
第一步切割红线部分,第二步切割蓝线部分,这样就可以得到独立的字符了。但是像下面这种情况
按上面的方法会把 dw 字符切割成一个字符,这是错误的切割,所以这里我们涉及到粘连字符的切割。
四:粘连字符切割,制作验证码时,规则字符的粘连很容易分割开,如果字符本身有缩放,变形就很难处理,经过分析,我们可以发现,上面的字符粘连属于很简 单的方式,只是规则字符的粘连,所以处理这种情况,我们也使用很简单的处理方式。当完成分割操作后,我们不能马上确定分割的部分就为一个字符,要进行验 证,验证的关键因素就是,切割下来的字符的宽是否大于阈值,这个阈值的取舍标准是,一个字符无论怎么旋转变形都不会大于这个阈值,所以,如果我们切割的块 大于这个阈值,就可以认为这是一个粘连字符;如果大于两个阈值之和,就认为是三个字符粘连,以此类推。知道这个规则后,切割粘连字符也就很简单了。如果我 们发现是粘连字符块,直接平分这个块为两个或者多个新的块就可以。当然为了更好的还原字符,我一般都采用平分 +1 , -1 对字符块的部分进行适当的补充。
五:经过上面四个步骤,我们就可以提取出比较纯的字符块了,接下来要做就是匹配字符了。对于旋转字符的特征码建立,有很多种方法,这里就不做深入研究了。我这里使用的最简单的方式,为所有字符的所有情况建立匹配库,所以在我提供的代码种增加了 study 操作,其目的就是,先有人手工识别图片的验证码,然后通过 study 方法,写入特征码库。这样写入的图片数据越多,验证识别的准确行也就越高。
好了,经过以上步骤,我们基本上可以识别现在互联网上大部分的验证码,这里我们都是使用的最简单的方法,没有使用任何 OCR 知识。这些方法,应该属于非 OCR 领域的顶峰了,要想识别更加复杂的验证码,那就需要更多的 OCR 知识了。有机会的话,我会在高级篇中一一做介绍。
下面是一些容易识别的验证码,希望引起网站管理者的重视。
制作验证码的一些建议
对于识别验证码的程序来说,最难得部分是验证字符的切割和特征码的建立,而国内很多程序员只做验证码时,总是喜欢在验证码加很多干扰素,干扰线,影响效果不说,还达不到很好的效果;所以,要想使自己验证码难于本识别,只做下面两点就够了
1 :字符粘连,最好所有的字符都有粘连的部分;
2 :不要使用规格字符,验证码的各个部分使用不同比例的缩放或者旋转。
只要做到这两点,或者这两点的变形,识别程序就很难识别。我们看看, yahoo 和 google 的验证码就知道,白字黑底,却很难被识别。
Goole:

yahoo:

源文件下:点击下载
作者 : 逸学堂( ugg )
转帖请注明:http://blog.csdn.net/ugg/archive/2009/03/09/3972368.aspx
[转载]php实现验证码的识别(初级篇) – ugg的专栏 – CSDN博客.
近期研究一些突破验证码方面的知识,记录下来。一方面算是对这几天学习知识的总结帮助自己理解;另一方面希望对研究这方面的技术同学有所帮助;另外 也希望引起网站管理者的注意,在提供验证码时多些考虑进去。由于刚刚接触这方面的知识,理解比较浅显,有错误再所难免,欢迎拍砖。
验证码的作用: 有效防止某个黑客对某一个特定注册用户用特定程序暴力破解方式进行不断的登陆尝试。其实现代的验证码一般是防止机器批量注册的,防止机器批量发帖回复。目前,不少网站为了防止用户利用机器人自动注册、登录、灌水,都采用了验证码技术。
所谓验证码,就是将一串随机产生的数字或符号,生成一幅图片,图片里加上一些干扰象素(防止OCR),由用户肉眼识别其中的验证码信息,输入表单提交网站验证,验证成功后才能使用某项功能。
我们最常见的验证码
1,四位数字,随机的一数字字符串,最原始的验证码,验证作用几乎为零。
2,随机数字图片验证码。图片上的字符比较中规中矩,有的可能加入一些随机干扰素,还有一些是随机字符颜色,验证作用比上一个好。没有基本图形图像学知识的人,不可破!
3,各种图片格式的随机数字+随机大写英文字母+随机干扰像素+随机位置。
4,汉字是注册目前最新的验证码,随机生成,打起来更难了,影响用户体验,所以,一般应用的比较少。
简单起见,我们这次说明的主要对象是第2种类型的,我们先看几种网上比较常见的这种验证码的图片.
(不知道怎么搞的,CSDN又不能上传图片了,我把这四种图片放到下载包中了,可以下载下来对比察看)
这四种样式,基本上能代表2中所提到的验证码类型,初步看起来第一个图片最容易破解,第二个次之,第三个更难,第四个最难。
真实情况那?其实这三种图片破解难度相同。
第一个图片,最容易,图片背景和数字都使用相同的颜色,字符规整,字符位置统一。
第二个图片,看似不容易,其实仔细研究会发现其规则,背景色和干扰素无论怎么变化,验证字符字符规整,颜色相同,所以排除干扰素非常容易,只要是非字符色素全部排除即可。
第三个图片,看似更复杂,处理上面提到背景色和干扰素一直变化外,验证字符的颜色也在变化,并且各个字符的颜色也各不相同。看似无法突破这个验证码,本篇文章,就一这种类型验证码为例说明,第四个图片,同学们自己搞。
第四个图片,除了第三个图片上提到的特征外,又在文字上加了两条直线干扰率,看似困难其实,很容易去掉。
验证码识别一般分为以下几个步骤:
1. 取出字模
2. 二值化
3. 计算特征
4. 对照样本
1:取出字模
识别验证码,毕竟不是专业的OCR识别,并且,由于各个网站的验证码各不相同,所以,最常见的方法就是就是建立这个验证码的特征码库。去字模时,我们需要多下载几张图片,使这些图片中,包括所有的字符,我们这里的字母只有图片,所以,只要收集到包括0-9的图片即可。
2:二值化
二值化就是把图片上的验证数字上每个象素用一种数字表示1,其他部分用0表示。这样就可以计算出每个数字字模,记录下这些字模来,当作key即可。
3:计算特征
把要识别的图片,进行二值化,得到图片特征。
4:对照样本
把步骤3种的图片特征码和验证码的字模进行对比,得到验证图片上的数字。
使用目前这种方法,对验证码的识别基本上可以做到100%。
通 过以上步骤,您可能说了,并没有发现如何取出干扰素啊!其实取出干扰素的方法很简单,干扰素的一个重要特征是,不能影响验证码的显示效果,所以制作干扰素 时它的RGB可能低于或者高于某个特定值,比如我给的例子中的图片,干扰素的RGB各项值是不会超过125的,所以,这样我们就很容易去掉干扰素了。
php代码
< ?php
define('WORD_WIDTH',9);
define('WORD_HIGHT',13);
define('OFFSET_X',7);
define('OFFSET_Y',3);
define('WORD_SPACING',4);
class valite
{
public function setImage($Image)
{
$this->ImagePath = $Image;
}
public function getData()
{
return $data;
}
public function getResult()
{
return $DataArray;
}
public function getHec()
{
$res = imagecreatefromjpeg($this->ImagePath);
$size = getimagesize($this->ImagePath);
$data = array();
for($i=0; $i < $size[1]; ++$i)
{
for($j=0; $j < $size[0]; ++$j)
{
$rgb = imagecolorat($res,$j,$i);
$rgbarray = imagecolorsforindex($res, $rgb);
if($rgbarray['red'] < 125 || $rgbarray['green']<125
|| $rgbarray['blue'] < 125)
{
$data[$i][$j]=1;
}else{
$data[$i][$j]=0;
}
}
}
$this->DataArray = $data;
$this->ImageSize = $size;
}
public function run()
{
$result="";
// 查找4个数字
$data = array("","","","");
for($i=0;$i<4;++$i)
{
$x = ($i*(WORD_WIDTH+WORD_SPACING))+OFFSET_X;
$y = OFFSET_Y;
for($h = $y; $h < (OFFSET_Y+WORD_HIGHT); ++ $h)
{
for($w = $x; $w < ($x+WORD_WIDTH); ++$w)
{
$data[$i].=$this->DataArray[$h][$w];
}
}
}
// 进行关键字匹配
foreach($data as $numKey => $numString)
{
$max=0.0;
$num = 0;
foreach($this->Keys as $key => $value)
{
$percent=0.0;
similar_text($value, $numString,$percent);
if(intval($percent) > $max)
{
$max = $percent;
$num = $key;
if(intval($percent) > 95)
break;
}
}
$result.=$num;
}
$this->data = $result;
// 查找最佳匹配数字
return $result;
}
public function Draw()
{
for($i=0; $i< $this->ImageSize[1]; ++$i)
{
for($j=0; $j< $this->ImageSize[0]; ++$j)
{
echo $this->DataArray[$i][$j];
}
echo "\n";
}
}
public function __construct()
{
$this->Keys = array(
'0'=>'000111000011111110011000110110000011110000011110000011110000011110000011110000011110000011011000110011111110000111000',
'1'=>'000111000011111000011111000000011000000011000000011000000011000000011000000011000000011000000011000011111111011111111',
'2'=>'011111000111111100100000110000000111000000110000001100000011000000110000001100000011000000110000000011111110111111110',
'3'=>'011111000111111110100000110000000110000001100011111000011111100000001110000000111000000110100001110111111100011111000',
'4'=>'000001100000011100000011100000111100001101100001101100011001100011001100111111111111111111000001100000001100000001100',
'5'=>
'111111110111111110110000000110000000110000000111110000111111100000001110000000111000000110100001110111111100011111000',
'6'=>'000111100001111110011000010011000000110000000110111100111111110111000111110000011110000011011000111011111110000111100',
'7'=>'011111111011111111000000011000000010000000110000001100000001000000011000000010000000110000000110000001100000001100000',
'8'=>'001111100011111110011000110011000110011101110001111100001111100011101110110000011110000011111000111011111110001111100',
'9'=>'001111000011111110111000111110000011110000011111000111011111111001111011000000011000000110010000110011111100001111000',
);
}
protected $ImagePath;
protected $DataArray;
protected $ImageSize;
protected $data;
protected $Keys;
protected $NumStringArray;
}
?>
我做了一个例子,你可以从这里下载 下载
破解完成上面的验证码,我们就可以使用snoopy(比curl要轻量,所以我喜欢)来模拟浏览器器,访问网站了。
[转载]Predator:比微软Kinect更强的视频追踪算法-来自捷克博士论文 – CSDN.NET – CSDN资讯.
boycott (haha):刚刚看到了这个用来演示一种新的物体跟踪的算法的视频,它是Zdenek Kalal博士论文里的一部分。Zdenek Kalal是英国萨里大学的一个捷克学生。他演示的是他的神奇的精确定位系统,这个系统几乎可以跟踪镜头里的任何物体,只要你能看见它,并把它选中。它能 做很多神情的事情。在这个视频中,他演示了通过摄像机拍摄他的手指、把他的手指选做目标。系统于是就能精确的跟踪他的手指的动作。更令人惊奇的是,这个系 统能够通过分析物体的运动来完善跟踪算法。你能在很短的时间里教会它跟踪你的手指、面孔或在高速公路上狂颠的轿车。有了这套系统,我们几乎真的可以实 现”Minority Report“那样的人机界面。就像微软Xbox的Kinect那样,而这个效果更好。
Kalal有12个视频来演示他的这套算法都能做什么。只要你有一个好的摄像头,把这个软件装到计算机上、平板电脑上或手机里,它就能精确的定位跟 踪你的前额上的一个点、你的指尖、或你的眼睛。你把摄像头放到门外,它就能自动识别是你认识的人来了,或警告你这是个陌生人。人们不用通过手就能简单的操 控计算机。这项技术应用前景广泛。

你可以从萨里大学的网站找到这个程序的代码,它是免费的。Kalal被授予了“Technology Everywhere”奖学金作为嘉奖。http://info.ee.surrey.ac.uk/Personal/Z.Kalal/tld.html
非常精彩的视频演示:请点击下图
- ● Input: video stream from single monocular camera, bounding box defining the object
- ● Output: object location in the stream, object model
- ● Implementation: Matlab + C, single thread, no GPU
- ● No offline training stage
- ● Real-time performance on QVGA video stream
- ● Dependence on OpenCV library (single function)
- ● Ported to Windows, Mac OS X and Linux
- ● Illumination invariant features
TLD can be downloaded for testing in a chosen application. We provide a precompiled demo (Windows) and a source code that is released under GPL version 3.0. In short, it means that any distributed project that includes or links any portion of TLD source code has to be released with the source code under the GPL version 3.0 license or later.
A license has to be purchased for using TLD in a commercial project. The licencing is managed by the IP owner, the University of Surrey and the fee is subject to negotiation. Please contact the University of Surrey for further information.
- ● High-level description of TLD
- ● Components of TLD
- ● Learning component of TLD
- ● Application of TLD tracker to faces
- ● Detailed description is in the following papers: ICCV’09 (w), CVPR’10, ICIP’10, ICPR’10
- ● Many technical questions (e.g. installation) are being discussed in the following discussion group.
MITBBS网友评论
lalaphin (orpheus):MIT一直在研究这玩意儿。我早就说过了,微软做kinect最终目标并不是游戏业,而是作为将来windows应用的探路石。
kz80 (雨过天晴):不错, 这学生很能下工夫. kinect也是差不多原理.光线暗了肯定就不能用了。
zlike (最终幻想):我觉得他这个没解决最关键的问题:what to track?object tracking/recognition的一个难点是如何对凭空冒出来的一副图象中判断“哪些”发生了变化(因为所有像素都有变化),而不是对预先选好 的一个小区域进行跟踪。如果他这个没有那个选取的步骤,而是直接能识别移动的“有效”物体,那就可以和kinnect媲美了。
[转载]如何写HtmlHelper的扩展方法。 – 斯年余芳 – 博客园.
写了一个ButtonHelper,我将这个方法封在Gqq.Common.dll中。在其他的网站引用中,如何能够实现智能感知呢?
分析了半天才知道,要在web.config(如果用Razor,有两个)中配置pages->namespace节点才可以。
<pages pageBaseType=”System.Web.Mvc.WebViewPage”>
<namespaces>
<add namespace=”System.Web.Mvc” />
<add namespace=”System.Web.Mvc.Ajax” />
<add namespace=”System.Web.Mvc.Html” />
<add namespace=”System.Web.Routing” />
<add namespace=”Gqq.Common.Helper” />
</namespaces>
</pages>
<pages>
<namespaces>
<add namespace=”System.Web.Helpers” />
<add namespace=”System.Web.Mvc” />
<add namespace=”System.Web.Mvc.Ajax” />
<add namespace=”System.Web.Mvc.Html” />
<add namespace=”System.Web.Routing” />
<add namespace=”System.Web.WebPages” />
<!–如果缺少了这个配置,那么在aspx页面中需要使用 @ Import来进行导入,如果多个页面需要使用这个,
(同样 @ Register 可以代替controls)
则最好在web.config中进行配置–>
<add namespace=”GqqMvcWeb.Message” />
<add namespace=”Gqq.Common.Helper” />
</namespaces>
</pages>
这样,在Razor界面中,就可以使用@Html.Button()来构造一个Button了。
[转载]深入剖析Android消息机制 – coolszy – 博客园.
在Android中,线程内部或者线程之间进行信息交互时经常会使用消息,这些基础的东西如果我们熟悉其内部的原理,将会使我们容易、更好地架构系统,避免一些低级的错误。在学习Android中消息机制之前,我们先了解与消息有关的几个类:
1.Message
消息对象,顾名思义就是记录消息信息的类。这个类有几个比较重要的字段:
a.arg1和arg2:我们可以使用两个字段用来存放我们需要传递的整型值,在Service中,我们可以用来存放Service的ID。
b.obj:该字段是Object类型,我们可以让该字段传递某个多项到消息的接受者中。
c.what:这个字段可以说是消息的标志,在消息处理中,我们可以根据这个字段的不同的值进行不同的处理,类似于我们在处理Button事件时,通过switch(v.getId())判断是点击了哪个按钮。
在使用Message时,我们可以通过new Message()创建一个Message实例,但是Android更推荐我们通过Message.obtain()或者 Handler.obtainMessage()获取Message对象。这并不一定是直接创建一个新的实例,而是先从消息池中看有没有可用的 Message实例,存在则直接取出并返回这个实例。反之如果消息池中没有可用的Message实例,则根据给定的参数new一个新Message对象。 通过分析源码可得知,Android系统默认情况下在消息池中实例化10个Message对象。
2.MessageQueue
消息队列,用来存放Message对象的数据结构,按照“先进先出”的原则存放消息。存放并非实际意义的保存,而是将Message对象以链表的方式串联起来的。MessageQueue对象不需要我们自己创建,而是有Looper对象对其进行管理,一个线程最多只可以拥有一个MessageQueue。我们可以通过Looper.myQueue()获取当前线程中的MessageQueue。
3.Looper
MessageQueue的管理者,在一个线程中,如果存在Looper对象,则必定存在MessageQueue对象,并且只存在一个Looper对象和一个MessageQueue对象。在Android系统中,除了主线程有默认的Looper对象,其它线程默认是没有Looper对象。如果想让我们新创建的线程拥有Looper对象时,我们首先应调用Looper.prepare()方法,然后再调用Looper.loop()方法。典型的用法如下:
class LooperThread extends Thread
{
public Handler mHandler;
public void run()
{
Looper.prepare();
//其它需要处理的操作
Looper.loop();
}
}
倘若我们的线程中存在Looper对象,则我们可以通过Looper.myLooper()获取,此外我们还可以通过Looper.getMainLooper()获取当前应用系统中主线程的Looper对象。在这个地方有一点需要注意,假如Looper对象位于应用程序主线程中,则Looper.myLooper()和Looper.getMainLooper()获取的是同一个对象。
4.Handler
消息的处理者。通过Handler对象我们可以封装Message对象,然后通过sendMessage(msg)把Message对象添加到MessageQueue中;当MessageQueue循环到该Message时,就会调用该Message对象对应的handler对象的handleMessage()方法对其进行处理。由于是在handleMessage()方法中处理消息,因此我们应该编写一个类继承自Handler,然后在handleMessage()处理我们需要的操作。
下面我们通过跟踪代码分析在Android中是如何处理消息。首先贴上测试代码:
/**
*
* @author coolszy
* @blog http://blog.csdn.net/coolszy
*
*/
public class MessageService extends Service
{
private static final String TAG = "MessageService";
private static final int KUKA = 0;
private Looper looper;
private ServiceHandler handler;
/**
* 由于处理消息是在Handler的handleMessage()方法中,因此我们需要自己编写类
* 继承自Handler类,然后在handleMessage()中编写我们所需要的功能代码
* @author coolszy
*
*/
private final class ServiceHandler extends Handler
{
public ServiceHandler(Looper looper)
{
super(looper);
}
@Override
public void handleMessage(Message msg)
{
// 根据what字段判断是哪个消息
switch (msg.what)
{
case KUKA:
//获取msg的obj字段。我们可在此编写我们所需要的功能代码
Log.i(TAG, "The obj field of msg:" + msg.obj);
break;
// other cases
default:
break;
}
// 如果我们Service已完成任务,则停止Service
stopSelf(msg.arg1);
}
}
@Override
public void onCreate()
{
Log.i(TAG, "MessageService-->onCreate()");
// 默认情况下Service是运行在主线程中,而服务一般又十分耗费时间,如果
// 放在主线程中,将会影响程序与用户的交互,因此把Service
// 放在一个单独的线程中执行
HandlerThread thread = new HandlerThread("MessageDemoThread", Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// 获取当前线程中的looper对象
looper = thread.getLooper();
//创建Handler对象,把looper传递过来使得handler、
//looper和messageQueue三者建立联系
handler = new ServiceHandler(looper);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
Log.i(TAG, "MessageService-->onStartCommand()");
//从消息池中获取一个Message实例
Message msg = handler.obtainMessage();
// arg1保存线程的ID,在handleMessage()方法中
// 我们可以通过stopSelf(startId)方法,停止服务
msg.arg1 = startId;
// msg的标志
msg.what = KUKA;
// 在这里我创建一个date对象,赋值给obj字段
// 在实际中我们可以通过obj传递我们需要处理的对象
Date date = new Date();
msg.obj = date;
// 把msg添加到MessageQueue中
handler.sendMessage(msg);
return START_STICKY;
}
@Override
public void onDestroy()
{
Log.i(TAG, "MessageService-->onDestroy()");
}
@Override
public IBinder onBind(Intent intent)
{
return null;
}
}
运行结果:

注:在测试代码中我们使用了HandlerThread类,该类是Thread的子类,该类运行时将会创建looper对象,使用该类省去了我们自己编写Thread子类并且创建Looper的麻烦。
下面我们分析下程序的运行过程:
1.onCreate()
首先启动服务时将会调用onCreate()方法,在该方法中我们new了一个HandlerThread对象,提供了线程的名字和优先级。
紧接着我们调用了start()方法,执行该方法将会调用HandlerThread对象的run()方法:
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
在run()方法中,系统给线程添加的Looper,同时调用了Looper的loop()方法:
public static final void loop() {
Looper me = myLooper();
MessageQueue queue = me.mQueue;
while (true) {
Message msg = queue.next(); // might block
//if (!me.mRun) {
// break;
//}
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
if (me.mLogging!= null) me.mLogging.println(
">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what
);
msg.target.dispatchMessage(msg);
if (me.mLogging!= null) me.mLogging.println(
"<<<<< Finished to " + msg.target + " "
+ msg.callback);
msg.recycle();
}
}
}
[/java]
通过源码我们可以看到loop()方法是个死循环,将会不停的从MessageQueue对象中获取Message对象,如果MessageQueue 对象中不存在Message对象,则结束本次循环,然后继续循环;如果存在Message对象,则执行 msg.target.dispatchMessage(msg),但是这个msg的.target字段的值是什么呢?我们先暂时停止跟踪源码,返回到onCreate()方法中。线程执行完start()方法后,我们可以获取线程的Looper对象,然后new一个ServiceHandler对象,我们把Looper对象传到ServiceHandler构造函数中将使handler、looper和messageQueue三者建立联系。
2.onStartCommand()
执行完onStart()方法后,将执行onStartCommand()方法。首先我们从消息池中获取一个Message实例,然后给Message对象的arg1、what、obj三个字段赋值。紧接着调用sendMessage(msg)方法,我们跟踪源代码,该方法将会调用sendMessageDelayed(msg, 0)方法,而sendMessageDelayed()方法又会调用sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)方法,在该方法中我们要注意该句代码msg.target = this,msg的target指向了this,而this就是ServiceHandler对象,因此msg的target字段指向了ServiceHandler对象,同时该方法又调用MessageQueue 的enqueueMessage(msg, uptimeMillis)方法:
[java]
final boolean enqueueMessage(Message msg, long when) {
if (msg.when != 0) {
throw new AndroidRuntimeException(msg
+ " This message is already in use.");
}
if (msg.target == null && !mQuitAllowed) {
throw new RuntimeException("Main thread not allowed to quit");
}
synchronized (this) {
if (mQuiting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
} else if (msg.target == null) {
mQuiting = true;
}
msg.when = when;
//Log.d("MessageQueue", "Enqueing: " + msg);
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
this.notify();
} else {
Message prev = null;
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
msg.next = prev.next;
prev.next = msg;
this.notify();
}
}
return true;
}
[/java]
该方法主要的任务就是把Message对象的添加到MessageQueue中(数据结构最基础的东西,自己画图理解下)。
handler.sendMessage()-->handler.sendMessageDelayed()-->handler.sendMessageAtTime()-->msg.target = this;queue.enqueueMessage==>把msg添加到消息队列中
3.handleMessage(msg)
onStartCommand()执行完毕后我们的Service中的方法就执行完毕了,那么handleMessage()是怎么调用的呢?在前面分析的loop()方法中,我们当时不知道msg的target字段代码什么,通过上面分析现在我们知道它代表ServiceHandler对象,msg.target.dispatchMessage(msg);则表示执行ServiceHandler对象中的dispatchMessage()方法:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
该方法首先判断callback是否为空,我们跟踪的过程中未见给其赋值,因此callback字段为空,所以最终将会执行handleMessage()方法,也就是我们ServiceHandler类中复写的方法。在该方法将根据what字段的值判断执行哪段代码。
至此,我们看到,一个Message经由Handler的发送,MessageQueue的入队,Looper的抽取,又再一次地回到Handler的怀抱中。而绕的这一圈,也正好帮助我们将同步操作变成了异步操作。
代码下载地址:http://u.115.com/file/f1e0a5d5db
[转载]服务器缓存不依赖URL的方法(OutPutCache)及客户端不缓存,完美做法 – 达奇-方向比努力更重要 – 博客园.
可以避免客户端缓存:
<%Response.Cache.SetNoStore();%>
———————————————————————————————————————-
经过测试,下面的方式会清空服务器端的缓存。不适合配合Global.asax使用
<% Response.CacheControl = “no-cache” %>
<% Response.AddHeader “Pragma”, “no-cache” %>
<% Response.Expires = -1 %>s
———————————————————————————————————————-
服务器缓存方式:
Global.asax:
新增函数:
public override string GetVaryByCustomString(HttpContext context, string custom)
{
if (“Student”.Equals(custom))
{
return string.Format(“{0}”, “动态数据”);
}
return base.GetVaryByCustomString(context, custom);
}
页面:
<%@ OutputCache Duration=”180″ VaryByCustom=”Student” VaryByParam=”none” %>
[转载]用sql写个作业每隔五分钟去读某个文件夹下所有xml文件,并导入到DB中 – 编程之美 – 博客园.
消息 15281,级别 16,状态 1,过程 xp_cmdshell,第 1 行
SQL Server 阻止了对组件 ‘xp_cmdshell’ 的 过程 ‘sys.xp_cmdshell’ 的访问,因为此组件已作为此服务器安全配置的一部分而被关闭。系统管理员可以通过使用 sp_configure 启用 ‘xp_cmdshell’。有关启用 ‘xp_cmdshell’ 的详细信息,请参阅 SQL Server 联机丛书中的 “外围应用配置器”。
exec sp_configure 'show advanced options', 1; reconfigure; exec sp_configure 'xp_cmdshell', 1; reconfigure;
用SQL写个作业每隔五分钟去读某个文件夹下所有xml文件,并把它的数据插入到一张表在把它删除
--create table TEST (
--A char(10),B char(10),C char(10)
--)
DECLARE
@XMLDIR varchar(2000), --XML放置的文件全路径
@DIR varchar(1000), --XML的文件夹
@FileFullName varchar(8000) --游标用,文件名称(全路径)
declare
@FileTable TABLE(x varchar(8000)) --临时表,存储 文件夹所有XML文件名称
SET @DIR='D:\TEST'
SET @XMLDIR=N'DIR '+ @DIR + '\*.xml' --*.xml 只读取是XML的扩展名文件
INSERT @FileTable
exec xp_cmdshell @XMLDIR --将文件夹的内容读取插入临时表中
delete from @FileTable where x not like '%.xml%' or x is null
update @FileTable set x=@DIR+'\'+SUBSTRING(x,37,120)
--select * from @FileTable
declare fCursor cursor for
select x from @FileTable
open fCursor
fetch next from fCursor into @FileFullName
while(@@fetch_status=0)
BEGIN
declare
@xml varchar(8000), --XML转换成列的内容
@Pointer INT, --指向位置的变量
@DELFILENAME varchar(2000) --删除文件的CMDSHELL
declare @table TABLE(x varchar(8000))
SET @DELFILENAME= 'DEL '+@FileFullName
insert into @table EXEC ('(SELECT * FROM OPENROWSET(BULK '''+@FileFullName+''',SINGLE_CLOB) as x)')
select @xml=x from @table
EXECUTE sp_xml_preparedocument @Pointer OUTPUT,@xml
INSERT INTO TEST(A,B,C)
SELECT A,B,C
FROM OPENXML (@Pointer,'ROOT/Row')WITH(A varchar(10),B varchar(10),C varchar(10))
EXEC sp_xml_removedocument @Pointer
EXEC xp_cmdshell @DELFILENAME
fetch next from fCursor into @FileFullName
END
close fCursor --关闭游标
deallocate fCursor --删除游标
剩下的就是加个代理了..哈…5分钟执行一次
XML文件如下
<ROOT> <Row A='1' B='1' C='1'></Row> <Row A='2' B='1' C='1'></Row> <Row A='3' B='1' C='1'></Row> <Row A='4' B='1' C='1'></Row> </ROOT>
[转载]ASP.NET中的图片缓存 – 心如止水 – 博客园.
原文地址:Caching Images in ASP.NET , 版权归原文作者所有。
引言:
在一个Web应用程序中,可以通过很多种方法来改善其性能,其中最简单但也是最有效的方法就是在客户端缓存图片,这篇文章就是告诉你如何在你的站点中实现图片的缓存。
问题:
我曾经建立过一个站点,在CSS样式表中使用了很多图片来作为菜单项的背景。网站完成之后,我使用Microsoft Network Monitor(微软的一款流量分析工具,可从微软下载中心下载)对网站的流量进行了统计,发现每次对首页的访问都会对20个不同的文件发出请求,其中一半以上都来至于对菜单背景图片的请求。
有两种方法可以解决这个问题,第一种方法是通过IIS实现图片的缓存;第二种方法是直接在ASP.NET实现缓存。
通过IIS缓存图片:
这种方法非常简单,首先选中IIS管理器中选中一个文件或文件夹,右键单击,打开属性对话框。
选中HTTP头选项卡中的“启用内容过期”,并根据需要设定过期时间。这样客户端就会对你设定的文件进行缓存,直到缓存过期才会向服务端发起新的请求。
当你对IIS拥有足够的管理权限,并且网站的图片位置相对比较集中时,这种方法是一种很好的选择。但这样的条件往往得不到满足,这个时候你就需要使用第二种方法了。
通过HttpHandle缓存图片
为了获取对ASP.NET的请求,需要编写一个自定义HttpHandle来对图片文件(*.jpg;*.gif;*.png)进行监听。首先在Visuan Studio中新建一个类库工程,取名为CachingHandler,负责处理对图片的请求。CachingHandler需要实现IHttpHandle接口,在IHttpHandle接口中,IsReusable属性指示其他请求是否可以使用该IHttpHandler实例,ProcessRequest()方法负责获取和发送数据。
namespace SoftwareArchitects.Web
{
public class CachingHandler : IHttpHandler
{
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
string file = context.Server.MapPath
(context.Request.FilePath.Replace(“.ashx”, “”));
string filename = file.Substring(file.LastIndexOf(‘\\’) + 1);
string extension = file.Substring(file.LastIndexOf(‘.’) + 1);
CachingSection config = (CachingSection)context.GetSection
(“SoftwareArchitects/Caching”);
if (config != null)
{
context.Response.Cache.SetExpires
(DateTime.Now.Add(config.CachingTimeSpan));
context.Response.Cache.SetCacheability
context.Response.Cache.SetValidUntilExpires(false);
if (fileExtension != null)
{
context.Response.ContentType = fileExtension.ContentType;
}
}
context.Response.AddHeader(“content-disposition”,
“inline; filename=” + filename);
context.Response.WriteFile(file);
}
}
}
配置Web.Config文件
在上面的代码中,我们使用了一个自定义类ConfigurationSection来读写Web.Config的信息,下面是这个类的实现。
namespace SoftwareArchitects.Web.Configuration
{
/// <summary>
/// Configuration for caching
/// </summary>
public class CachingSection : ConfigurationSection
{
[ConfigurationProperty(“CachingTimeSpan”, IsRequired = true)]
public TimeSpan CachingTimeSpan
{
get { return (TimeSpan)base[“CachingTimeSpan”]; }
set { base[“CachingTimeSpan”] = value; }
}
IsRequired = true)]
public FileExtensionCollection FileExtensions
{
get { return ((FileExtensionCollection)base[“FileExtensions”]); }
}
}
/// List of available file extensions
/// </summary>
public class FileExtensionCollection : ConfigurationElementCollection
{
…
}
/// Configuration for a file extension
/// </summary>
public class FileExtension : ConfigurationElement
{
[ConfigurationProperty(“Extension”, IsRequired = true)]
public string Extension
{
get { return (string)base[“Extension”]; }
set { base[“Extension”] = value.Replace(“.”, “”); }
}
[ConfigurationProperty(“ContentType”, IsRequired = true)]
public string ContentType
{
get { return (string)base[“ContentType”]; }
set { base[“ContentType”] = value; }
}
}
}完整的ConfigurationSection类:CachingSection.cs
最后是在Web.Config文件中添加相关的信息:
<SoftwareArchitects>
<Caching CachingTimeSpan=”1″>
<FileExtensions>
<add Extension=”gif” ContentType=”image\gif” />
<add Extension=”jpg” ContentType=”image\jpeg” />
<add Extension=”png” ContentType=”image\png” />
</FileExtensions>
</Caching>
</SoftwareArchitects>
…
<httpHandlers>
<add verb=”*” path=”*.gif.ashx”
type=”SoftwareArchitects.Web.CachingHandler,
SoftwareArchitects.Web.CachingHandler”/>
<add verb=”*” path=”*.jpg.ashx”
type=”SoftwareArchitects.Web.CachingHandler,
SoftwareArchitects.Web.CachingHandler”/>
<add verb=”*” path=”*.png.ashx”
type=”SoftwareArchitects.Web.CachingHandler,
SoftwareArchitects.Web.CachingHandler”/>
</httpHandlers>
</configuration>
在站点中完成以上代码的添加之后,再次使用Microsoft Network Monitor进行测试,第一次访问首页时依然是20个不同的请求,但到了第二次访问请求就变为了7个,因为所有的图片文件都已经缓存到了客户端。
在原文中,作者还提供了一个完整的Solution来测试图片缓存功能,大家可以自由下载.