[转载]《大型网站技术架构》读书笔记之五:万无一失之网站的高可用架构 - Edison Chou - 博客园

mikel阅读(973)

[转载]《大型网站技术架构》读书笔记之五:万无一失之网站的高可用架构 – Edison Chou – 博客园.

此篇已收录至《大型网站技术架构》读书笔记系列目录贴,点击访问该目录可获取更多内容。

一、可用性度量与考核

首先,不得不说:要保证一个网站永远完全可用几乎是一件不可能完成的任务(Mission Impossible,是不是有点碟中谍的感觉)

(1)如何度量网站可用性?

一个神奇的数字—9!你有几个9,就代表了你的可用性。例如QQ可用性达到了4个9:99.99%

①2个9=基本可用  ②3个9=较高可用  ③4个9=具有自动恢复能力的高可用  ④5个9=极高可用->理想状态

那么,可用性的9又是怎么计算出来的呢:

①网站不可用时间=故障修复时间点-故障发现时间点

②网站年度可用性指标=(1-网站不可用时间/年度总时间)*100%

(2)如何考核网站可用性?

广泛采用故障分的,它是对网站故障进行分类加权计算故障责任的方法。一般会给每个分类的故障设置一个权重 (例如事故级故障权重为100,A类为20等),其计算公式为:故障分=故障时间(分钟)*故障权重。公司对技术团队的考核一般会参考故障分,例如某团队 今年发生了几个事故级故障,那么其绩效考核估计受到很大影响,年终奖什么的就悲剧了。

二、高可用的架构

目前,通常企业级应用系统(特别是政府部门和大企业的应用系统)一般会采用安规的软硬件设备,如IOE(IBM的小型机、Oracle数据、 EMC存储设备)系列。而一般互联网公司更多地采用PC级服务器(x86),开源的数据库(MySQL)和操作系统(Linux)组建廉价且高容错(硬件故障是常态)的应用集群。

(1)设计的目的?

保证服务器硬件故障服务依然可用,数据依然保存并能够被访问

(2)主要的手段?

数据和服务的①冗余备份以及②失效转移

对于服务而言,一旦某个服务器宕机,就将服务切换到其他可用的服务器上;

对于数据而言,如果某个磁盘损坏,就从备份的磁盘(事先就做好了数据的同步复制)读取数据。

三、高可用的应用

应用层处理网站应用的业务逻辑,应用的一个最显著的特点是:应用的无状态性

PS:提到无状态特性,不得不说下Http协议。我们常常听到说,Http 是一个无状态协议,同一个会话的连续两个请求互相不了解,他们由最新实例化的环境进行解析,除了应用本身可能已经存储在全局对象中的所有信息外,该环境不 保存与会话有关的任何信息。之所以我们在使用ASP.NET WebForm开发中会感觉不到Http的无状态特性,完全是因为Microsoft帮我们实现了ViewState,它是ASP.NET WebForm中保存页面信息的基本单位,本质是一个HTML中的隐藏域,回调时会将这个隐藏域中的数据提交到服务器端。  

(1)通过负载均衡进行无状态服务的失效转移

(2)应用服务器集群的Session管理

首先,不得不说的是:Web应用中将上下文对象称为会话(Session),单机情况下由部署在服务器上得Web容器(如IIS、Tomcat、JBoss等)管理。在使用了负载均衡的集群环境中,由于请求的分发是随机的,所以保证每次请求依然能够获得正确的Session比单机时要复杂得多

其次,我们来看看在集群环境中,Session管理的几种常见手段。

①Session复制:该方案简单易行,集群中的几台服务器之间同步Session对象,任何一台服务器宕机都不会导致Session对象的丢失,服务器也只需要从本机获取即可。但是,该方案只适合集群规模较小的情况下。当规模较大时,大量的Session复制操作会占用服务器和网络的大量资源,系统不堪重负

②Session绑定:利用负载均衡的源地址Hash算法,总是将源于同一IP地址的请求分发到同一台服务器上。这样的话,在整个会话期间,用户所有的请求都在同一台服务器上进行处理,即Session绑定在某台特定服务器上,保证Session总能在这台服务器上获取。(这种方案又叫做会话粘滞)。

但是,这种方案不符合高可用的需求。因为一旦某台服务器宕机,那么该机器上得Session也就不复存在了,用户请求切换到其他机器后因为没有Session而无法完成业务处理。因此,很少有网站采用此方案进行Session管理。

③Cookie记录Session:利用浏览器支持的Cookie记录Session简单易行,可用性高,并且支持服务器的线性伸缩,因此,许 多网站都或多或少地使用了Cookie来记录Session。但是Cookie记录Session有缺点:比如受Cookie大小限制、每次请求响应都要 传输Cookie影响性能、用户关闭了Cookie会造成访问不正常等。

Session服务器:利用独立部署的Session服务器(集群)统一管理Session,应用服务器每次读写Session时,都访问Session服务器。这种方案实际上是将应用服务器的状态分离,分为无状态的应用服务器有状态的Session服务器

对于,有状态的Session服务器,一种较简单的方法是利用分布式缓存(如Memcached、Redis等,有关Redis的简单介绍可以阅读我的博文:NoSQL初探之人人都爱Redis)、数据库等,在这些产品的基础上进行封装,使其符合Session的存储和访问要求。

四、高可用的服务

高可用的服务模块为业务产品提供基础公共服务,在大型站点中这些服务通常都独立分布式部署,被具体应用远程调用。

在具体实践中,有以下几点高可用的服务策略可以参考:

①分级管理:核心应用和服务具有更高的优先级,比如用户及时付款比能否评价商品更重要;

②超时设置:设置服务调用的超时时间,一旦超时后,通信框架抛出异常,应用程序则根据服务调度策略选择重试or请求转移到其他服务器上;

③异步调用:通过消息队列等异步方式完成,避免一个服务失败导致整个应用请求失败的情况。

PS:不是所有服务都可以异步调用,对于获取用户信息这类调用,采用异步方式会延长响应时间,得不偿失。对于那些必须确认服务调用成功后才能继续进行下一步的操作的应用也不适合异步调用。有关具体使用消息队列实现异步调用的案例,请阅读我的博文:《使用Redis作为消息队列服务场景的应用案例》。

④服务降级:网站访问高峰期间,为了保证核心应用的正常运行,需要对服务降级。

降级有两种手段:一是拒绝服务,拒绝较低优先级的应用的调用,减少服务调用并发数,确保核心应用的正常运行;二是关闭功能,关闭部分不重要的服务,或者服务内部关闭部分不重要的功能,以节约系统开销,为核心应用服务让出资源;

⑤幂等性设计:保证服务重复调用和调用一次产生的结果相同;

五、高可用的数据

对于大多数网站而言,数据是其最宝贵的物质资产。

保证数据高可用的主要手段有两种:一是数据备份,二是失效转移机制;

①数据备份:又分为冷备份和热备份,冷备份是定期复制,不能保证数据可用性。热备份又分为异步热备和同步热备,异步热备是指多份数据副本的写入操作异步完成,而同步方式则是指多份数据副本的写入操作同时完成。

关系数据库的热备机制就是通常所说的主从同步机制,实践中通常使用读写分离的方法来访问Master和Slave数据库,也就是说写操作只访问Master库,读操作均访问Slave库。

PS:在MS SQL Server中,可以通过发布订阅功能实现主从分离。关于发布订阅,可以参考MSDN的这篇文章:http://technet.microsoft.com/zh-cn/ff806143.aspx

②失效转移:若数据服务器集群中任何一台服务器宕机,那么应用程序针对这台服务器的所有读写操作都要重新路由到其他服务器,保证数据访问不会失败。

六、高可用的QA

①网站发布:在柔性的发布过程中,每次关闭的服务都是集群中的一小部分,并在发布完成后立即可以访问;

②自动化测试:使用自动测试工具或脚本完成测试;

③预发布验证:引入预发布服务器,与正式服务器几乎一致,只是没有配置在负载均衡服务器上,外部用户无法访问;

④代码控制:目前大多数网站采用SVN,分支开发,主干发布模式;另外,目前开源社区广泛采用Git作为版本控制工具,正逐步取代SVN的地位;

七、网站运行监控

”不允许没有监控的系统上线“

(1)监控数据采集

①用户行为日志收集:服务器端的日志收集和客户端的日志收集;目前许多网站逐步开发基于实时计算框架Storm的日志统计与分析工具;

②服务器性能监控:收集服务器性能指标,如系统Load、内存占用、磁盘IO等,及时判断,防患于未然;

③运行数据报告:采集并报告,汇总后统一显示,应用程序需要在代码中处理运行数据采集的逻辑;

(2)监控管理

①系统报警:配置报警阀值和值守人员联系方式,系统发生报警时,即使工程师在千里之外,也可以被及时通知;

②失效转移:监控系统在发现故障时,主动通知应用进行失效转移;

③自动优雅降级:为了应付网站访问高峰,主动关闭部分功能,释放部分系统资源,保证核心应用服务的正常运行;—>网站柔性架构的理想状态

学习小结

本篇我们通过书籍学习了为了实现网站的高可用,可以使用的策略和模式。书中作者有一句话说的十分好,”事务总是先求生存,然后求发展“。保证网 站高可用,万无一失,任重而道远啊!今天的学习笔记就分享到这里,洗洗睡了,么么嗒!对了,今天去影院看了老男孩之猛龙过江,看二傻大闹牛哟可(New York),听了小苹果,顿时感觉自己萌萌哒,有木有?不谈梦想和剧情,里面的几首歌还是蛮好听的。

本章思维导图

 

flaredream studio

[转载]使用jQuery Mobile和Phone Gap开发Android应用程序 - 技术狂 - 博客园

mikel阅读(1259)

[转载]使用jQuery Mobile和Phone Gap开发Android应用程序 – 技术狂 – 博客园.经过了一段时间的学习,初步了解了该如何使用JQuery Mobile和 Phone Gap来开发一个Android应用程序,也想把这些东西介绍给大家。
1、 软件准备
要进行Android app的开发,当然需要准备Java, eclipse和安装Android SDK,这个部分网络上面很多方法,搜索“安装Android SDK”即可找到很多答案,所以就不再这里浪费口水。

2、 知识准备
(1)了解JQuery Mobile这个js框架,知道怎么组织一个简单的页面。
官方网站:http://JQuerymobile.com/(记得下载一个js库文件)
(2)了解Phone Gap,怎么利用Phone Gap在后面的内容也有介绍。
官方网站:http://phonegap.com/(同样记得下载相关文件)
(3)能够使用jQuery进行开发。

3、 组织工程目录
(1)打开Eclipse,建立一个android应用工程,见下图

(2)解压phonegap的压缩包,可以看到它针对不懂的应用类型进行了不同的分类,有android、IOS、Windows Phone等移动终端系统,打开其中的android文件夹。
(3)在刚才新建的工程的根目录下新建一个名为libs的文件夹,找到(1)中android文件夹中的jar包粘贴到刚才的libs文件夹下。
(4)将(1)中android文件夹下的xml文件夹整个粘贴到工程更目录下的res文件夹下。
(5)在工程的assets文件夹下新建文件夹www,这个文件夹其实可以看作是phonegap的工程目录,用来放js或者html文件。
(6)在文件夹www下面新建一个js文件夹,用来放置js和css文件;新建文件夹pages用来放置html文件。(新建html和引入js库可以参照图操作)
工程目录如下图:

4 Conding
(1)首先打开src下的Java类,修改继承类为DroidGap(如果找不到这个类,估计是忘记将PhoneGap的jar包加入工程的Libraries),并且修改代码,如下图

(2)打开index.html文件,进行编辑,记得开头要用html5的doctype声明。我在里面加入两个简单的jQuery Mobile的页面,并且调用了简单的Phone Gap的API:
http://docs.phonegap.com/en/1.3.0/phonegap_notification_notification.md.html#notification.vibrate
代码如下:


<!Doctype html>

Phone Gap Introduce

<script src="../JS/jquery_1_6_4.js"></script><script src="../JS/phonegap-1.2.0.js"></script>
<script src="../JS/jquery.mobile-1.0rc1.js"></script><script>// <![CDATA[
$('#PageOne').live('pageinit', function(event){                              var showTip = function(){                           navigator.notification.alert("this is a message from page one!", null, "Message", "Close");                           $(this).die("click");                       };                                              var confirm = function(){                           navigator.notification.confirm(                                   'You are the winner!',  // message                                   null,                   // callback to invoke with index of button pressed                                   'Game Over',            // title                                   'Restart,Exit'          // buttonLabels                               );                           $(this).die("click");                       };                                              var redirectPage = function(){                           $.mobile.changePage("#PageTwo");                           $(this).die("click");                       };                                              $(event.target).find('#alert').bind('click', showTip);                       $(event.target).find('#confirm').bind('click', confirm);                       $(event.target).find('#changePage').bind('click', redirectPage);                   });                                      $('#PageTwo').live('pageshow', function(event){                       var showTip = function(){                           navigator.notification.alert("this is a message from page two!", null, "Message", "Close");                           $(this).die("click");                       };                                              var confirm = function(){                           navigator.notification.confirm(                                   'You are the losser!',  // message                                   null,                   // callback to invoke with index of button pressed                                   'Game Over',            // title                                   'Restart,Exit'          // buttonLabels                               );                           $(this).die("click");                       };                       $(event.target).find('#alert').bind('click', showTip);                       $(event.target).find('#confirm').bind('click', confirm);                   });
// ]]></script>
<div id="PageOne" data-role="page">
<div data-role="header" data-backbtn="false">
<h1>Phone Gap One</h1>
</div>
<div data-role="content">
<div><a id="alert" href="#" data-role="button" data-theme="b">Alert</a></div>
<div><a id="confirm" href="#" data-role="button" data-theme="b">Confirm</a></div>
<div><a id="changePage" href="#" data-role="button" data-theme="b">Change Page</a></div>
</div>
<div data-role="footer">
<div data-role="navbar">
<ul>
	<li><a href="#PageOne">Page One</a></li>
	<li><a href="#PageTwo">Page Two</a></li>
</ul>
</div>
</div>
</div>
<div id="PageTwo" data-role="page">
<div data-role="header" data-backbtn="true">
<h1>Phone Gap Two</h1>
<a data-role="button" data-rel="back">Previous</a>

</div>
<div data-role="content">
<div><a id="alert" href="#" data-role="button" data-theme="b">Alert</a></div>
<div><a id="confirm" href="#" data-role="button" data-theme="b">Confirm</a></div>
<div><a href="file:///android_asset/www/Pages/pageThree.html" data-role="button" data-theme="b">Page Three</a></div>
</div>
<div data-role="footer">
<div data-role="navbar">
<ul>
	<li><a href="#PageOne">Page One</a></li>
	<li><a href="#PageTwo">Page Two</a></li>
</ul>
</div>
</div>
</div>

要特别注意的是引入js库的顺序,参照下图:


即:自己的包和phonegap的js包要放在中间,不然会出一些错误,我开发的时候是遇见过这种状况的,而且jQuery Mobile的官方也是这么要求的。

再打开pageThree.html,加入如下代码:

<div id="PageThree" data-role="page">
<div data-role="header" data-backbtn="true">
<h1>Phone Gap Three</h1>
<a data-role="button" data-rel="back">Previous</a>

</div>
<div data-role="content">
<div><a id="alert" href="#" data-role="button" data-theme="b">Alert</a></div>
<div><a id="confirm" href="#" data-role="button" data-theme="b">Confirm</a></div>
</div>
<div data-role="footer">
<div data-role="navbar">
<ul>
	<li><a href="#PageOne">Page One</a></li>
	<li><a href="#PageTwo">Page Two</a></li>
</ul>
</div>
</div>
</div>

选择工程,右键run as > android application,你应该能够看到下图:

到这里工程的开发已经完了,希望有兴趣的可以具体操作一遍,然后可以修修改改其中的一些东西,这样才能体会到这个开发过程是怎么一回事,光看和复制粘贴是很容易忘记怎么去开发的。

在我进行了一段时间的开发之后,我认为phonegap的好处在于:
(1)一个应用能够很容易就移植到其他的平台,不需要同样的逻辑写多种语言版本;
(2)容易上手,学习了html5和js既可以进行开发;
(3)如果学会了如何开发phonegap插件,那么android能够做的事情,phonegap都能够帮你完成,其他平台开发也如此。(如何开发插件已经不是这篇blog的内容了)
同时我感觉phonegap让我最不爽的一点就是调试太麻烦了,要在模拟器上才能看到效果到底对不对。

同时附上开发简易顺序:
(1)把phonegap的jar包和xml文件放到工程下的正确目录;
(2)修改src下的android默认类,参照4 (1);
(3)在aseets下面建立工程的根目录www,并在其中放入js、html、image、css等普通的web文件;
(4)只需要在index页面加入js包和css文件,其他页面只需要组织成一个简单的jQuery Mobile页面。
(5)调用一些特殊的api时,需要注意申请android许可!(百度一下就可以轻松解决)

最后一个压缩包是工程压缩包。

  • Introduce.7z (595.5 KB)
  • 描述: 工程压缩包
  • 下载次数: 1205

想法和产品之间到底有多远?!

mikel阅读(934)

习惯了早晨写日志,脑袋清醒。

想法随时随地都会产生,可能够实现的少之又少,就比如我建的豆瓣“创意想法”小组,里面收集了我和大家的奇思妙想,可能够经得起推敲,并付诸实施的很少,很多都流产了。

想法多了不会“怀孕”!但是不去实践,那就只是想法!

有想法就去做吧!不做怎么知道不行?!如果连做的机会都不给自己,那你就别想了!那是浪费时间!

很多时候,从一个想法到实现产品中间要经过不知道多少次迭代完善的过程,有些人放弃了;有些人否定自己了;有些人坚持下来了;做出来才是硬道理!

只要出了产品就会有人用!不要想太多,很多时候都是在咸吃萝卜淡操心,意淫而已!

最直接最有效验证想法是否可行,产品是否有市场,的方法就是让目标用户来投票!

完美主义者是最坑爹的一帮人,世界上就tmd没有完美的东西!他们还愣要做出完美的东西!这不是自己跟自己过不去么?!

可能你会问:如果做出的产品真的没人用怎么办?

没人用你还获得了一次经验,何乐而不为呢?!

开发实践的过程往往会和最初的想法出入很大,产品和想法之间的距离不是笔直的直线,而是曲曲折折的图,没有坚持和改造想法是不完整的,产品也是不完整的。

这是一个从无到有的过程,也是一个没有终点的过程,但是只有一点是不变的产品是给目标用户用的!必须符合事物的客观规律,只有好的想法才能吸引好的用户,然后好的用户又会产生好的想法来成就好的产品,然后就是死循环的迭代过程。

想法和产品之间到底有多远? 用户就和你有多远!

[转载]Flex4/Flash+BlazeDS+JAVA+MySql 开发在线音乐播放器实例,含测试地址 - Li-Cheng - 博客园

mikel阅读(1411)

[转载]Flex4/Flash+BlazeDS+JAVA+MySql 开发在线音乐播放器实例,含测试地址 – Li-Cheng – 博客园.

要求

  • 必备知识

    本文要求基本了解 Adobe Flex编程知识和JAVA基础知识。

  • 开发环境

    MyEclipse10/Flash Builder4.6/Flash Player11及以上

  • 演示地址

    演示地址

 

传统网络程序的开发是基于页面的、服务器端数据传递的模式,把网络程序的表现层建立于HTML页面之上,而HTML是适合于文本的,传统的基于页面 的系统已经渐渐不能满足网络浏览者的更高的、全方位的体验要求了。而富互联网应用(Rich Internet Applications,缩写为RIA)的出现就是为了解决这个问题。在HTML5发布以前,RIA领域的技术解决方案一直相都是各展所长,并无争议。 Adobe体系中,Flash做不了的事情,Flex可以做到;.Net系决策者在选用RIA解决方案时,Silverlight是不二之选。随着 HTML 5横空出世,Flex“易主”(这里说的是Adobe将Flex捐给Apache),Silverlight被“雪藏”(这里指微软停止对 Silverlight的更新),RIA领域的技术解决方案开始变得扑朔迷离。 HTML 5无疑是“明日之星”,苹果公司前CEO乔布斯对它赞赏有加,绝大多数智能手机浏览器均支持HTML 5,基于HTML 5的网站也如雨后春笋般出现。这些似乎预示着HTML 5时代来临,人们试图让决策者相信,Flash/Flex时代已经过去了,HTML 5才是RIA领域的最佳解决方案。那到底真相会是什么呢? HTML 5其实也存在许多劣势,并不完美。HTML 5的浏览器兼容性问题(由于国内传统IE浏览器占了相当大的比重);要实现html5应用,还要写CSS与JavaScript,增加了人员构成和开发成 本,其编写难度也要远大于Flex。在较长一段时间内,HTML5是无法”替代”Flex技术的,也许最终HTML 5与Flex将成为是两种截然不同的技术解决方案,所以,它们是互补的,而非替代。如对Flash/Flex/Html5还不了解,我到互联网上找了一篇 相关的文章和大家分享一下: 浅谈 Flash/Flex/HTML5 技术选型

 

一,Flex4&BlazeDS&JAVA整合:

Myeclipse10.6+Flash Builder 4.6安装配置

 

二,用户界面设计:

  • 播放器界面的设计:

 

  • 专辑制作界面的设计:

 

三,数据库设计:

  • album(专辑表)创建语句:
CREATE TABLE `album` (
`a_id` int(11) NOT NULL AUTO_INCREMENT,
`a_name` varchar(20) NOT NULL DEFAULT '',
`a_singer` varchar(20) NOT NULL DEFAULT '',
`a_image` varchar(120) NOT NULL DEFAULT '',
PRIMARY KEY (`a_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;

song(歌曲表)创建语句:

CREATE TABLE `song` (
`s_id` int(11) NOT NULL AUTO_INCREMENT,
`a_id` int(11) NOT NULL,
`s_name` varchar(120) NOT NULL DEFAULT '',
`s_source` varchar(150) NOT NULL DEFAULT '',
PRIMARY KEY (`s_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;

四,前端代码物理实现(Flex4部分代码实现):

  • 播放器界面物理实现,通过在主程序中定义三个不同的自定义组件,”专辑””列表”“播放器控制面板”来构成整个播放器界面。

1,程序入口文件:myMusicPlayer.mxml

<!--?xml version="1.0" encoding="utf-8"?-->

<!-- 将非可视元素(例如服务、值对象)放在此处 -->

[Event(name="changeitem",type="events.MyEvent")] //通过 Event元数据 定义自定义监听事件

&nbsp;

<!--程序主题-->

<!--背景图片-->

<!--专辑-->

<!--列表-->

<!--播放器控制面板-->

2,自定义“专辑”组件:Album.mxml

<!--?xml version="1.0" encoding="utf-8"?-->
<!-- 将非可视元素(例如服务、值对象)放在此处 -->
[Event(name="changeitem3",type="events.MyEvent3")]

&nbsp;

3,自定义“列表”组件:List.mxml

<!--?xml version="1.0" encoding="utf-8"?-->

<!-- 将非可视元素(例如服务、值对象)放在此处 -->
[Event(name="changeitem",type="events.MyEvent")]

&nbsp;

4,自定义“播放器控制面板”组件:Player.mxml

<!--?xml version="1.0" encoding="utf-8"?-->

<!-- 将非可视元素(例如服务、值对象)放在此处 -->

&nbsp;

<!--背景图片-->

<!--播放按钮-->

<!--音量条-->

<!--进度条和歌曲信息-->

关于flex操作音频文件,可以参考我之前做的一个简单音乐播放器实例:

博客中 Flex4/Flash mp3音乐播放器实例 含演示地址

 

  • 专辑制作插件物理实现、

MyUpload3.mxml

关于flex操作音频文件,可以参考我之前做的一个简单音乐播放器实例:

博客中 Flex4/Flash mp3音乐播放器实例 含演示地址

专辑制作插件物理实现、

MyUpload3.mxml

<!--?xml version="1.0" encoding="utf-8"?-->

<!-- 将非可视元素(例如服务、值对象)放在此处 -->
<![CDATA[             import components.Album;             import components.GridHeaderRenderer;             import components.MultiFileUpload;                          import events.MyEvent;                          import mx.collections.ArrayCollection;             import mx.collections.ArrayList;             import mx.controls.Alert;             import mx.utils.StringUtil;                          import spark.components.gridClasses.GridColumn;             [Bindable]             private var data=new ArrayCollection([                 {id:"1",song:"心愿",sf:".mp3"},                 {id:"2",song:"心愿",sf:".mp3"},                 {id:"3",song:"心愿",sf:".mp3"},                 {id:"4",song:"心愿",sf:".mp3"},                 {id:"4",song:"心愿",sf:".mp3"}             ]);                          public var album:Album;  //专辑信息上传对象             public var multiFileUpload:MultiFileUpload;  //音乐文件上传对象                          public var imageTypes:FileFilter = new FileFilter("Images (*.jpg; *.jpeg; *.gif; *.png)" ,"*.jpg; *.jpeg; *.gif; *.png");             public var imagefilesToFilter:Array = new Array(imageTypes); //图片过滤数组             public var ImageUploadDestination:String = "http://localhost:8080/MyUpload4/AlbumAdd";  //专辑信息上传地址                          public var musicTypes:FileFilter = new FileFilter("Music Files (*.mp3)","*.mp3");             public var filesToFilter:Array = new Array(musicTypes); //音乐过滤数组             public var uploadDestination:String = "http://localhost:8080/MyUpload4/FileUploadServlet";  //专辑信息上传地址                                                    private function init():void{                                  album=new Album(albumName,singer,ImageUploadDestination,imagefilesToFilter,img,create);                 album.addEventListener(MyEvent.COMPLETE,albumUpload);                                                   //初始化数据列表头                 var _nameColumn = new GridColumn;                 var _typeColumn = new GridColumn;                 var _sizeColumn = new GridColumn;                                                   _nameColumn.headerText= "File";                 _nameColumn.headerRenderer=new ClassFactory(GridHeaderRenderer);                                  _typeColumn.headerText = "File Type";                 _typeColumn.width = 80;                 _typeColumn.headerRenderer=new ClassFactory(GridHeaderRenderer);                                  _sizeColumn.headerText = "File Size";                 _sizeColumn.width = 150;                 _sizeColumn.headerRenderer=new ClassFactory(GridHeaderRenderer);                                  var _columns = new ArrayList([_nameColumn,_typeColumn,_sizeColumn]);                 filesDG.columns=_columns;             }                          private function albumUpload(e:MyEvent):void{                                               var postVariables:URLVariables = new URLVariables;                 postVariables.id = e.data;                                                                         multiFileUpload = new MultiFileUpload(                     filesDG,                     browseBTN,                     clearButton,                     delButton,                     upload_btn,                     progressbar,                     uploadDestination,                     postVariables,                     10240000,                     filesToFilter                 );                              //    var test=filesDG.columns.getItemAt(0);                                                                                 }                                                                               ]]>

<!--专辑信息-->

&nbsp;

<!--数据列表-->

<!--<s:columns>
<s:ArrayList>
<s:GridColumn dataField="id" headerText="#" headerRenderer="components.GridHeaderRenderer"  itemRenderer="components.GridItemRenderer"></s:GridColumn>
<s:GridColumn dataField="song" headerText="歌曲" headerRenderer="components.GridHeaderRenderer"  itemRenderer="components.GridItemRenderer"></s:GridColumn>
<s:GridColumn dataField="sf" headerText="后缀" headerRenderer="components.GridHeaderRenderer" itemRenderer="components.GridItemRenderer"></s:GridColumn>
</s:ArrayList>
</s:columns>-->

&nbsp;

关于文件上传部分,是根据MultiFile Upload插件,通过自定义UI组件皮肤完成,可以参考我之前的一个修改实例:

Flex4/Flash多文件上传(带进度条)实例分享

五,后端代码物理实现(JAVA部分代码物理实现):

AlbumDao.java

package com.dao;

import java.awt.List;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;

import com.bean.Album;

import com.resource.JDBCUtilSingle;

public class AlbumDao {

/**
* 插入专辑信息 返回ID号
* @param a_name
* @param a_singer
* @param a_image
* @return
*/
public int addAlbum(String a_name,String a_singer,String a_image){
Connection connection=null;
PreparedStatement statement=null;
ResultSet rs=null;
connection=JDBCUtilSingle.getInitJDBCUtil().getConnection();
int id=0;

try {
//专辑信息插入
String sql="insert into album(a_name,a_singer,a_image) values(?,?,?)";
statement=connection.prepareStatement(sql);
statement.setString(1,a_name);
statement.setString(2,a_singer);
statement.setString(3,a_image);
//System.out.println(sql);
statement.executeUpdate();
//获取插入ID
rs = statement.getGeneratedKeys();
rs.next();
id = rs.getInt(1);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
JDBCUtilSingle.getInitJDBCUtil().closeConnection(rs, statement, connection);
return id;
}

/**
* 获取所有专辑信息
* @return 返回专辑列表
*/
public ArrayList selectAlbum(){
Connection connection=null;
PreparedStatement statement=null;
ResultSet rs=null;
connection=JDBCUtilSingle.getInitJDBCUtil().getConnection();
ArrayList albums=new ArrayList();
try {
//专辑信息插入
String sql="select * from album";
statement=connection.prepareStatement(sql);
rs=statement.executeQuery();
while(rs.next()){
albums.add(new Album(rs.getInt("a_id"), rs.getString("a_name"), rs.getString("a_singer"), rs.getString("a_image")));
}

} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
JDBCUtilSingle.getInitJDBCUtil().closeConnection(rs, statement, connection);
}
return albums;
}
}

SongDao.java

package com.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;

import com.bean.Album;
import com.bean.Song;
import com.resource.JDBCUtilSingle;

public class SongDao {

public int addSongs(int a_id,String s_name,String s_source){
Connection connection=null;
PreparedStatement statement=null;
ResultSet rs=null;
connection=JDBCUtilSingle.getInitJDBCUtil().getConnection();
int tag=0;
try {
String sql="insert into song(a_id,s_name,s_source) values(?,?,?)";
statement=connection.prepareStatement(sql);
statement.setInt(1,a_id);
statement.setString(2,s_name);
statement.setString(3,s_source);
tag=statement.executeUpdate();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

JDBCUtilSingle.getInitJDBCUtil().closeConnection(rs, statement, connection);
return tag;
}

public ArrayList getSongs(int a_id){
ArrayList songs=new ArrayList();
Connection connection=null;
PreparedStatement statement=null;
ResultSet rs=null;
connection=JDBCUtilSingle.getInitJDBCUtil().getConnection();
try {
String sql="select * from song where a_id="+a_id;
statement=connection.prepareStatement(sql);
rs=statement.executeQuery();
while(rs.next()){
songs.add(new Song(rs.getInt("s_id"), rs.getInt("a_id"), rs.getString("s_name"), rs.getString("s_source")));
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{

JDBCUtilSingle.getInitJDBCUtil().closeConnection(rs, statement, connection);
}

return songs;

}
}

好吧,就写到这里了,因为代码是较早之前写的了,由于学校放假,闲来没事,就那之前的代码翻看了一遍,由于时间久远,也没写什么注释,在代码的语法 和程序逻辑上,自己也硬是看了半天才回过神来。现在在代码中加了一些注释,添加了一些歌曲内容,已上传到测试空间中,欢迎大家来测试。由于本人水平有限, 如文章在表述或代码方面有何不妥之处,欢迎批评指正。

 

你可能还对以下关于Flex的文章内容还感兴趣:

Flex4/AS3.0自定义VideoPlayer组件皮肤,实现Flash视频播放器

Flex4+BlazeDS+JAVA+MySQL 构建J2EE工程 对用户信息进行管理实例

Flex4/Flash多文件上传(带进度条)实例分享

在自己网站中引入CU3ER/Flash 3D幻灯片效果和照片画框

博客中 Flex4/Flash mp3音乐播放器实例 含演示地址

 

如以上文章或链接对你有帮助的话,别忘了在文章结尾处轻轻点击一下 “还不错”按钮或到页面右下角点击 “赞一个” 按钮哦。你也可以点击页面右边“分享”悬浮按钮哦,让更多的人阅读这篇文章。

作者:Li-Cheng
由于本人水平有限,文章在表述和代码方面如有不妥之处,欢迎批评指正。留下你的脚印,欢迎评论哦。你也可以关注我,一起学习哦!

[转载]p2p网贷平台设计简析 - 发表是最好的记忆 - 博客园

mikel阅读(1101)

[转载]p2p网贷平台设计简析 – 发表是最好的记忆 – 博客园.

以我之前主持开发的一个商业产品:p2p网袋为例进行分析。大家如果要查看平台的具体,可以查看

www.huixinp2p.com/www.hanghangp2p.com

界面可以直接参考前期博客:http://www.cnblogs.com/shenliang123/p/3435427.html

其中涉及到的部分web安全的解决可以参考最新博客:http://www.cnblogs.com/shenliang123/p/3835116.html

1.1.需求分析

系统按功能模块进行划分可分为三大模块:网站前台交易平台用户个人账户中心业务后台支撑系统。根据分析可以得到图3-1所示的分析用例图

图1-1 分析用例图

根据如图3-1所示A区域即为网站前台交易平台主要包括的操作有网站新闻及服务信息查看,会员注册,借款浏览等。B区域即为用户个人账户中心包括各种会员认证,VIP申请,资料上传,额度申请,借款的发布及查看,投资管理,偿还借款,充值,提现等功能。C区域即为业务后台支撑系统包括贷款管理,资金管理,资金记录,会员管理,报表分析,奖励与费用,系统维护等功能。

1.2功能模块分析

系统按功能模块进行划分可分为三大模块:网站前台交易平台用户个人账户中心业务后台支撑系统。对这三大模块进行功能的细分:

网站前台交易平台:可以细分为四个模块,分别是贷款标浏览,贷款标详情,会员注册和网站信息查阅。具体功能模块图如图1-2所示:

图1-2 功能模块图1

用户个人账户中心:可以细分为五个功能模块,分别为基本设置,资金管理,借款管理,投资管理和好友管理。具体功能模块图如图1-3所示:

图1-3 功能模块图2

业务后台支撑系统:可以细分为七个功能模块,分别是贷款管理,资金管理,资金记录,会员管理,报表分析,奖励与费用和系统维护。具体功能模块图如图1-4所示:

图1-4 功能模块图3

1.3功能模块分析

贷款流程

用户注册并通过短信验证码验证成功后成为本系统的会员,有相应的会员中心。会员登录中心后首先需要填写基本资料,有个人资料,联系资料,单位资料, 财务资料,房产资料,联保资料等。成功填写完资料后需要经过一系列的认证,包括邮箱认证,手机认证,实名认证,现场认证,视频认证,资料认证等。待管理员 审核成功后可以申请VIP会员,管理员审核成功后用户即可进行正常贷款。

会员首先选择自己需要发起的标种,分别有抵押标,信用标,秒还标,净值标。

选择标种后填写相应的贷款信息,核查无误后贷款提交,管理员进行发标审核,审核通过后,就进行资金的募集,在规定时间内成功募集到资金,则自动提交 管理员进行满标复审,复审通过后则进入还款阶段,按照提交贷款所选择的还款方式进行还款,若出现逾期,则按相应的规则进行处罚。若未成功募集满资金,则本 次借款自动流标。整个流程图如图1-3所示:

图1-3 贷款流程图

投标流程

用户注册并通过短信验证码验证成功后成为本系统的会员,进行身份验证,验证审核通过后进行账户的充值,充值成功后对正在筹资的标进行投资,满标通过后即投标成功,收取相应的本金和利息,之后可以选择继续投资其他标或进行提现。整个流程图如图1-4所示:

图1-4 投标流程图

1.4数据库分析

系统概念设计

本系统E-R图分析如图1-5所示。主要实体为借款,投标记录,应收明细,还款明细,奖励记录,回款记录,续投奖励记录等。

图1-5 借款投标E-R图(部分)

 系统物理模型简介

会员的借款和投资是本系统的核心流程之一,以图1-6所示物理模型进行分析。

会员发起借款,提交确认借款信息后,借款信息将被存储在“借款”表中,系统管理员通过网站后台对借款进行发标初审和设定借款发布时间,审核结果存储 在“借款”表的“状态”字段中,借款发布时间存储在“借款”表的“计划发布时间”字段中,待发标初审通过并到达发布时间,借款将自动发布,“借款”表的 “状态”字段自动更新为“发布中”。

投资人浏览借款信息,投资符合条件的借款标,投资记录将被存储在“投标记录”表中,相应的在“借款”表中更新相应的“已投总额”,待“已投总额”= = “借款总额”,该借款将自动提交,系统管理员进行满标复审。复审通过后,将会计算相应的还款明细,收款明细,奖励(投标奖励),费用(网站风险补偿金)和 续投奖励等分别存储在表“还款明细”,“应收明细”,“奖励记录”,“手续费(风险费)”和“续投奖励记录”中。整个借款投标过程中涉及到的资金明细记录 都存储在“资金记录”表中。满标复审通过后,进入还款阶段,还款的物理模型分析将在后面具体展开。

图1-6 借款与投标物理模型

本系统的另一大核心是会员还款,以图1-7所示还款物理模型进行分析:

“借款”表中的“还款方式”字段存储了借款人提交借款信息时选择的还款方式,借款依照“还款方式”按期进行还款,到期正常还款或提前还款后,系统将更新 “应收明细”和“还款明细”表中的“状态”为“已还”。系统相应的收取会员的利息管理费存储于“手续费”表中。若会员逾期未还,则系统会按相应的规则自动 计算逾期罚款,存储于“逾期罚款”表中。

图1-7 还款物理模型

 

 

 

 

 

 

 

 

 

希望多多交流,多多关注,共同成就梦想

[转载]解决C#读写txt文件中文(汉字)乱码的问题 - Virson Ma - 博客园

mikel阅读(1166)

[转载]解决C#读写txt文件中文(汉字)乱码的问题 – Virson Ma – 博客园.

不少朋友遇到用System.IO.StreamReader读取包含汉字的txt文件时,经常会读出乱码(StreamWriater写文本 文件也有类似的问题),原因很简单,就是文件的编码(encoding)和StreamReader/Writer的encoding不对应。
为了解决这个问题,我写了一个类,来取得一个文本文件的encoding,这样我们就可以创建对应的
StreamReader和StreamWriter来读写,保证不会出现乱码现象。其实原理很简单,文本编辑器(比如XP自带的记事
本)在生成文本文件时,如果编码格式和系统默认的编码(中文系统下默认为GB2312)不一致时,会在txt文件开头
部分添加特定的“编码字节序标识(Encoding Bit Order Madk,简写为BOM)”,类似PE格式的”MZ”文件头。这样
它在读取时就可以根据这个BOM来确定该文本文件生成时所使用的Encoding。这个BOM我们用记事本等程序打开默认
是看不到的,但是用stream按字节读取时是可以读到的。我的这个TxtFileEncoding类就是根据这个BOM“文件头”
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace OnlineEditLib
{
public class TxtFileEncoding
{
public TxtFileEncoding()
{
//
// TODO: 在此处添加构造函数逻辑
//
}

///
/// 取得一个文本文件的编码方式。如果无法在文件头部找到有效的前导符,Encoding.Default将被返回。
///

///文件名。 ///
public static Encoding GetEncoding(string fileName)
{
return GetEncoding(fileName, Encoding.Default);
}

///
/// 取得一个文本文件流的编码方式。
///

///文本文件流。 ///
public static Encoding GetEncoding(FileStream stream)
{
return GetEncoding(stream, Encoding.Default);
}

///
/// 取得一个文本文件的编码方式。
///

///文件名。 ///默认编码方式。当该方法无法从文件的头部取得有效的前导符时,将返回该编码方式。 ///
public static Encoding GetEncoding(string fileName, Encoding defaultEncoding)
{
FileStream fs = new FileStream(fileName, FileMode.Open);
Encoding targetEncoding = GetEncoding(fs, defaultEncoding);
fs.Close();
return targetEncoding;
}

///
/// 取得一个文本文件流的编码方式。
///

///文本文件流。 ///默认编码方式。当该方法无法从文件的头部取得有效的前导符时,将返回该编码方式。 ///
public static Encoding GetEncoding(FileStream stream, Encoding defaultEncoding)
{
Encoding targetEncoding = defaultEncoding;
if (stream != null &amp;&amp; stream.Length &gt;= 2)
{
//保存文件流的前4个字节
byte byte1 = 0;
byte byte2 = 0;
byte byte3 = 0;
byte byte4 = 0;
//保存当前Seek位置
long origPos = stream.Seek(0, SeekOrigin.Begin);
stream.Seek(0, SeekOrigin.Begin);

int nByte = stream.ReadByte();
byte1 = Convert.ToByte(nByte);
byte2 = Convert.ToByte(stream.ReadByte());
if (stream.Length &gt;= 3)
{
byte3 = Convert.ToByte(stream.ReadByte());
}
if (stream.Length &gt;= 4)
{
byte4 = Convert.ToByte(stream.ReadByte());
}

//根据文件流的前4个字节判断Encoding
//Unicode {0xFF, 0xFE};
//BE-Unicode {0xFE, 0xFF};
//UTF8 = {0xEF, 0xBB, 0xBF};
if (byte1 == 0xFE &amp;&amp; byte2 == 0xFF)//UnicodeBe
{
targetEncoding = Encoding.BigEndianUnicode;
}
if (byte1 == 0xFF &amp;&amp; byte2 == 0xFE &amp;&amp; byte3 != 0xFF)//Unicode
{
targetEncoding = Encoding.Unicode;
}
if (byte1 == 0xEF &amp;&amp; byte2 == 0xBB &amp;&amp; byte3 == 0xBF)//UTF8
{
targetEncoding = Encoding.UTF8;
}

//恢复Seek位置
stream.Seek(origPos, SeekOrigin.Begin);
}
return targetEncoding;
}
}
}
来确定txt文件生成时用到的编码的。

[转载]VS2010程序打包操作(超详细的) - DaBan - 博客园

mikel阅读(1088)

[转载]VS2010程序打包操作(超详细的) – DaBan – 博客园.

1、  在vs2010 选择“新建项目”—-“其他项目类型”—-“Visual Studio Installerà“安装项目”:

命名为:Setup1 。

这是在VS2010中将有三个文件夹,

1.“应用程序文件夹”表示要安装的应用程序需要添加的文件;

2.“用户的‘程序’菜单”表示:应用程序安装完,用户的“开始菜单”中的显示的内容,一般在这个文件夹中,需要再创建一个文件用来存放:应用程序.exe和卸载程序.exe;

3.“用户桌面”表示:这个应用程序安装完,用户的桌面上的创建的.exe快捷方式。

 

2、  应用程序文件夹中点右键添加文件:表示添加要打包的文件;

添加的文件一般是已经编译过应用程序的Debug目录下的文件;

如果Debug 下面有子文件夹则需要“添加文件夹”,例如:data

然后把对应的子文件里的内容添加到此文件夹中;

把需要创建程序快捷方式的图标也添加进来:后缀名为:ico

3、   在创建的项目名称(Setup1)上点击右键:属性


选择系统必备

然后选择.NET的版本,和Windows Installer3.1(可选项)

选择:“从与我应用程序相同的位置下载系统必备组件”

这样安装包就会打包.NET FrameWork ,在安装时不会从网上下载.NET  FrameWork组件;但是安装包会比较大。

VS2010发布.NET2.0的版本,在创建安装程序时,需要设置启动条件:在项目名称(setup1)上,点击右键选择“视图”à“启动条件”:

然后VS2010会创建如下文件:

在“启动条件”中, 点击“.NET Framework”在Version上面选择.NET Framework 2.0;

这样.NET Framework 2.0上创建的项目在安装时,就不会安装.NET3.5或其他版本,

也不会重启(解决.NET 2.0 创建的项目,安装.NET FrameWork3.5的问题)。

 

4、  设置安装文件的目录(路径):

创建的项目名称(setup1)点击左键(不是右键),在属性中设置参数如下图:

 

其中Author 为作者;Manufacturer为公司名称;ProductName为应用程序的名字;

例如我们设置为:BeyondKKO;Manufacturer:自由公司;ProductName:串口测试。

把InstallAllUsers 设置为True 。

(这样在“控制面板”程序中会显示公司的名称;

在安装时会默认为“任何人”,否则默认为“只有我”)

在应用程序上点击左键,如下图:第一个为系统主目录(默认C:\Programe),第二个为公司名([Manufacturer]),第三个为应用程序名称,这样在安装时就会创建两层的文件路径。需要删除DefaultLocation中的:[Manufacturer] 。删除后只有应用程序的名称。

5、 创建应用程序图标与卸载程序:

A.“在应用程序文件夹”中的.exe文件中,点击右键,创建快捷方式:

 

重命名“串口测试程序”,然后点击右键属性:选择Icon

双击进入选择我们之前添加的“MiXer.ico”

 

确定后,拖动此快捷方式到“用户桌面”

B. “用户的‘程序’菜单”中添加一个文件夹,命名为:“串口程序”

然后同样的方式创建TEXTCOM.exe一个快捷方式(“串口测试程序”),拖动到“串口程序”中;

然后给.NET应用程序创建一个卸载程序:

在“应用程序文件夹”中添加:C:Windows\System32\Msiexec.exe

 

右键创建快捷方式,重命名为:卸载,把此快捷方式拖动到“串口程序”;


点击项目名称(Setup1),在属性中找到:ProductCode

复制此ProductCode ,粘贴到“卸载”快捷方式的Arguments属性,前面加/x空格

6、  完成以上步骤,就可以生成解决方案了。

7、生成解决方案后,在debug文件夹中就是我们需要的安装包.


8、  安装完在开始菜单中有“串口程序”的文件夹,里面有我们创建的两个快捷方式;


桌面上也有快捷方式。

(完)

经过自己的测试,发现只有把Debug文件夹整个都发给别的人,别人才能正常安装,否则就会出现错误。

[转载]Android自动化测试_开心一族_新浪博客

mikel阅读(1134)

[转载]Android自动化测试_开心一族_新浪博客.

Android自动化测试

——MonkeyMonkeyRunner

作者:王进勇

2012.8.29

一、自动化测试概述

通常,在设计了测试用例并通过评审之后,由测试人员根据测试用例中描述的规程步步执行测试,得到实际结果与期望结果的比较。在此过程中,为了节省人力、时 间或硬件资源,提高测试效率,便引入了自动化测试的概念。自动化测试就是把以人为驱动的测试行为转化为机器执行的一种过程。在Android测试中常用的测试包括:MonkeyMonkeyRunner以及robotium,本文中主要介绍下MonkeyMonkeyRunner自动化测试。

二、Monkey测试与MonkeyRunner测试比较

2.1Monkey

Monkey是一个命令行工具,可以运行在模拟器里或实际设备中,它是一种轻量级的性能测试工具。它向系统发送伪随机的事件流,实现对正在运行的应用程序进行压力测试。Monkey包括许多选项,它们大致分为四类:

1)基本配置选项,如设置尝试的事件数量。

2)运行约束选项,如设置只对单独的一个包进行测试

3)事件类型和频率

4)调试选项

Monkey运行的时候,它生成事件,并把它们发给系统。同时,Monkey还对测试中的系统进行监测,对下列三种情况进行特殊处理:

        1)如果限定了Monkey运行在一个或几个特定的包上,那么它会监测试图转到其它包的操作,并对其进行阻止。

2) 如果应用程序崩溃或接收到任何失控异常,Monkey将停止并报错。

3) 如果应用程序产生了应用程序不响应(application not responding)的错误,Monkey将会停止并报错。

按照选定的不同级别的反馈信息,在Monkey中还可以看到其执行过程报告和生成的事件。

2.2MonkeyRunner

MonkeyRunner工具提供了一个API,使用此API写出的程序可以在Android代码之外控制Android设备和模拟器。通过MonkeyRunner,您可以写出一个Python程序去安装一个Android应用程序或测试包,运行它,向它发送模拟击键,截取它的用户界面图片,并将截图存储于工作站上。MonkeyRunner工具的主要设计目的是用于测试功能/框架水平上的应用程序和设备,或用于运行单元测试套件,但您当然也可以将其用于其它目的。MonkeyRunner能够实现的功能包括:

1)多设备控制:monkeyrunner API可以跨多个设备或模拟器实施测试套件。您可以在同一时间接上所有的设备或一次启动全部模拟器(或统统一起),依据程序依次连接到每一个,然后运行一个或多个测试。您也可以用程序启动一个配置好的模拟器,运行一个或多个测试,然后关闭模拟器。

2)功能测试: monkeyrunner可以为一个应用自动贯彻一次功能测试。您提供按键或触摸事件的输入数值,然后观察输出结果的截屏。

3)回归测试:monkeyrunner可以运行某个应用,并将其结果截屏与既定已知正确的结果截屏相比较,以此测试应用的稳定性。

4)可扩展的自动化:由于monkeyrunner是一个API工具包,您可以基于Python模块和程序开发一整套系统,以此来控制Android设备。除了使用monkeyrunner API之外,您还可以使用标准的Python ossubprocess模块来调用Android Debug Bridge这样的Android工具。

2.3MonkeyMonkeyRunner的区别

Monkey

Monkey工具直接运行在设备或模拟器的adb shell中,生成用户或系统的伪随机事件流。

MonkeyRunner

MonkeyRunner工具则是在工作站上通过API定义的特定命令和事件控制设备或模拟器。

三、测试环境的搭建

见:

http://wenku.baidu.com/view/6f8d631e227916888486d75e.html

或者

http://blog.sina.com.cn/s/blog_a289fd65010155w5.html

四、MonkeyMonkeyRunner测试工具启动

4.1Monkey的启动

可以通过开发机器上的命令行或脚本来启动Monkey。由于Monkey运行在模拟器/设备环境中,所以必须用其环境中的shell来进行启动。可以通过在每条命令前加上adb shell来达到目的,也可以进入Shell后直接输入Monkey命令。基本语法如下:

 $ adb shell monkey [options]

如果不指定optionsMonkey将以无反馈模式启动,并把事件任意发送到安装在目标环境中的全部包。下面是一个更为典型的命令行示例,它启动指定的应用程序,并向其发送500个伪随机事件:

$ adb shell monkey -p your.package.name -v 500

启动Monkey步骤:

启动CMD输入adb shell所在位置(例如下:

E:\Program Files (x86)\Android\android-sdk-windows\platform-tools键入adb shell

如下图1所示,则启动成功。

Android自动化测试

1 启动adb工具

         启动后就可以输入Monkey命令,使其自动产生随机系列测试应用程序。

(注:命令可以直接输入,也可以通过编写脚运行,脚本文档可以是.text

4.2MonkeyRunner工具的启动

         MonkeyRunner自动化测试工具比Monkey测试更具有人的意志,目的性更加明确。启动MonkeyRunner方法如下:

CMD键入MonkeyRunner位置输入MonkeyRunner,如图2所示:

Android自动化测试

2 启动MonkeyRunner

(注:运行MonkeyRunner时可以在以上状态下输入指令,也可以通过monkeyrunner命令运行Python脚本,其后缀为.py

五、MonkeyMonkeyRunner命令介绍

5.1Monkey命令介绍

下表中列出了Monkey命令行可用的全部选项。

类别

 选项

 说明

 

常规

 1–help

 列出简单的用法。

 

2-v

 命令行的每一个-v将增加反馈信息的级别。Level 0(缺省值)除启动提示、测试完成和最终结果之外,提供较少信息。Level 1提供较为详细的测试信息,如逐个发送到Activity的事件。Level 2提供更加详细的设置信息,如测试中被选中的或未被选中的Activity

 

事件

3 -s <seed>

 伪随机数生成器的seed值。如果用相同的seed值再次运行Monkey,它将生成相同的事件序列。

 

4–throttle <milliseconds>

 在事件之间插入固定延迟。通过这个选项可以减缓Monkey的执行速度。如果不指定该选项,Monkey将不会被延迟,事件将尽可能快地被产成。

 

5–pct-touch <percent>

 调整触摸事件的百分比(触摸事件是一个down-up事件,它发生在屏幕上的某单一位置)

 

6–pct-motion <percent>

 调整动作事件的百分比(动作事件由屏幕上某处的一个down事件、一系列的伪随机事件和一个up事件组成)

 

7–pct-trackball <percent>

 调整轨迹事件的百分比(轨迹事件由一个或几个随机的移动组成,有时还伴随有点击)

 

8–pct-nav <percent>

 调整“基本”导航事件的百分比(导航事件由来自方向输入设备的up/down/left/right组成)

 

9–pct-majornav <percent>

 调整“主要”导航事件的百分比(这些导航事件通常引发图形界面中的动作,如:5-way键盘的中间按键、回退按键、菜单按键)

 

10–pct-syskeys <percent>

 调整“系统”按键事件的百分比(这些按键通常被保留,由系统使用,如HomeBackStart CallEnd Call及音量控制键)

 

11–pct-appswitch <percent>

 调整启动Activity的百分比。在随机间隔里,Monkey将执行一个startActivity()调用,作为最大程度覆盖包中全部Activity的一种方法。

 

12–pct-anyevent <percent>

 调整其它类型事件的百分比。它包罗了所有其它类型的事件,如:按键、其它不常用的设备按钮、等等。

 

约束限制

 13-p <allowed-package-name>

 如果用此参数指定了一个或几个包,Monkey将只允许系统启动这些包里的Activity。如果你的应用程序还需要访问其它包里的Activity(如选择取一个联系人),那些包也需要在此同时指定。如果不指定任何包,Monkey将允许系统启动全部包里的Activity。要指定多个包,需要使用多个 -p选项,每个-p选项只能用于一个包。

 

14-c <main-category>

 如果用此参数指定了一个或几个类别,Monkey将只允许系统启动被这些类别中的某个类别列出的Activity。如果不指定任何类别,Monkey将选 择下列类别中列出的ActivityIntent.CATEGORY_LAUNCHERIntent.CATEGORY_MONKEY。要指定多个类别,需要使用多个-c选项,每个-c 项只能用于一个类别。

 

调试

15 –dbg-no-events

 设置此选项,Monkey将执行初始启动,进入到一个测试Activity,然后不会再进一步生成事件。为了得到最佳结果,把它与-v、一个或几个包约 束、以及一个保持Monkey运行30秒或更长时间的非零值联合起来,从而提供一个环境,可以监视应用程序所调用的包之间的转换。

 

16–hprof

 设置此选项,将在Monkey事件序列之前和之后立即生成profiling报告。这将会在data/misc中生成大文件(~5Mb),所以要小心使用它。

 

17–ignore-crashes

 通常,当应用程序崩溃或发生任何失控异常时,Monkey将停止运行。如果设置此选项,Monkey将继续向系统发送事件,直到计数完成。

 

18–ignore-timeouts

 通常,当应用程序发生任何超时错误(如“Application Not Responding”对话框)时,Monkey将停止运行。如果设置此选项,Monkey将继续向系统发送事件,直到计数完成。

 

19–ignore-security-exceptions

 通常,当应用程序发生许可错误(如启动一个需要某些许可的Activity)时,Monkey将停止运行。如果设置了此选项,Monkey将继续向系统发送事件,直到计数完成。

 

20–kill-process-after-error

 通常,当Monkey由于一个错误而停止时,出错的应用程序将继续处于运行状态。当设置了此选项时,将会通知系统停止发生错误的进程。注意,正常的(成功的)结束,并没有停止启动的进程,设备只是在结束事件之后,简单地保持在最后的状态。

 

21–monitor-native-crashes

 监视并报告Android系统中本地代码的崩溃事件。如果设置了–kill-process-after-error,系统将停止运行。

 

22–wait-dbg

 停止执行中的Monkey,直到有调试器和它相连接

5.2MonkeyRunner方法介绍

MonkeyRunner帮助

 

1com.android.monkeyrunner.MonkeyRunner.alert

  当前脚本的运行过程中显示警告对话框。对话框是模态,因此脚本停止,直到用户关闭对话框。

  参数:

消息 消息显示在对话框。

    标题 对话框的标题。默认值是“提醒”。

    okTitle – 使用对话框中的按钮的文本。默认值是“OK”。

  返回:返回任何内容。

 

2com.android.monkeyrunner.MonkeyDevice.broadcastIntent

  发送一个广播设备的意图。

  参数:

    URI – 意图的URI

    行动 该行动的意图。

    数据 数据的URI意图

    mimetype – mime类型的意图。

    类别 迭代的意图类别名称。

    演员 一个额外的字典添加到意图。这些额外的类型推断的值的Python类型。

    组件 组件的意图。

    标志 一个迭代的标志为Intent.All参数是可选的。每个参数的默认值是null。(见android.content.Context.sendBroadcast(意向))

  返回:返回任何内容。

 

3com.android.monkeyrunner.MonkeyRunner.choice

  显示一个选择对话框,允许用户选择从一个单一的项目,一个项目清单。

  参数:

    消息 提示信息显示在对话框。

    选择 一个可迭代的Python类型的选择列表显示

    标题 对话框的标题。默认的是“输入”

  返回:所选项目的抵消了Iterable基于0的数字。

 

4com.android.monkeyrunner.MonkeyImage.convertToBytes

  转换成一个特定格式的MonkeyImage作为一个字符串返回结果。使用此获得rawpixels在一个特定的格式。字符串输出是有更好的表现。

  参数:

    格式 目标格式(例如,“PNG便携式网络图形格式)。默认为PNG

  返回:一个String生成的图像。

 

5com.android.monkeyrunner.MonkeyDevice.drag

  模拟设备屏幕上拖动(触摸,持有和移动)。

  参数:

    开始 拖动起点(以像素为单位元组(XY))

    结束 阻力终点(以像素为单位元组(XY

    时间 在几秒钟内拖时间(默认为1.0秒)

    步骤 插值点时要采取的步骤。 (默认是10

  返回:返回任何内容。

 

6com.android.monkeyrunner.MonkeyView.getAccessibilityIds

  返回当前视图的辅助IDS

  返回:视图的辅助IDS作为一个整数列表

 

7com.android.monkeyrunner.MonkeyRect.getCenter

  返回一个两个项目清单,其中包含矩形的中心的xy的值

  返回:该中心作为一个整数的两个项目清单坐标

 

8com.android.monkeyrunner.MonkeyView.getChecked

  获取视图的选中状态

  返回:一个布尔值,该项目是否被选中或不

 

9com.android.monkeyrunner.MonkeyView.getChildren

  返回当前视图的孩子们

  返回:认为儿童MonkeyView对象名单

 

10com.android.monkeyrunner.MonkeyView.getEnabled

  返回启用状态的看法。

  返回:作为一个布尔启用状态

 

11com.android.monkeyrunner.MonkeyView.getFocused

  视图返回集中状态

  返回:集中的状态作为一个布尔

 

12com.android.monkeyrunner.MonkeyRect.getHeight

  返回矩形的高度。

  返回:作为一个整数的矩形的高度

 

13com.android.monkeyrunner.MonkeyDevice.getHierarchyViewer

  获取设备HierarchyViewer对象。

  返回一个HierarchyViewer对象

 

14com.android.monkeyrunner.MonkeyView.getLocation

  返回视图的位置在一个MonkeyRect形式

  返回:视图作为MonkeyRect对象的位置

 

15com.android.monkeyrunner.MonkeyView.getParent

  返回当前视图的父

  返回:视图的父作为MonkeyView对象

 

16com.android.monkeyrunner.MonkeyDevice.getProperty

  由于设备上的变量的名称,返回变量的值

  参数:

关键 变量的名称。可用的名称在

http://developer.android.com/guide/topics/testing/monkeyrunner.html上市。

  返回:该变量的值

 

17com.android.monkeyrunner.MonkeyDevice.getPropertyList

  检索,可以查询的属性

  返回:返回任何内容。

 

18com.android.monkeyrunner.MonkeyImage.getRawPixel

  一个单一的ARGBalpha,红色,绿色,蓝色)像素位置的XY。参数XY0,表示在像素尺寸。 X增加的权利,对底部的Y增加。此方法返回一个tuple

  参数:

    X – x偏移的像素

    Y – 像素的y偏移量

  返回的元组(一RGB)的像素。 tuple中的每个项目都有0-255范围内。

 

19com.android.monkeyrunner.MonkeyImage.getRawPixelInt

  一个单一的ARGBalpha,红色,绿色,蓝色)像素位置的XY。参数XY0,表示在像素尺寸。 X增加的权利,对底部的Y增加。此方法返回一个整数。

  参数:

    X – x偏移的像素

    Y – 像素的y偏移量

  返回:XY的无符号整数像素。高阶8位是Afollowedby 8位为RG8 8B

 

20com.android.monkeyrunner.MonkeyDevice.getRootView

  获取当前根视图

  返回:根视图对象

 

21com.android.monkeyrunner.MonkeyView.getSelected

  返回视图的选中状态

  返回:作为一个布尔选中状态

 

22com.android.monkeyrunner.MonkeyImage.getSubImage

  复制图像的矩形区域。

  参数:

    矩形 一个元组(XYWH),说明该地区复制。 xy指定上左边角落的区域。 w是该地区的像素宽度,h是它的高度。

  返回:MonkeyImage对象代表复制的区域。

 

23com.android.monkeyrunner.MonkeyDevice.getSystemProperty

  代名词的getProperty()

  参数:

    关键 系统变量的名称。

  返回:该变量的值。

 

24com.android.monkeyrunner.MonkeyView.getText

  返回视图所载文本

  返回:在视图中的文本

 

25com.android.monkeyrunner.MonkeyDevice.getViewByAccessibilityIds

  获得指定的无障碍标识的看法。

  参数:

    windowId – 视图来检索窗口的ID

    无障碍标识 辅助视图来检索标识。

  返回:具有指定ID的视图对象。

 

26com.android.monkeyrunner.MonkeyDevice.getViewById

  取得具有指定id的观点。

  参数:

    ID – ID的视图来检索。

  返回:具有指定ID的视图对象。

 

27com.android.monkeyrunner.MonkeyView.getViewClass

  返回视图类的名称。

  返回:类名作为一个字符串的看法

 

28com.android.monkeyrunner.MonkeyDevice.getViewIdList

  检索当前应用程序的视图ID

  返回:返回任何内容。

 

29com.android.monkeyrunner.MonkeyDevice.getViewsByText

  获得一个列表包含指定文本的意见。

  参数:

    文本 文本搜索

  返回:包含指定文本的视图对象的名单。

 

30com.android.monkeyrunner.MonkeyRect.getWidth

  返回矩形的宽度。

  返回:作为一个整数的矩形的宽度

 

31com.android.monkeyrunner.MonkeyRunner.help

  格式和API参考显示为MonkeyRunner的。

  参数:

    格式 输出所需的格式,无论是“文本”为纯文本或HTML标记的HTML“。

  返回:一个字符串,其中包含所需格式的帮助文本。

 

32com.android.monkeyrunner.MonkeyRunner.input

  显示一个对话框,接受输入。对话框,模态,因此脚本停止,直到用户点击两个对话框按钮之一。输入一个值,用户输入的值,并单击“确定”按钮。要退出,而无需输入一个值对话框,用户点击“取消”按钮。使用这种方法提供的参数来定制这些按钮的文本。

  参数:

    消息 提示信息显示在对话框。

    初值 提供给用户的初始值。默认是一个空字符串)

    标题 对话框的标题。默认的是“输入”

    okTitle – 文本中使用对话框的确认按钮。默认的是“OK”。文本中使用对话框的“取消”按钮。默认是“取消”。

    cancelTitle –

  返回:测试用户,或没有输入,如果用户取消了输入;

 

33com.android.monkeyrunner.MonkeyDevice.installPackage

  设备安装到指定的Android包(。apk文件)。如果设备上已经存在的包,它会被替换。

  参数:

    路径 包的主机上的文件系统路径和文件名。

  返回:如果安装成功

 

34com.android.monkeyrunner.MonkeyDevice.instrument

  与仪器仪表运行指定的包,并返回它产生的输出。使用此运行测试包使用InstrumentationTestRunner

  参数:

    className – 类运行仪表。格式是软件包名/类名。使用软件包名来指定运行,Android包和类名来指定运行该包内的类。对于测试包,这是通常35testpackagename / InstrumentationTestRunner

    参数 包含参数传递给这个仪器(默认值是“无”)的对象映射到一个字符串。

  返回:一个字符串映射到从包中输出的对象。对于一个测试包,包含一个单一的键 值对:关键是的值是一个字符串,其中包含测试输出。

 

35com.android.monkeyrunner.MonkeyRunner.loadImageFromFile

  加载文件MonkeyImage

  参数:

    路径 要加载的文件的路径。该文件的路径是在计算机上运行MonkeyRunner,而不是在Android设备上的路径。

  返回值:一个的新MonkeyImage代表指定的文件

 

36com.android.monkeyrunner.MonkeyDevice.press

  发送到指定键的一个关键事件

  参数:

    名称 键的键码按(见android.view.KeyEvent

    如由TouchPressType()返回的类型 触摸事件类型。为了模拟输入键,发送DOWN_AND_UP

  返回:返回任何内容。

 

37com.android.monkeyrunner.MonkeyDevice.reboot

  重新引导到指定的引导程序指定的设备。

  参数:

    重启进入引导程序:引导程序,恢复,或没有

  返回:返回任何内容。

 

38com.android.monkeyrunner.MonkeyDevice.removePackage

  从设备中删除指定的软件包,包括其相关的数据和高速缓存。

  参数:

    包的名字删除。

  返回:True如果删除成功

 

39com.android.monkeyrunner.MonkeyImage.sameAs

  比较这MonkeyImage的对象aother MonkeyImage对象。

  参数:

    其他 其他MonkeyImage的对象。

    一个在0.01.0的范围内浮动,说明需要是相同的方法,返回“真”的像素的百分比。默认为1.0

  返回:布尔,如果两个对象包含相同的图像。

 

40com.android.monkeyrunner.MonkeyView.setFocused

  设置视图的集中状态

  参数:

    重点 布尔值设置集中到

  返回:返回任何内容。

 

41com.android.monkeyrunner.MonkeyView.setSelected

  设置视图的选定状态

  参数:

    选择 布尔值设置选择

  返回:返回任何内容。

 

42com.android.monkeyrunner.MonkeyDevice.shell

  执行一个adb shell命令,并返回结果,如果有的话。

  参数:

    CMD – adb shell命令来执行。

  返回:从命令的输出。

 

43com.android.monkeyrunner.MonkeyRunner.sleep

  暂停目前正在运行的程序指定的秒数。

  参数:

    暂停的秒数。

  返回:返回任何内容。

 

44com.android.monkeyrunner.MonkeyDevice.startActivity

  开始从指定的参数构造一个Intent发送设备上的活动。

  参数:

    URI – 意图的URI

    行动 该行动的意图。

    数据 数据的URI意图

    mimetype – mime类型的意图。

    类别 一个Python迭代包含的意图类别名称。

    演员 一个额外的字典添加到意图。这些额外的类型推断的值的Python类型。

    组件 组件的意图。

    标志 一个迭代的标志为Intent.All参数是可选的。为每个参数的默认值是null。(见android.content.Intent

  返回:返回任何内容。

 

45com.android.monkeyrunner.MonkeyDevice.takeSnapshot

  获取设备的屏幕缓冲区,产生了整个显示器的屏幕捕获。

  返回:一个MonkeyImage对象(点阵图包装)

 

46com.android.monkeyrunner.MonkeyDevice.touch

  在指定位置发送触摸事件

  参数:

    X – X坐标,以像素为单位

    Y – Y坐标,以像素为单位

    类型 触摸事件类型TouchPressType(返回)

  返回:返回任何内容。

 

47com.android.monkeyrunner.MonkeyDevice.type

  键盘上的类型指定的字符串。这相当于要求每个字符串中的字符按(键码,DOWN_AND_UP)。

  参数:

    消息 字符串发送到键盘。

  返回:返回任何内容。

 

48com.android.monkeyrunner.MonkeyRunner.waitForConnection

  等待工作站连接到设备。

  参数:

    超时 在几秒钟等待超时。默认是无限期地等待。

    deviceid – 一个正则表达式指定的设备名称。看到开发指南,了解有关设备名称为“亚行”的文件。

  返回:一个ChimpDevice对象代表连接的设备。

 

49com.android.monkeyrunner.MonkeyDevice.wake

  在设备屏幕上唤醒

  返回:返回任何内容。

 

 

50com.android.monkeyrunner.MonkeyImage.writeToFile

  写文件MonkeyImage。如果没有指定格式,这种方法猜测输出格式的基础上提供的文件扩展名的扩展。如果是无法猜测的格式,它使用的PNG

  参数:

    路径 输出文件名,也可以包括其路径

    格式 目标格式(例如,“PNG便携式网络图形格式。

  返回:Boolean如果写成功。

六、MonkeyRunner测试程序的编写示例

         MonkeyRunner程序是用Python语言写的,但是由于在此主要目的在于介绍自动化测试,所以不专门介绍Python语言,大家可以通过专门的Python教程进行学习。(辅导教程可用:《Python基础教程(2)》或者《Python3程序开发指南(第二版)》)。

6.1MonkeyRunner坐标记录程序的编写

         常用的坐标测试程序一般都相同,只要做简单修改就可使用,示例如下:

#This Python file uses the following encoding: utf-8

#!/usr/bin/env monkeyrunner

# Copyright 2010, The Android Open Source Project

#

# Licensed under the Apache License, Version 2.0 (the “License”);

# you may not use this file except in compliance with the License.

# You may obtain a copy of the License at

#

# http://www.apache.org/licenses/LICENSE-2.0

#

# Unless required by applicable law or agreed to in writing, software

# distributed under the License is distributed on an “AS IS” BASIS,

# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

# See the License for the specific language governing permissions and

# limitations under the License.

 

from com.android.monkeyrunner import MonkeyRunner as mr

from com.android.monkeyrunner.recorder import MonkeyRecorder as recorder

 

#连接设备

device = mr.waitForConnection()

 

mr.sleep(10)

 

#安装应用程序

#device.installPackage(“安装包路径+名字“)

 

#启动Android应用程序

#device.startActivity(component=”程序启动的首个Activity“)

 

#启动录制程序

recorder.start(device)

6.2MonnkeyRunner测试程序的编写

         运用5.2中提供的命令,就可以编写应用程序,示例程序如下:

# 导入此程序所需的monkeyrunner模块

from com.android.monkeyrunner import MonkeyRunner MonkeyDevice, MonkeyImage

# 连接当前设备,返回一个MonkeyDevice对象

device= MonkeyRunner.waitForConnection()

# 安装Android 包,注意,此方法返回的返回值为boolean ,由此您可以判

#断安装过程是否正常

device.installPackage 安装路径

# 运行此应用中的一个活动

device.startActivity component=’com.example.android.myapplication.MainActivity’

# 按下菜单按键

device.press‘KEYCODE_MENU’‘DOWN_AND_UP’

# 截取屏幕截图

result = device.takeSnapShot

#将截图保存至文件

result.writeToFile‘myproject/shot1.png’ ‘png’

[转载]android基础知识12:android自动化测试01——MONKEY - xianming01的专栏——android菜鸟 - 博客频道 - CSDN.NET

mikel阅读(999)

[转载]android基础知识12:android自动化测试01——MONKEY – xianming01的专栏——android菜鸟 – 博客频道 – CSDN.NET.

本周开始启动手机输入法simeiji的自动化测试,同时开始接触手机浏览器自动化测试。接下来会对Android自动化测试工具和方法做一个专题研究。

第一篇介绍monkey测试工具。

 

1 自动化测试背景 
软件测试是软件产品开发过程中的重要环节。测试对于发现软件中缺陷,保障软件的质量具有重要意义。统计表明,软件测试的时间通常占到软件开发时间的 40%,一些可靠性要求高的软件甚至高达 60%以上。
手工测试和自动化测试是软件测试的两种基本手段,二者相互补充,不可相互替代。其中,自动化测试能捕捉到资源冲突、多线程、死锁等手工测试难以发现的错 误,可以高效的完成大量机械性、重复性测试,能模拟大量数据或大量并发用户等应用场景,从而有效的缩短软件测试周期,提高测试的可靠性及准确度。

 

2 什么是 Monkey 
Monkey 测试是 Android 自动化测试的手段之一,它通过模拟用户的按键输入、触摸屏输入等,测试设备多长时间出现异常。Monkey 是一个命令行工具,可以运行在模拟器或实际设备中,通过向系统发送伪随机的用户事件流,实现对全系统或某个应用程序进行压力测试。
Monkey 工具使用非常简单,直接在命令窗口键入 monkey 命令即可进行测试。Monkey 命令有较多的配置选项,为便于理解这些命令,此处先将其命令选项简单划分为四大类,在后文中陆续介绍各种命令选项的使用,然后在附录中附上整个 monkey 命令选项便于查阅。
Monkey 命令可简单分为如下四类:

  • 基本配置选项,如设置尝试的事件数量
  • 运行约束选项,如设置只对单独的一个包进行测试
  • 事件类型和频率,如设置触摸屏事件的发生频率
  • 调试选项,如设置忽略应用程序异常,继续向系统发送事件,直到计数完成
3 使用 monkey 进行自动化测试 
Monkey 命令工具可以在模拟器和实际设备上使用。这里分别对两种环境下的 monkey 测试进行介绍。
3.1 在模拟器上运用 Monkey 进行自动化测试
3.1.1 monkey 命令最简单用

首先,介绍最简单 monkey 命令用法,其使用格式为如下:
monkey  n
其中,n 为一个整数,表示用于测试的伪随机事件发生的次数。
如图 3.1 所示,启动模拟器。然后,在命令行窗口下输入 adb shell 命令,如图 3.2 所示。
登录 shell 之后,即可使用 monkey 命令。最简单的用法是,直接在其后面指定一个数字,表
示向系统发送事件的数量,如图 3.3 所示。模拟器的测试的结果如图 3.4 所示。
3.1.2 指定测试的约束选项 
所谓测试约束选项,即根据具体的测试需求,通过参数指定对系统的某些包或类进行测试。主要包括两类约束:包和类。
(1) 指定测试的包
如果需要对某一个具体的应用进行测试,可指定具体的包名。可以在系统目录/data/data
下查找对应的包名,如图 3.5 所示。
查找到对应的包名后,使用 monkey 时可用“-p”参数进行指定,此外还可以使用“-v”
参数指定测试中反馈的信息。常用格式如下:
monkey  –p  com.android.gallery  –v  50
如图 3.6 和 3.7 分别为命令输入及测试过程的截图。
注意:每个“-p”只能指定一个包,若需指定对多个包进行测试,则要用多个“-p”参数进行指定。
(2) 指定测试的类
若需对某个具体类进行测试,可以使用“-c”参数指定,若需指定多个类别,则要用多个“-c”参数选项分别指定,每个“-c”只能指定一个类别。如图 3.1 所示,其使用的命令格式
为:
Monkey  –c  android.intent.category.LAUNCHER  -v   50
图 3.8 及图 3.9 分别为指定类测试命令输入及测试过程的截图。
3.1.3 指定测试的事件类型和频率
Monkey 命令提供了众多选项用于指定事件的类型和发生频率, 例如触摸事件、 导航事件、轨迹事件(由一个或多个随机的移动组成,有时还伴随有点击)。有时为了测试某个具体应用,需要通过“事件”的反复发生来进行专项的功能测试,此 时即可通过 monkey 的“—pct”选项来进行指定具体事件及其发生的频率。命令使用格式如下示例:
Monkey –pct-syskeys  90  200
以上命令表示系统按键事件的发生率为 90%,共测试 200 次,如图 3.10 及图 3.11 所示。
注意: “pct”前面是英文输入状态下两杠“——” 。
还可以分别指定两个事件的发生频率,命令使用方式如下所示:
Monkey  –pct-touch 50  –pct-trackball  40  200
以上命令表示触摸事件和轨迹事件发生概率分别为 50%和 40%,共测试 200 次,如图 3.12 和图 3.13 所示。
        通过“–throttle”指定事件间的间隔,可以延缓事件的发生,如下图 3.14 所示为指定导航事件发生概率为 80%, 每次事件相隔 1 秒中发生, 共测试 200 次。 测试的效果图如 3.15 所示。
3.1.4 指定测试的调试选项 
Monkey 命令提供了若干用于控制测试过程的调试选项。例如参数“–ignore-crashes” ,将忽略应用程序崩溃或发生的任何异常,继续向系统发送事件,直到计数完成。使用命令格式如下:
monkey  –ignore-crashes  -v 5000
图 3.16 及图 3.17 分别显示了指定调试选项的测试及其测试过程的效果图。
3.2 在开发板上运用 Monkey 进行自动化测试
上一节中主要介绍了用 monkey 命令工具在模拟器上进行测试, 列举了若干基本的命令使用示例。在实际的设备中,也可以方便的运用 monkey 工具进行应用程序的测试。
要在实际设备上进行 monkey 测试,首先需要启动 Android 系统,然后让设备通过串口与pc 相连,在 pc 上通过 SecureCRT 登录到 Android 系统的根目录下。此时,即可运用上一节介绍的各种命令对设备进行自动化测试。
注意, 登录到 Android 系统的根目录后, 若找不到 monkey 命令, 应先运行 system/bin/sh,切换 bash 环境。
4 附录
4.1 monkey 工具的命令选项

usage: monkey [-p ALLOWED_PACKAGE [-p ALLOWED_PACKAGE] …]
[-c MAIN_CATEGORY [-c MAIN_CATEGORY] …]
[–ignore-crashes] [–ignore-timeouts]
[–ignore-security-exceptions]
[–monitor-native-crashes] [–ignore-native-crashes]
[–kill-process-after-error] [–hprof]
[–pct-touch PERCENT] [–pct-motion PERCENT]
[–pct-trackball PERCENT] [–pct-syskeys PERCENT]
[–pct-nav PERCENT] [–pct-majornav PERCENT]
[–pct-appswitch PERCENT] [–pct-flip PERCENT]
[–pct-anyevent PERCENT]
[–pkg-blacklist-file PACKAGE_BLACKLIST_FILE]
[–pkg-whitelist-file PACKAGE_WHITELIST_FILE]
[–wait-dbg] [–dbg-no-events]
[–setup scriptfile] [-f scriptfile [-f scriptfile] …]
[–port port]
[-s SEED] [-v [-v] …]
[–throttle MILLISEC] [–randomize-throttle]
[–profile-wait MILLISEC]
[–device-sleep-time MILLISEC]
[–randomize-script]
[–script-log]
[–bugreport]
COUNT

4.2 monkey 命令选项介绍 
参考资料:

Android Monkey使