华为手机来电显示未知为什么显示EMSS

列表网公众号列表活动随时有扫我活动不错过
下次自动登录(公共场合慎用)
使用合作网站账号登录:
收藏成功!
您可在个人中心,查看
类&&&&别:
起订数量:
商家地址:
中国 湖北 武汉
湖北省武汉市洪山区广
查看联系方式
查看联系方式
联系我时说明在列表网看到,说不定有意外惊喜哟!
微信扫一扫,快速拨打电话
类型:网络摄像机
1/3& Exwave PRO逐行扫描 CCD/130万像素/DEPA/JPEG/MPEG4/超大视角100.8&/双向音频/智能语音报警/电子PTZ,图像裁剪功能/模拟视频输出(600线 高清晰)/PoE全网管,48个10/100/1000M千兆铜缆端口 ,4个mini-GBIC(共享),支持SNMP和WEB管理公司介绍&&& 欢迎您访问武汉英莱德科技有限公司,我公司是一家集软件开发、网络产品销 售、安防产品销售,安防工程与系统集成、网络设备安装调试、网络设备维护租赁,IT服务于一体的专业化网络公司。主要经营产品有:H3C 华为 思科 中兴 博达等网络设备;MOXA工业级交换机;深圳源拓光纤收发器,视频光端机;腾龙 图腾机柜;VIKOR SONY 景阳安防监控产品产品;宝利通视频会议系统 海盟视频会议系统 电话会议 多媒体系统及网络系统集成、综合布线、安防监控、智能楼宇等弱电工程。武汉英莱德科技有限公司==============================地址:湖北省武汉市洪山区广八路银海雅苑D-2006室-----------------------------联系电话:027-传 真:027-124小时客服热线:24小时客服热线:------------------------------联系人:龚先生 杨小姐 杨先生E-mail:QQ:网址:www.whyldkj.com详细信息主营产品或服务:源拓光纤收发器;源拓视频光端机;源拓收发器机架;光端机机架;思科网络产品;华为网络产品;中兴网络产品;博达网络产品;安防监控产品;综合布线;网络集成;机房环境监控;KVM切换器(交换机 路由器 网线等);网络机柜服务器机柜;光纤接续盒/ODF光纤配线架;光纤终端盒/熔接盒/光纤法兰/藕合器;光纤/光缆,单模/多模光纤跳线;H3C DLINK;PDU机房设备;餐饮点菜收银系统;主营行业:网络机柜;网络工程;防火墙;光纤设备;交换机;监控摄像机经营模式:经销批发企业类型:有限责任公司公司注册地:中国 湖北 武汉主要经营地点:湖北省武汉市洪山区银海雅苑D2006室公司成立时间:2009法定代表人/负责人:杨宁年营业额:人民币 200 万元/年 - 300 万元/年员工人数:11 - 50 人经营品牌:英莱德注册资本:人民币 100万主要客户群:中小型企业 网吧 制造型企业 广电 电信主要市场:大陆、年出口额:人民币 10 万元/年以下年进口额:人民币 10 万元/年以下开户银行:&帐号:&是否提供加工/定制服务?&研发部门人数:&月产量:50W 台厂房面积:120 平方米质量控制:第三方管理体系认证:ISO 9001;联系方式武汉英莱德科技有限公司杨宁 女士 (总经理)地  址:中国 湖北 武汉市洪山区 湖北省武汉市洪山区广八路银海雅苑D-2006室邮  编:&传  真:86 027
&免费试用电子传真移动电话:电  话:86 027 公司主页:http://www.whyldkj.com/&http://yldkjwh.cn.alibaba.com全网管,48个10/100/1000M千兆铜缆端口 ,4个mini-GBIC(共享),支持SNMP和WEB管理公司介绍&&& 欢迎您访问武汉英莱德科技有限公司,我公司是一家集软件开发、网络产品销 售、安防产品销售,安防工程与系统集成、网络设备安装调试、网络设备维护租赁,IT服务于一体的专业化网络公司。主要经营产品有:H3C 华为 思科 中兴 博达等网络设备;MOXA工业级交换机;深圳源拓光纤收发器,视频光端机;腾龙 图腾机柜;VIKOR SONY 景阳安防监控产品产品;宝利通视频会议系统 海盟视频会议系统 电话会议 多媒体系统及网络系统集成、综合布线、安防监控、智能楼宇等弱电工程。武汉英莱德科技有限公司==============================地址:湖北省武汉市洪山区广八路银海雅苑D-2006室-----------------------------联系电话:027-传 真:027-124小时客服热线:24小时客服热线:------------------------------联系人:龚先生 杨小姐 杨先生E-mail:QQ:网址:www.whyldkj.com详细信息主营产品或服务:源拓光纤收发器;源拓视频光端机;源拓收发器机架;光端机机架;思科网络产品;华为网络产品;中兴网络产品;博达网络产品;安防监控产品;综合布线;网络集成;机房环境监控;KVM切换器(交换机 路由器 网线等);网络机柜服务器机柜;光纤接续盒/ODF光纤配线架;光纤终端盒/熔接盒/光纤法兰/藕合器;光纤/光缆,单模/多模光纤跳线;H3C DLINK;PDU机房设备;餐饮点菜收银系统;主营行业:网络机柜;网络工程;防火墙;光纤设备;交换机;监控摄像机经营模式:经销批发企业类型:有限责任公司公司注册地:中国 湖北 武汉主要经营地点:湖北省武汉市洪山区银海雅苑D2006室公司成立时间:2009法定代表人/负责人:杨宁年营业额:人民币 200 万元/年 - 300 万元/年员工人数:11 - 50 人经营品牌:英莱德注册资本:人民币 100万主要客户群:中小型企业 网吧 制造型企业 广电 电信主要市场:大陆、年出口额:人民币 10 万元/年以下年进口额:人民币 10 万元/年以下开户银行:&帐号:&是否提供加工/定制服务?&研发部门人数:&月产量:50W 台厂房面积:120 平方米质量控制:第三方管理体系认证:ISO 9001;联系方式武汉英莱德科技有限公司杨宁 女士 (总经理)地  址:中国 湖北 武汉市洪山区 湖北省武汉市洪山区广八路银海雅苑D-2006室邮  编:&传  真:86 027
&免费试用电子传真移动电话:电  话:86 027 公司主页:http://www.whyldkj.com/&http://yldkjwh.cn.alibaba.com
联系我时,请说是在列表网栏目上看到的,谢谢!
小贴士:酒店监控系统设备,酒店监控系统服务商信息由列表网网友发布,其真实性及合法性由发布人负责。列表网仅引用以供用户参考。详情请阅读列表网免责条款。
请在查看您收到的留言
手机号码:
获取确认码
已发送(59)
所在城市:
留言内容:
快捷留言:
想了解一下详情,请尽快联系我。
我对您的商品感兴趣,请尽快和我联系。
酒店监控系统设备,酒店监控系统服务商 相关广告
金牌会员推荐
&2017 列表网&琼ICP备号-12&增值电信业务经营许可证B2-&24小时热门版块排行榜&&&&
【有奖交流】积极回复本帖子,参与交流,就有机会分得作者 bookbook 的 65 个金币
(著名写手)
在线: 60小时
虫号: 67123
有愿意研究蜘蛛的么,西南大学生科院张志升课题组欢迎调剂(68楼有更新信息)
很多人不喜欢蜘蛛,但即使是低概率事件,还是会有人喜欢,欢迎喜欢做有关蜘蛛研究的加入我们的课题组。
要求和联系方式请参见
非诚勿扰!
请自认为符合条件的同学将你们的简历发到,谢谢
[ Last edited by bookbook on
at 10:15 ]
2015年继续接收调剂生,请看清我的要求,如果你觉得你可以,请发信到我的邮箱。
[ Last edited by bookbook on
at 11:23 ]
& 本帖已获得的红花(最新10朵)
& 猜你喜欢
已经有18人回复
已经有330人回复
已经有6人回复
已经有140人回复
已经有85人回复
已经有24人回复
已经有3人回复
已经有54人回复
已经有9人回复
已经有13人回复
& 本主题相关商家推荐:
& 抢金币啦!回帖就可以得到:
(著名写手)
在线: 92.4小时
虫号: 279291
★ bookbook: 金币+1, 没什么恐怖的,别听科普节目胡说八道
研究黑寡妇之类的?有点恐怖啊!祝楼主好运,找到志同道合的同志吧!呵呵
(初入文坛)
在线: 10.1小时
虫号: 1517750
★ bookbook: 金币+1, 哇,财政学呀,好像差距太大了点
老师,您好!
& && &我本科学经济的,研究生报考湖南大学财政学,总分359(英语71、政治70、数学三102、经济学综合116),但自小对生物、化学具有浓厚的兴趣,请问有希望加入贵团队吗?
(初入文坛)
在线: 27小时
虫号: 2920521
★ bookbook: 金币+1, 英语偏低,专业课极高
报考单位:北京理工大学
报考专业:生物学
英语一:44
生物化学:117
微生物学:117
联系方式:
(初入文坛)
在线: 59分钟
虫号: 1297061
★ 送红花一朵bookbook: 金币+1, 谢谢!我却不知道你是哪位高人呢
帮忙顶一下,张老师是个很好的老师,欢迎大家来咨询讨论~
(小有名气)
在线: 79.6小时
虫号: 1699060
★ bookbook: 金币+1, 谢谢关注
大自然是神奇的,每一种生命都有无穷的奥秘,蜘蛛也一样,我觉得非常好,蜘蛛丝也许就是高分子研究的另一个方向呢
(著名写手)
在线: 60小时
虫号: 67123
今年的报名应该结束了吧,虽然我还没看到全部名单。不过这两年的感觉,真正感兴趣的人确实是少之又少。确实让我的信心少了不少。自己顶一下吧!
(文学泰斗)
在线: 4265.2小时
虫号: 119626
thank you for sharing ~~~~~~~~~~~~~~~~~~
(初入文坛)
在线: 12.6小时
虫号: 2985127
现在住宿舍滴说,女生胆子都不大,连乌龟都怕,肿么能养蜘蛛┐( ̄▽ ̄”)┌ 等考上研看看女生胆子会不会大一点啦。
(文学泰斗)
在线: 8957.5小时
虫号: 814608
★ bookbook: 金币+1, 谢谢
祝福早日找到科研人才
(初入文坛)
在线: 29.4小时
虫号: 2996389
★ bookbook: 金币+1, 好
你好,我加了您qq,希望把详细资料发给您
(初入文坛)
在线: 8小时
虫号: 1889431
★ bookbook: 金币+1, 谢谢支持
课题很喜欢,悲催的273分不够啊,哎!
(小有名气)
在线: 93.3小时
虫号: 2682081
你好,我是山东农业大学的学生考研报的预防兽医,本科学的动物检疫,和动物医学学的科目差不多,应届本科,已过六级,接受调剂么?
(著名写手)
在线: 60小时
虫号: 67123
自己顶一下,因为今年继续接收调剂生。
(著名写手)
在线: 60小时
虫号: 67123
有愿意推免到我这里来的,请和我直接联系!
(小有名气)
在线: 23.6小时
虫号: 2818046
我喜欢蜘蛛,接受跨专业调剂吗
(著名写手)
在线: 176.4小时
虫号: 833797
★ bookbook: 金币+1, 谢谢支持
帮顶一下,祝蓬勃发展。
(小有名气)
在线: 26.7小时
虫号: 1979318
本科生物工程,考的江南大学食品工程专硕,分数295有希望吗
(文坛精英)
在线: 818.3小时
虫号: 2210253
★ bookbook: 金币+1, 谢谢
祝楼主好运,找到志同道合的学子!
(文坛精英)
在线: 129.4小时
虫号: 2424106
(初入文坛)
在线: 12.6小时
虫号: 2985127
★ bookbook: 金币+1, 吓不到人,只要不吓到你自己就没问题了
呀!我喜欢呐~红玫瑰橙巴布都很可爱~想养蜘蛛啊,可是怕吓到人呢
(小有名气)
在线: 29.3小时
虫号: 2467827
★ bookbook: 金币+1, 如果你考的是植保专业,可能有点麻烦,因为植保属于农学一级学科,我这里是生物学
植保专业可以吗?
[ 发自小木虫客户端 ]
(文坛精英)
在线: 426.1小时
虫号: 1173841
★ bookbook: 金币+1, 谢谢
帮顶,加油,blessing!!!!!!!!
(文坛精英)
在线: 426.1小时
虫号: 1173841
帮顶,加油,blessing!!!!!!!!
(文坛精英)
在线: 2576.4小时
虫号: 752823
★ bookbook: 金币+1, 谢谢
祝福楼主,心想事成!
(小有名气)
在线: 29.3小时
虫号: 2467827
引用回帖:: Originally posted by GQ一杯清茶 at
植保专业可以吗? 好的,谢谢!
[ 发自小木虫客户端 ]
(初入文坛)
在线: 58.9小时
虫号: 1075575
(初入文坛)
在线: 13.8小时
虫号: 1293149
顶起,加油!
(小有名气)
在线: 56.8小时
虫号: 1248633
顶啊顶啊顶啊顶
(初入文坛)
在线: 11.1小时
虫号: 2705341
★ bookbook: 金币+1, 有点困难,英语太低
老师好,我今年考川大生科院,304分。英语49,可以t调剂过去么?
(文学泰斗)
在线: 4265.2小时
虫号: 119626
★ bookbook: 金币+1, 谢谢
帮顶,blessing, blessing, blessing ! ~~~
(文学泰斗)
在线: 33480.9小时
虫号: 827383
★ bookbook: 金币+1, 谢谢
很特别的研究,帮顶一下
(初入文坛)
在线: 29.4小时
虫号: 2996389
★ bookbook: 金币+1, 发详细材料来看看吧
报考厦门大学细胞生物学317分、英语62分、本科生物科学专业、曾多次前往烟台养马岛、崑崳山等地实习考察、并在校成绩较为优异、动手能力强、请问有希望加入贵团队吗
(初入文坛)
在线: 3.4小时
虫号: 2993749
★ bookbook: 金币+1, 这是跨学科,有点问题
本人本科生物技术,报考的是农学专业,您看行吗?
(著名写手)
在线: 220.6小时
虫号: 2473376
★ bookbook: 金币+1, 谢谢支持
顶起,360行行行出状元,有些事总要有人去做
(正式写手)
在线: 31小时
虫号: 2907277
★ bookbook: 金币+1, 不可以
我很喜欢这些小动物,本科专业是生物科学,考研报考专业为课程与教学论不知道可吗?
(小有名气)
在线: 60.3小时
虫号: 2145355
★ bookbook: 金币+1, 谢谢
帮忙顶一下~~祝楼主找到适合的人。
(小有名气)
在线: 23小时
虫号: 2977103
★ bookbook: 金币+1, 跨学科,可能性很小
考大连理工有机化学及youji化学实验的324,可以研究蜘蛛吗
(小有名气)
★ bookbook: 金币+1, 谢谢关注
本帖内容被屏蔽
(小有名气)
在线: 50.2小时
虫号: 1476080
★ bookbook: 金币+1, 谢谢关注
我报的北师大,动物专业,想调剂,请加我qq
(小有名气)
在线: 48.9小时
虫号: 1982394
请问楼主主要研究蜘蛛的什么方向
(著名写手)
在线: 60小时
虫号: 67123
引用回帖:: Originally posted by bansainan at
请问楼主主要研究蜘蛛的什么方向 介绍里面有
相关版块跳转
English Cafe
招聘信息布告栏
公务员考试
我要订阅楼主
的主题更新
小木虫,学术科研互动社区,为中国学术科研免费提供动力
违规贴举报删除请联系客服电话: 邮箱:(全天候) 或者 QQ:
广告投放与宣传请联系 李想 QQ:
QQ:&&邮箱:
Copyright &
MuChong.com, All Rights Reserved. 小木虫 版权所有{{item.name}}
{{subItem.name}}
开发工具和文档
其它订制文档
应用自启动配置说明
开发第一个 EMSS Web 应用
欢迎开发 EMSS Web 应用。
开发 EMSS Web 应用程序需要使用 HTML5,CSS 和 JavaScript 等 Web 语言。
本章接下来将介绍 EMSS Web 应用程序的开发过程,以及使用 EMSS Studio 将创建的应用程序安装在手机设备上。通过阅读说明,您可以创建并运行基本的 EMSS Web 应用程序。
在开始开发 EMSS Web 应用程序之前,请先下载并安装 EMSS Studio。
双击 .exe 应用安装文件,选择安装路径后,一直选择“Next”,直到最后一步单击“Finish”完成安装。
有关安装过程的更多信息,请参见《》。
使用 EMSS Studio 创建 Web 项目。
该步骤介绍如何使用预先设计的项目模板来创建项目所需的所有基本文件和文件夹。
构建应用程序。
在您实现了所需功能的代码之后,此步骤将显示如何构建应用程序来验证和编译代码。
运行应用程序。
此步骤显示如何在目标设备上运行应用程序,并实现身份证信息识别。
使用模板创建一个应用项目
EMSS Studio 创建项目有两种情况,一种是将已有项目导入开发,一种是新建项目开发。本例说明如何在 EMSS Studio 中创建和配置基本的 Web 应用程序。
在菜单栏中单击“File > New > EMSS Application”,弹出应用创建向导。
图1&nbsp打开应用创建向导
在“EMSS Application”向导页左侧 Project 页签中打开 Base 样例模板库。
图2&nbsp选择样例模板
在 Base 中选择 HelloWorld 样例模板,修改下方“project name”和“package name”(应用唯一标识)编辑框内容,单击“Finish”完成项目创建。
图3&nbsp修改项目名和包名,完成创建
刚刚创建的项目展示在 Project Explorer 视图中,项目文件和文件夹如图。
图4&nbsp项目文件结构图
css 文件夹:存放 css 文件,该类型文件定义了应用中的内容样式。
js 文件夹:存放 JavaScript 文件,该类型文件用于实现应用的功能逻辑。
res 文件夹:存放应用程序资源文件,如项目中使用到的图像、字符串和样式等资源。
index.html:应用程序屏幕布局的主 HTML 文件。
manifest.json:应用配置清单文件,包括应用的基本信息,如名称、概要、版本号、权限等,该文件须位于项目文件夹根目录。
以 HelloWorld 这个项目的 manifest 文件为例。
"name":"HelloWorld",
"description":"Simple web example application to demonstrate",
"package":"com.example.helloworld",
"version":"0.1.0",
"start_url":"index.html",
"name":"launcher",
"src":"res/img/appicon.png"
"name":"notification",
"src":"res/img/appicon.png"
"developer":{
"name":"Example Developer",
"url":"http://www.example.com/",
"email":""
"request-permissions":[
"atelier.permission.NOTIFICATION"
以下为 manifest 文件中的各字段介绍,其中带“[]”符号的字段为可选字段。
表1 manifest.json 文件字段含义表
应用的名称
HelloWorld
[description]
应用的概要信息
Simple web example application to demonstrate
应用的 id,用来标识应用的唯一性
com.example.helloworld
应用的版本号
应用的启动页面路径
index.html
应用的图标。尺寸为163*163,格式为 png,颜色深度为32位深度,大小小于64K。
包含子字段:
name:应用的图标文件名
src:应用的图标文件路径
name:launcher
src:res/img/appicon.png
[developer]
应用开发者的信息,包含子字段:
[name]:开发者名字
[url]:开发者主页 URL
[email]:开发者 email
name:Example Developer
url:http://www.example.com/
[request-permissions]
应用所需的权限声明
atelier.permission.NOTIFICATION
这个基础的项目创建完成后,就可以在项目文件中编写代码,为应用实现所需的功能。
当您的应用程序代码准备就绪时,就可以开始编译应用程序。 编译过程会执行验证检查并编译您的 JavaScript 和 CSS 文件。
在 Project Explorer 中选择工程名,单击鼠标右键打开快捷菜单,选择“Make Application”中菜单项,在工程根目录中生成的后缀名为 .app 的应用文件就是部署打包后的 EMSS 应用。
图5&nbsp部署打包应用
在手机上运行应用
应用编写完后,可以使用 EMSS Studio 直接将生成的应用推送安装到手机设备中,这时需要首先将手机与PC连接好。
将手机通过 USB 数据线连接至 PC,在手机通知栏中查看 USB 连接情况并点击进入“USB 连接方式”,确保“USB 调试”开关处于打开状态,如下图所示。
图6&nbsp手机 USB 连接方式图
如果设备 USB 连接不成功,请再次检查设备驱动安装是否正常,以及端口的系统环境变量设置。
在 Project Explorer 中选中工程名,单击鼠标右键打开快捷菜单,选择“Run As > EMSS App”即可以推送到手机中运行应用。
图7&nbsp运行应用
如何使用项目模板开发应用
本章以 EMSS Studio 自带的 IDCardDiscern 项目模板为例,介绍如何修改它,开发出一个新的应用程序,它的主要功能仍然是使用手机 NFC 扫描识别身份证信息。
开发一个身份证扫描应用,并将用户最近3次扫描的身份证信息显示在应用界面,方便查看。
打开 EMSS Studio 后选择“File -> New -> EMSS Application”进入到项目创建向导,在如图 Project 页签 sample 中选择 IDCardDiscern,单击“Finish”完成。
图1&nbsp创建 IDCardDiscern 项目
打开 index.html 后,EMSS Studio 页面右侧就已呈现出该应用的主页面。
图2&nbsp打开 index.html 显示的主页面
该应用的初始界面、运行后的页面分别如下。
图3&nbsp应用初始界面
选择“Run -> Run As -> EMSS App”将该应用安装到手机后,依次打开手机 NFC 应用身份识别开关,将身份证靠近手机NFC感应位置,“嘀”地一声后,就表示感应成功并打印出来了虚拟用户信息。
图4&nbsp运行成功界面
首先来分析应用的基本功能,用户从打开应用到完成身份证识别,符合如下流程。
图5&nbsp应用设计流程
将该应用运行过程分为三个阶段:
扫描前——应用启动后识别 NFC 状态、持续侦听 NFC 状态
扫描中——扫描身份证设备,显示扫描结果
扫描后——结束扫描,关闭扫描开关
接下来介绍如何基于它做进一步的开发,实现该需求可以基于现有模板项目修改实现,主要修改第二阶段中扫码后的数据存放和显示方法。
在每次扫描身份证信息成功后,显示该信息并保存下来。
当扫描次数达到3次及以上时,显示最近3次的信息。
EMSS 在 systemsettings 类定义了 NFC 相关系统设置,并在 nfc 类定义了它的数据结构和方法,由于使用后者需要系统相关权限,所以在开发时需要为应用添加相应的权限声明,在 manifest.json 文件中添加。
"request-permissions":[
"atelier.permission.NFC_CONTROL"
接下来介绍模板项目的关键代码段功能,以及如何通过修改实现新需求的功能,主要修改部分以粗体字呈现。
应用启动后需要识别当前 NFC 是否在开启状态。
模板应用中定义了函数 checkNfcState,用于检查 NFC 当前状态,如果检查成功,会在日志中打印 state 信息,如果失败,打印错误码 errorCode。这个功能由 systemsettings 类 getProperty 接口实现。
显示状态由 showNfcState 函数实现,并且如果 NFC 已开启,直接打开身份证侦听开关,如果没有,则提示用户操作。
//checkNfcState 函数:获取当前 NFC 状态
atelier.systemsettings.getProperty(
atelier.SystemSettingsConstants.NFC_STATE
).then(function(res){
console.log("[nfc] type= " + res.type + ",state= " + res.state);
nfcState = res.
showNfcState(res.state);
if (nfcState) {
//打开监听
openWatch();
$("#openTips").html("已关闭!");
$("#watchMessage").html("操作提示:请先开启 NFC 功能!");
检查成功后,持续监听 NFC 并显示状态,这里借助了 systemsettings 类的 addEventListener 接口实现。
//addEventListener:监听 NFC 功能状态
atelier.systemsettings.addEventListener(atelier.SystemSettingsConstants.NFC_STATE, function(res){
console.log("[nfc] type= " + res.type + ",state= " + res.state);
nfcState = res.
showNfcState(res.state);
NFC 监听和扫描身份证主要依赖 nfc 类 watch 接口实现。
本例中需要多次扫描身份证,因此首先定义一个识别次数变量,初始化赋值为0。
//识别数量
var cardCount = 0;
由于开发本例时没有公安的专用网络环境,这里使用数组构建多个虚拟用户的身份证信息,每个用户信息仍然是 NFCMessage 结构。
本例为 cardData.name 这个字段后加上了扫描序号,用来区别不同的虚拟身份证,其它字段内容不变。
//打印虚拟身份信息:修改该函数,加上 message 参数
var showIDCardInfo = function(message) {
//虚构身份证数据
var cardData = {
name: "某某某",
sex: "男",
nation: "汉",
birthday: "日",
add: "XX省XX市XX区",
ID: "888888",
organ: "XXXX公安局",
validPeriod: "~"
//构建虚拟信息数组
var cardInfo = [];
for (var i = 0; i & cardC i++) {
cardInfo[i] = {};
Object.assign(cardInfo[i],cardData);
//修改名字,用于区分各条数据
cardInfo[i].name = cardData.name + " ( " + (cardCount - i) + " )";
//最多构建三条数据
if(i === 2){
//模拟 message 结构
var IDCard = [];
for (var j = 0; j & cardInfo. j++) {
IDCard[j] = {
recordType: "json",
mediaType: "",
data: cardInfo[j]
url: "myPath/myFileName.txt"
上面代码段中用到了两个数组结构,用途不同
cardInfo[]用于创建多个虚拟身份证信息
IDCard[]用于保存要打印的3个身份证信息
监听身份证识别用到了 nfc 类 watch 接口,当扫描到了信息后,返回 message 数据对象和 watchId。
//打开监听
var openWatch = function() {
navigator.nfc.watch(function(message){
console.log("[nfc] *************************************");
console.log("[nfc] *
console.log("[nfc] *************************************");
//识别数量统计
cardCount += 1;
console.log("[nfc] 识别数量统计 :" + cardCount);
//界面显示虚拟身份信息
showIDCardInfo(message);
}).then(function(id) {
接下来是在页面打印最近3次扫描的身份证信息,示例代码段省略了部分身份证信息字段。
//页面显示身份证信息
var showGetedInfo = $("#showGetedInfo");
var IDCardInfo = $("&div class='well a-bounceinT' id='IDCardInfo'&&/div&");
var detailInfo =
for (var k = 0; k & IDCard. k++) {
detailInfo = $("&div class='detailInfo' id='detailInfo_" + k + "'&&/div&");
$("&span&姓&&&&&&&&名:&&&&" + IDCard[k].data.data.name + "&/span&&br/&").appendTo(detailInfo);
$("&span&性&&&&&&&&别:&&&&" + IDCard[k].data.data.sex + "&/span&&br/&").appendTo(detailInfo);
$("&span&身份证号:&&&&" + IDCard[k].data.data.ID + "&/span&&br/&").appendTo(detailInfo);
$(detailInfo).appendTo(IDCardInfo);
$(IDCardInfo).appendTo(showGetedInfo);
模板应用只能显示1个身份证信息,而新的应用要显示3个身份证信息,对显示页面高度进行调整。
//调整信息显示高度
if (IDCard.length & 1) {
$("#showGetedInfo").css("height","380px");
$("#showGetedInfo").css("height","253px");
$("#watchMessage").html("操作提示:" + IDCard[0].data.data.name + " 识别成功!");
关闭扫描开关由 nfc 类 cancelWatch 接口实现,在提示用户身份识别“已关闭”,并删除扫描记录。
//取消指定 watchId 监听
navigator.nfc.cancelWatch(watchId)
.then(function() {
console.log("[nfc] successed cancelWatch.");
watchState =
//扫描数量重置
cardCount = 0;
$("#openTips").html("已关闭!");
应用调试和运行
编写完以上代码后,在 Project Explorer 选中项目名称“IDCardDiscern”并单击鼠标右键,在 IDE 菜单中选择“Run As -> EMSS App”就能在手机中运行该应用。
图6&nbsp运行应用
连续多次(>3)扫描身份证,可以看到自定义的最近3个用户身份证信息显示在界面上,如图是个扫描了5次的显示结果。
图7&nbsp应用扫描5次的结果显示
如果需要调试该应用,观察应用运行的过程状态信息,也可以在工程中选择“Debug As -> EMSS App”进行过程调试。
图8&nbsp调试应用
关于调试应用,具体请查看《EMSS Studio 用户手册》中的调试方法。
如果在上一步的开发调试和运行中,应用运行正确无误,那么这个扫描身份证的应用就可以正式发布。同样在 Project Explorer 选中工程名,在右键菜单中选择“Make Application -> Make Release Application”。
图9&nbsp发布 IDCardDiscern 应用
会在如图位置生成修改后的 XXX.idcarddiscern_release.app。
图10&nbsp生成修改后的应用
这样,一个经过修改的手机 NFC 读取身份证信息的应用就开发好了,我们可以将该应用发布到相应的应用市场中。
代码样例下载
IDCardDiscern
EMSS(Enterprise Mobile Security Solution)聚焦政企行业用户,除了提供平台基本能力,还根据行业特点提供相关业务能力,目前主要提供的是与外围设备互联互通的功能。
本手册以开发者视角,从当前 EMSS 主要开放的平台基础模块(表 1 )以及企业办公需求模块(表 2)出发,按照使用场景分类,详细介绍了每个业务模块及其接口使用示例。
表1 平台基础模块
表2 企业办公需求模块
本文档主要适用于以下人员:
EMSS&应用开发人员
EMSS&应用性能优化和调试人员
表3 修订记录说明
应用管理模块是整个 EMSS 的核心,主要控制应用程序的生命周期。为了兼顾性能和功耗,将可执行状态分为 FOREGROUND 和 BACKGROUND。当前,为了系统后台行为更为可控,BACKGROUND 状态仅对系统用户开放且配合低功耗管控方案才能使用,第三方用户不可设置。
图1 应用生命周期状态图
从上图可以看出,应用的起始状态为 CREATED,在进入 FOREGROUND 或 BACKGROUND 之前,会先调用事件,通知用户资源已经加载完。该事件是复用的 W3C 标准事件。
应用程序间状态跳转由 EMSS 控制,第三方应用程序在状态跳转之前都会接收到通知事件,用户需要在约定的时间内尽快执行完相关的操作,因为每个状态的停留时间不可预知,在跳转到下一个状态之前需要将逻辑执行完毕,确保程序逻辑完整。表1所示的是事件触发的状态转移规则以及约定的最长执行时间。
表1 事件状态转移规则
最长执行时间
应用管理事件处理机制
在应用程序启动后,用户选择注册所关心的事件。由于每个事件触发时机不一样,需要在不同的事件中处理相关的程序逻辑。同时,在事件处理函数中,建议只处理必要的逻辑,避免函数执行耗时超过建议值。
var appState = "CREATED";
var initAppman = function() {
console.log("[Main] appman init");
// 注册 onForeground 监听
atelier.appman.onForeground.addListener(function() {
console.log("[Main] APP ON FOREGROUND");
appState = "FOREGROUND";
// 触发界面更新...
// 注册 onBackground 监听
atelier.appman.onBackground.addListener(function() {
console.log("[Main] APP ON BACKGROUND");
appState = "BACKGROUND";
// 保存状态...
// 注册onResume监听
atelier.appman.onResume.addListener(function() {
console.log("[Main] APP ON BACKGROUND");
appState = "BACKGROUND";
// JavaScript 恢复可执行状态,恢复保存的状态信息...
// 注册onSuspend监听
atelier.appman.onSuspend.addListener(function() {
console.log("[Main] APP ON SUSPENDING");
appState = "SUSPENDING";
// 在 JavaScript 不可执行之前,保存所有需要保存的状态信息...
// 注册 onClose 监听
atelier.appman.onClose.addListener(function() {
console.log("[Main] APP ON CLOSING");
appState = "CLOSING";
// 在 JavaScript 不可执行之前,保存所有需要保存的状态信息...
代码样例下载
通知是在应用常规界面向用户显示的消息。当用户向系统发出通知时,系统将消息先以图标的形式显示在状态栏中,用户可以打开通知应用查看通知的详细信息。
状态栏和通知应用均是由系统控制的区域,用户可以随时查看。
图1 状态栏中的通知
图2 通知应用中的通知
以上图片分别是状态栏和通知应用的的实例,当消息推送成功后,在状态栏和通知应用中有对应消息的提示。每个消息包括四个基本的提示信息:推送时间、图标、标题、内容。其中,推送时间和图标由系统生成的。
在通知应用中,用户可以查看和删除对应的消息。
状态栏和通知应用中显示的图标,可以在 EMSS Studio 中的工程文件 manifest.json 中定义。示例如下:
// 创建通知图标
// manifest.json 中关于 icons 的设置
"icons": [{
"name": "launcher",
"src": "res/img/appicon_demo.png"
// 应用桌面图标
"name": "statusbar",
"src": "res/img/statusbar_demo.png"
// 状态栏中显示的应用图标
"name": "notification",
"src": "res/img/notification_demo.png"
// 通知应用中显示的应用图标
申请通知权限
通知操作需要申请的全部权限可在 EMSS Studio 中的工程文件 manifest.json 中定义。
// 申请通知权限
"request-permissions":[
"atelier.permission.NOTIFICATION"
创建通知必须包含通知消息的标题和内容,两者分别由参数 title 和 content 设置,以下是创建通知的实例,可作为参数传给 atelier.notification.post 接口。
// 创建通知实例的参数信息
title: "My Notification",
content: "Hello World!",
vibration: true,
// 开启振动
ledColor: "Red",
// 设置呼吸灯为红色,默认为绿色
sound: true
// 开启提示音
通知消息生成后,会在状态栏和通知应用中分别生成对应的通知条目,通知应用中的消息如图3所示。
图3 单个通知消息
上图的通知时间为“19:03”,图标为 USB 接入,标题为“USB 已连接”,内容为“点击可切换 USB 模式”。
发送通知接口 post 的返回值是通知标识 id,使用此 id 可以用于后续对通知消息的操作,包括删除和更新。同时,此 id 是全局唯一的,以下是发送一个简单通知的代码片段。
// 创建简单通知,包括 title 和 content 信息
atelier.notification.post({
title: "My Notification",
content: "Hello World!"
}).then(function(res){
console.log('id: ' + res.id);
// Do something with the id
}).catch(function(res){
console.log('errorCode: ' + res.errorCode);
// Do something with the errorCode
通知消息发送成功后,通知应用会显示标题为“My Notification”,内容为“Hello World!”的通知条目,如图4所示。
图4 新增通知消息
当应用程序的状态改变后,需要将此消息推送到通知应用中时,如果已经有之前推送的消息在通知应用中,不会再重复推送一个消息,但是可以更新该消息。更新通知需要用到被更新消息的 id,代码片段如下所示。
// 创建简单通知,包括 title 和 content 信息
var newId;
atelier.notification.post({
title: "My Notification",
content: "Hello World!"
}).then(function(res){
console.log('id: ' + res.id);
// Do something with the id
newId = res.
}).catch(function(res){
console.log('errorCode: ' + res.errorCode);
// Do something with the errorCode
// 更新已推送通知消息,其中的 id 需要设置为已推送通知消息的 id
atelier.notification.update({
id: newId,
// id 为已经发送成功的通知标记
title: "My Updated Notification",
content: "Hello World Again!",
}).then(function(res){
console.log('Remove success/fail: ' + res);
}).catch(function(res){
console.log('errorCode: ' + res.errorCode);
// Do something with the errorCode
通知消息更新成功后,推送内容为标题“My Updated Notification”的通知条目,如图5所示。
图5 更新通知消息
对于已经失效的通知信息,需要应用及时删除。用户可以选择删除所有消息,或者删除特定消息。通知模块提供了查询通知消息的接口,提供当前应用所有已经发送通知消息的内容。用户选择对应的通知 id,调用删除接口即可。如下代码片段示例为删除特定的通知消息。
// 删除所有 title 为“My Updated Notification”的通知条目
atelier.notification.getAll().then(function(res){
for (var index = 0; index & res. index++){
if (res[index].title == 'My Updated Notification') {
// 删除相关通知消息
atelier.notification.remove(res[index].id).then(function(res){
console.log('Remove success/fail: ' + res);
}).catch(function(res){
console.log('errorCode: ' + res.errorCode);
// Do something with the errorCode
// 显示当前所有剩余通知信息
atelier.notification.getAll().then(function(res){
for (var index = 0; index & res. index++){
console.log('notification id:' + res[index].id);
console.log('notification title:' + res[index].title);
console.log('notification content:' + res[index].content);
console.log('notification postTIme:' + res[index].postTime);
如果要删除所有与该应用相关的已推送通知消息,可以参考如下代码片段。
// 删除当前该应用所有通知消息
atelier.notification.removeAll().then(function(res){
console.log('Remove success/fail: ' + res);
}).catch(function(res){
console.log('errorCode: ' + res.errorCode);
// Do something with the errorCode
增强通知显示效果
当前系统开放了物理提示的接口,包括振动、提示音和呼吸灯这三个信息。用户可以选择是否开启振动和提示音。同时,对于呼吸灯,用户有三种选择:
“Red”,红色闪烁呼吸灯,由参数 ledColor 设置
“Orange”,橙色闪烁呼吸灯,由参数 ledColor 设置
“Green”,绿色闪烁呼吸灯,由参数 ledColor 设置
创建新的通知消息,其中使能了物理提示功能。
// 创建新的通知消息,其中使能了振动、提示灯、提示音
atelier.notification.post({
title: "My Notification",
content: "Hello World!",
vibration: true,
// 开启振动
ledColor: "Red",
// 设置呼吸灯为红色,默认为绿色
sound: true
// 开启提示音
}).then(function(res){
console.log('id: ' + res.id);
// Do something with the id
}).catch(function(res){
console.log('errorCode: ' + res.errorCode);
// Do something with the errorCode
代码样例下载
notification
EMSS 提供了查询设备状态信息的功能,当前主要提供的是硬件设备(如 Wi-Fi、NFC、LOCATION 等)是否使能的信息。表1是对 Wi-Fi、NFC、位置信息、数据业务、静音的简单说明。
表1 硬件设备说明
用户可以获取设备当前最新状态,也可以监听设备状态变化信息。对于设备状态的修改,则统一放到 Settings 系统应用中去处理,避免了设备的敏感状态被任意篡改。
下面以 NFC 为例,描述系统设置模块接口的使用场景。
获取设备当前状态
// 判断 NFC 是否打开
atelier.systemsettings.getProperty({
atelier.SystemSettingsConstants.NFC_STATE
).then(function(res){
if ( res.state == false ){
console.log("NFC power off");
console.log("NFC power on");
}).catch(function(res){
console.log('errorCode: ' + res.errorCode);
// Do something with the errorCode
监听设备设置状态改变
var myListener = function(res){
console.log("type = " + res.type + ", state= " + res.state);
// 注册监听 NFC 状态
atelier.systemsettings.addEventListener(
atelier.SystemSettingsConstants.NFC_STATE,
myListener
// 释放监听 NFC 状态
atelier.systemsettings.removeEventListener(
atelier.SystemSettingsConstants.NFC_STATE,
myListener
代码样例下载
systemsettings
EMSS 提供了拍照和图库相关的选择器(picker)功能,当前只支持图片相关的选择,不支持音频和视频。用户可以通过 &input& 标签和标准属性“type=file”来激活 picker 的功能。
申请 picker 权限
激活 picker 组件的功能需要申请的全部权限可在 EMSS Studio 中的工程文件 manifest.json 中定义。
// 申请 picker 权限
"request-permissions":[
"atelier.permission.LAUNCH_CAMERA_PICKER",
// 拍照选择器
"atelier.permission.LAUNCH_IMAGE_PICKER",
// 图库选择器
使用拍照选择器组件的功能,需要设置&input&标签的属性 accept 和 capture,见表1。
表1 拍照功能的input标签属性
利用 &input& 标签激活拍照功能:
// 显示拍照 input 标签
&div style="position: top: 30 right: 30 background-color: width: 100 height: 100 text-align:"&
&p&选择图库&/p&
&input type="file" name="image" accept="image/*" capture style="width: 100%; height: 100%; opacity: 0;" onchange="setImagePreview(this)"&
// 预览图片
&script type="text/javascript"&
function setImagePreview(data) {
var str = "";
for (var i = 0; i & data.files. i++) {
console.log(data.files[i]);
var src = window.URL.createObjectURL(data.files[i]);
console.log("image path: " + src);
str += "&img src='" + src + "' style='width:100height:100margin:15px 15px 0 15 float:' /&";
document.getElementById('content').innerHTML =
图1 选择拍照
使用图库选择器组件的功能,需要设置&input&标签的属性 accept ,见表2。
表2 图库选择功能的input标签属性
利用 &input& 标签激活图库选择器功能:
// 显示图库 input 标签
&div style="position: top: 30 right: 30 background-color: width: 100 height: 100 text-align:"&
&p&选择图库&/p&
&input type="file" name="image" accept="image/*" style="width: 100%; height: 100%; opacity: 0;" onchange="setImagePreview(this)"&
// 预览图片
&script type="text/javascript"&
function setImagePreview(data) {
var str = "";
for (var i = 0; i & data.files. i++) {
console.log(data.files[i]);
var src = window.URL.createObjectURL(data.files[i]);
console.log("image path: " + src);
str += "&img src='" + src + "' style='width:100height:100margin:15px 15px 0 15 float:' /&";
document.getElementById('content').innerHTML =
图2 选择图库
用户可以选择后续需要处理的图片,点击图片右下角的选择按钮即可选中。
图3 选择图片
同时,用户可以点击每张图片的任意区域去预览图片。
图4 预览图片
更多 input 标签的资料请参见。
EMSS 支持基本录音功能,用户可以通过设置参数来选择具体录音的方式。
申请录音权限
由于用到了音频模块和存储功能,需要申请相应的权限。
//申请录音权限
"request-permissions":[
"atelier.permission.STORAGE_MANAGER",
// 存储权限
"atelier.permission.RECORD_AUDIO",
// 录音权限
"atelier.permission.AUDIO_MANAGER"
// AudioFocus 权限
录音选项设置
在录音示例创建后,需要对录音的选项做相关设置。
audioSource:录音源,目前平台只支持 MIC 格式
outputFormat:录音文件输出格式,目前平台只支持 AAC_ADTS 格式
audioEncoder:录音编码格式,目前平台只支持 AAC 格式
在音频录制过程中发生的错误,可以通过注册监听接口的方式来监测,当前的错误消息有以下两种:
达到录音文件的最大值限制
后台服务异常
录音流程见如下示例代码。
// 注册监听接口
var onRecordingListenerCallback = function(status){
if(status == 'error_no_service'){
console.log("recorder error code is RECORDER_ERROR_SERVER_DIED");
}else if(status == 'error_max_filesize_reached'){
console.log("recorder info code is MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED");
}else if(status == 'loss_audiofocus'){
console.log("recorder loss audiofocus");
console.log("recorder error code is " + status);
atelier.audiorecorder.onRecordingListener.addListener(onRecordingListenerCallback);
// 创建录音
atelier.audiorecorder.create().then(function(){
console.log("audio recorder create success");
}).catch(function(res){
console.log("audio recorder create fail: " + res.errorCode);
// 开始录音
atelier.audiorecorder.start({
audioSource: "MIC",
// 目前平台只支持 MIC
outputFormat: "AAC_ADTS",
// 目前平台只支持 AAC_ADTS
audioEncoder: "AAC"
// 目前平台只支持 AAC
}).then(function(res){
console.log("audio recorder start file name:" + res);
}).catch(function(res){
console.log("audio recorder start fail: " + res.errorCode);
// 录音中...
// 暂停录音
atelier.audiorecorder.pause().then(function(){
console.log("audio recorder pause success");
}).catch(function(res){
console.log("audio recorder pause fail: " + res.errorCode);
// 暂停中...
// 继续录音
atelier.audiorecorder.resume().then(function(){
console.log("audio recorder resume success");
}).catch(function(res){
console.log("audio recorder resume fail: " + res.errorCode);
// 停止录音
atelier.audiorecorder.stop().then(function(){
console.log("audio recorder stop success");
}).catch(function(res){
console.log("audio recorder stop fail: " + res.errorCode);
// 释放录音
atelier.audiorecorder.release().then(function(){
console.log("audio recorder release success");
}).catch(function(res){
console.log("audio recorder release fail: " + res.errorCode);
// 注销监听接口
atelier.audiorecorder.onRecordingListener.removeListener(onRecordingListenerCallback);
删除录音文件
录制下来的音频文件,会保存在特定目录下,用户可以通过如下方式删除已录制音频文件。步骤为:设置 -& 存储 -& 系统空间清理 -& 音频清理。
图1 音频清理
代码样例下载
audiorecorder
NFC(Near Field Communication)是一种近距离无线通信技术,有效距离为 10cm 以内,工作频率为 13.56MHz,目前所支持的数据传输速率有 106Kbps、212Kbps和424Kbps 三种。NFC 主要用于设备间近距离通信场景,有三种运行模式:
读写模式(Read/Write Mode,R/W),例如读公交卡、身份证
卡模拟模式(Card Emulation Mode,CE),这种模式下可以将手机模拟成一张公交卡、门禁卡等
点对点模式(Peer-to-Peer Mode,P2P),用于两个 NFC 设备文件传输等
目前装有 EMSS 的手机(以下简称安全手机)支持 NFC 读写模式。
申请 NFC 权限
对 NFC 进行读写操作前,需要先申请 NFC 权限。
//申请 NFC 权限
"request-permissions":[
"atelier.permission.NFC_CONTROL"
NFC 读写模式
在 NFC 读模式下(写模式类似),手机作为 NFC Reader,发起交互操作。操作对象被称为 NFC tag(例如公交卡),需要 NFC Reader 通过电磁感应提供电能。
图1 NFC 读模式数据交互
目前 NFC 根据不同的存储空间、数据传输速率以及底层协议,支持以下4种不同类型的标签。
表1 NFC 支持的标签类型
数据传输速率
NFC 在进行数据交互时一般使用 NDEF(NFC Forum Data Exchange Format )格式。一个 NDEF 格式消息由多个 NFC Record 组成,真正的数据封装在 Record 中。NDEF 和
Record 的具体格式请参见。
大部分的 NFC tag 都使用 NDEF 格式,但是有些设备(如身份证)读出来的数据不用 NDEF 格式封装,而是原始的数据(raw data),这些数据由对应的第三方应用程序自己定义说明。
安全手机支持 NDEF 格式和 raw data 格式。应用在进行第三方操作前,用户必须使能“设置&无线和网络&NFC”菜单项。
图2 使能 NFC
W3C NFC 标准支持三种类型的数据写入,分别为 DOMString、ArrayBuffer 和 NFCMessage。以下主要介绍 DOMString 类型和 NFCMessage 类型的数据写入。
//WebIDL定义
typedef (DOMString or ArrayBuffer or NFCMessage) NFCPushM
使用 DOMString 数据类型,写入数据
// 字符串数据类型
navigator.nfc.push(
"Text meant for peers only", { target: "tag" }
).then(() =& {
console.log("Message pushed.");
}).catch((error) =& {
console.log("Push failed :-( try again.");
使用 NFCMessage 数据类型,写入 NDEF 格式的数据
// NDEF 数据类型
navigator.nfc.push({
data: [{ recordType: "url", data: "https://w3c.github.io/web-nfc/" }]
}).then(() =& {
console.log("Message pushed.");
}).catch((error) =& {
console.log("Push failed :-( try again.");
另外,EMSS 还对标准接口进行了扩展,增加了加密卡的读写功能,在 NFCPushOptions 中定义了 isNdef。
dictionary NFCPushOptions {
NFCPushTarget target = "any";
unrestricted double timeout = I
// 单位:毫秒
boolean ignoreRead =
boolean isNdef =
// NDEF 标准格式开关,默认为 true。当 isNdef 为 false 的时候,表示使用加密卡模式。
加密卡密钥写入(解密后可通过 watch 监听返回数据)
// 协议介绍:http://nfc-forum.org/
// x[0]:NFC 协议规定是加密卡密码验证方式,0x60--TypeA、0x61--TypeB
// x[1]:1号扇区
// x[2]-x[7]:TypeB 的密码,密码为12位
var buff = new ArrayBuffer(8);
var x = new Uint8Array(buff);
x[0] = 0x61;
x[2] = 0x22;
x[3] = 0x22;
x[4] = 0x22;
x[5] = 0x22;
x[6] = 0x22;
x[7] = 0x22;
// 启动解密卡读取流程(注:isNdef 需要设置为 false)
navigator.nfc.push(buff,{isNdef:false})
// 开启解密
.then(()=&{
// 设置读取命令
console.log("start send.");
var buff1 = new ArrayBuffer(2);
var y = new Uint8Array(buff1);
y[0] = 0x30;
// 开启读取数据的设置
navigator.nfc.push(buff1,{ target: "tag", isNdef:false} ).then(()=&{
console.log("push successed.");
}).catch(err =&{
console.log("push err = " + err);
}).catch(err =&{
console.log("push err = " + err);
读取和写入数据
可以通过 NFCWatchOptions 来过滤出需要读取的数据,然后将数据陆续写入到 tag 中,示例代码如下所示。
// 通过 url 地址过滤出需要读取的数据
navigator.nfc.watch(reader, { url: "*/mypath/mygame/*" });
function reader(message) {
console.log("Source:
" + message.url);
console.log("Game state: " + message.data);
var message = {
url: "/mypath/mygame/update",
recordType: "json",
mediaType: "application/json",
data: { level: 3, points: 4500, lives: 3 }
//将新的数据写入到 tag 中
navigator.nfc.push(message).then(() =& {
console.log('Progress stored!');
}).catch((error) =& {
console.log('Failure, please try again.');
在 NFC 接触过程中,如果数据的读取依赖于 push 写入的数据内容,那么 watch 和 push 函数的调用过程中需要满足两个条件:
ignoreRead 属性值为 false
watch 函数先于 push 函数之前调用
// 打印当前 tag 端内容,watch 函数需要在 push 函数之前调用
navigator.nfc.watch((message) =& {
for (let record of message.data) {
console.log("Record type:
" + record.recordType);
console.log("MIME type:
" + record.mediaType);
console.log("=== data ===\n" + record.data);
// ignoreRead 参数为 false,表示需要从 watch 函数中读取数据
navigator.nfc.push("Pushing data is fun!", { target: "tag", ignoreRead: false });
注:在 NFC 读卡器模式下,具体交互操作取决于具体卡的内容,以上示例仅供参考。
更多关于 NFC 的资料请参见:。
安全手机支持 USB 主机模式(简称 USB Host)。在该模式下,第三方应用可以通过 EMSS 提供的 WebUSB 接口,将手机作为 USB 主设备,来访问和控制 USB 外部设备,访问模式如下图所示。
图1 USB 访问模式
当安全手机与外部 USB 设备连接上后,可以对 USB 外设进行如下操作:枚举设备、访问设备和监听设备状态。
申请设备权限
在对 USB 外部设备进行相关操作之前,还需要给设备申请权限。第三方应用可以通过在 manifest.json 文件中增加下列代码,以获取使用 USB 的权限。
// 申请 USB 权限
"request-permissions":[
"atelier.permission.USB"
可以通过 Device Enumeration 接口列出当前 USB 外部设备,示例代码(在 arduino 测试板上测试验证)如下:
// 获取所有设备信息
navigator.usb.getDevices().then(devices =& {
console.log("USB Device Number: ", devices.length);
// 显示设备总数
for(var i=0; i & devices. i++){
console.log("=============================");
console.log("usbVersionMajor: ", devices[i].usbVersionMajor);
console.log("usbVersionMinor: ", devices[i].usbVersionMinor);
console.log("usbVersionSubminor: ", devices[i].usbVersionSubminor);
console.log("deviceClass: ", devices[i].deviceClass);
console.log("deviceSubclass: ", devices[i].deviceSubclass);
console.log("deviceProtocol: ", devices[i].deviceProtocol);
console.log("vendorId: ", devices[i].vendorId);
console.log("productId: ", devices[i].productId);
// 显示用户感兴趣的设备信息...
在枚举设备时,可以通过 Device Enumeration 接口设置过滤条件,只列出感兴趣的设备,示例代码如下:
// 设置过滤条件
const filters = [
{ 'vendorId': 0x2341, 'productId': 0x8036 },
{ 'vendorId': 0x2341, 'productId': 0x8037 },
// 获取到过滤后的设备列表
return navigator.usb.requestDevice({ 'filters': filters }).then(
devices =& {
for(var i=0; i & devices. i++){
console.log("=============================");
// 显示用户感兴趣的设备信息...
另外,枚举设备时还可以得到 USB 外部设备的详细信息:配置(Configuration)、接口(Interface)、端点(Endpoint)。
在 USB 设备的组织结构中,包含设备、配置、接口和端点 4 个层次。每个 USB 设备都提供了不同级别的配置信息,可以包含一个或多个配置,不同的配置使设备表现出不同的功能组合(在探测/连接期间需从其中选定一个),配置由多个接口组成,而接口由多个端点组成。
端点是 USB 通信的最基本形式,主机只能通过端点与设备进行通信。在 USB 系统中每一个端点都有由设备地址和端点号给出的唯一地址,每个端点都有一定的属性,包括传输方式、总线访问频率、带宽、端点号和数据包的最大容量等。端点可看作是一个单向管道,只能承载设备和主机之间在一个方向的数据。端点 0 通常为控制端点,用于设备初始化参数等。端点 1、2 等一般用作数据端点,存放主机与设备间往来的数据。
图2 USB 设备组织结构图
USB 访问设备包括的操作为:设备打开和关闭、设备控制和数据传输。
设备打开后,就可以选择对应的接口进行控制和数据传输。具体设备所能够提供的配置和接口信息,可以在设备枚举阶段获取。根据 W3C 标准定义,USBConfiguration 的 IDL 定义方式如下所示。
// WebIDL 定义
[Constructor(USBDevice device, octet configurationValue)]
interface USBConfiguration {
readonly attribute octet configurationV
readonly attribute DOMString? configurationN
readonly attribute FrozenArray&USBInterface&
可以先设置所用到的接口,具体接口的信息可以查询 。
// 打开 myUsbDevice,假定 devices[i] 是已经存在的 USB 设备对象
var myUsbD
devices[i].open().then(() =& {
console.log("device opened.");
myUsbDevice = devices[i];
// 查询当前都有哪些 configuration 和 interface
for (var i = 0; i & myUsbDevice.configurations. i++) {
// configuration 的 value
console.log("configurationValue", myUsbDevice.configurations[i].configurationValue);
for (var j = 0; j & myUsbDevice.configurations[i].interfaces. j++) {
// interface 的 Number
console.log("interfaceNumber", myUsbDevice.configurations[i].interfaces[j].interfaceNumber);
// 选择特定配置和接口,假设:配置可以是 1、接口可以是 0
myUsbDevice.selectConfiguration(1).then(() =& myUsbDevice.claimInterface(0))
.then(() =& myUsbDevice.controlTransferOut({
'requestType': 'class',
'recipient': 'interface',
'request': 0x22,
'value': 0x01,
'index': 0x02
// 具体的控制字的内容,参考 USB 设备的规范
.then(() =& myUsbDevice.transferIn(1, 8))
.then(result =& {
// 解析读取到的数据
assert_true(result instanceof USBInTransferResult);
assert_equals(result.status, 'ok');
assert_equals(result.data.byteLength, 8);
for (let i = 0; i & 8; ++i)
assert_equals(result.data.getUint8(i), i, 'mismatch at byte ' + i);
// 传输完成,关闭设备
myUsbDevice.releaseInterface(0);
// 传输完成,关闭设备
myUsbDevice.close();
USB 设备上的属性值由具体设备指定,需要遵循设备的数据手册来设置。对应的属性值有:USBConfiguration,USBInterface,USBEndpoint。
暂不支持同步接口,如标准中的 isochronousTransferIn 和 isochronousTransferOut。
监听设备状态
通过设置监听 onconnect 和 ondisconnect 事件,可以监听 USB 外设的连接或断开状态。
// 监听连接事件
navigator.usb.addEventListener("connect", function(e){
// 显示刚连接的设备信息
console.log("usbVersionMajor: ", e.device.usbVersionMajor);
console.log("usbVersionMinor: ", e.device.usbVersionMinor);
console.log("usbVersionSubminor: ", e.device.usbVersionSubminor);
// 显示用户感兴趣的设备信息...
// 监听断开事件
navigator.usb.addEventListener("disconnect", function(e){
// 显示刚断开的设备信息
console.log("usbVersionMajor: ", e.device.usbVersionMajor);
console.log("usbVersionMinor: ", e.device.usbVersionMinor);
console.log("usbVersionSubminor: ", e.device.usbVersionSubminor);
//显示用户感兴趣的设备信息...
更多关于 USB 的资料请参见以下网址:
WebUSB API:
USB 协议介绍:
Arduino 测试板原型:
Android USB Host:
代码样例下载
运行和调试应用
将手机通过USB数据线连接至PC,在手机通知栏中查看USB连接情况并点击进入“USB连接方式”,确保“USB调试”开关处于打开状态,如下图所示。
图1&nbsp手机USB连接方式图
如果设备USB连接不成功,请再次检查设备驱动安装是否正常,以及端口的系统环境变量设置。
添加环境变量EMSS支持adb连接方式。
为手机在环境变量中设置端口。右键单击“计算机”,选择“属性 & 高级系统设置 & 高级 & 环境变量”,在“系统变量”中找到“ANDROID_ADB_SERVER_PORT”,单击“编辑”。如果该项不存在,单击“新建”添加该变量名称。
图2&nbsp增加变量ANDROID_ADB_SERVER_PORT
单击“编辑”弹出“编辑系统变量”对话框,在“变量名”中输入“ANDROID_ADB_SERVER_PORT”,在“变量值”中输入未占用的端口号(如“21677”),单击“确定”。
图3&nbsp设置设备端口号
设备连接信息
设备连接成功后,在EMSS Studio下方显示的“Console”面板中显示“device connected:当前设备序列号”。同时,在EMSS Studio底部也会显示“Device Connected:当前设备序列号”,如图。
图4&nbsp控制台显示设备已连接
在菜单栏中单击“Window & Show View & Other... & EMSS Web & Devices”,在EMSS Studio下方的“Devices”面板中显示了设备的序列号和应用日志等信息。
应用下拉列表默认只显示正在运行的用户开发的应用。当手机系统中没有用户开发的应用时,显示为“running application not found”。
如图,com.example.blank这个应用正在运行,则在应用下拉列表框中显示“com.example.blank”。
图5&nbspDevices面板显示信息
如需手动刷新应用下拉列表,则需重新选择设备下拉列表框中的原设备序列号。
LogCat和过滤器
LogCat用于显示系统打印的日志。日志信息包括Level、Time、PID、TID、Application、Tag和Text,表示为日志级别、时间、进程ID、线程ID、应用名、Tag和日志详细内容。
过滤器用于过滤日志信息。操作如下:
单击“Devices”页签右上角“Filters View”,弹出日志信息过滤器窗口,其中包含了“All messages”、“chromium messages”和“application-selected”3个过滤器。
“chromium messages”过滤器是用来查看系统的chromium的日志信息。“application-selected”过滤器是用来查看应用下拉列表框中选中的应用的日志信息,使用该过滤器时需要保持所选应用是在运行状态。
图6&nbspDevice面板中过滤器的显示位置与信息
自定义过滤器创建自定义过滤器。单击“Devices”页签右侧“Saved Filters”中的“”,在弹出的过滤器对话窗口中输入内容,如过滤器名字、过滤器条件等。此外,还可以对自定义的过滤器进行删除、编辑,只需要使用右边的“”“”图标即可。
在“Devices”页签中的Logcat底部可输入过滤信息的条件,在下拉列表框中选择过滤级别,窗口会显示过滤后的日志。
在日志过滤级别右边还有三个图标,选择它们(从左至右)可将打印日志导出、清空或暂停。
图7&nbspDecive面板中自定义过滤器
过滤级别如下表所示。
表1 日志过滤级别说明级别
Terminal工具
EMSS Studio集成了Terminal工具,开发者不用打开操作系统自带的终端工具,在EMSS Studio中即可输入adb或其他命令行。
单击工具栏中的图标。在“Choose terminal”和“Encoding”下拉列表框中选择Terminal的连接方式和字符编码类型。默认为“Local Terminal”,即本地系统终端方式。
图8&nbspTerminal发布设置
单击“OK”后,Studio下方会出现“Terminal”页签。
图9&nbsp设置成功后的Terminal 页签
在Terminal页签的编辑区域,可以输入“adb shell”等调试命令。
签名和混淆
对应用做签名校验和代码混淆是应用开发中一项常见的安全措施,在打包生成EMSS应用前,同样需要先签名。如果未对应用做签名,则无法进行打包、运行和调试等操作。
应用签名EMSS Studio当前已有默认的Profile用于应用签名,如下步骤介绍怎样生成一个新的Profile。
在EMSS Studio上方菜单栏中选择“Window & Preferences”,在弹出的Preferences对话框中选择“ EMSS & Security”进入Security页面。单击“Add”弹出“Profile Name”对话框,输入名字,单击“OK”。如图,单击“Create”弹出窗口,在“Name”(名字)、“Alias”(别名)、“Password”(密码)、“Password confirmation”(密码再确认)中分别输入相应的内容。在目录“emss_studio\sdk\default”下存放了签名文件,在“Developer private key”(开发者私钥)中选择导入签名所需的.key文件。在“Certificate chain”(证书链)中择导入签名所需的.crt文件,然后单击“OK”。
图10&nbsp新建Profile
在Preferences窗口中单击“Apply”,再单击“OK”。
执行完以上操作后,就可以打包生成EMSS应用了。
压缩和混淆
开发者在生成EMSS应用时还可以选择压缩,让生成的.app文件体积更小,也可以同时选择代码混淆,让生成的.app文件不容易在反向编译后被他人读取。
在Project Explorer中选择工程并单击鼠标右键打开快捷菜单,单击“Make Application & Make Release Application”,在工程根目录中生成的名为XXX_release.app的应用文件就是压缩和混淆后的EMSS应用。
打包和运行
在Project Explorer中选择工程并单击鼠标右键打开快捷菜单,单击“Make Application & Make Debug Application”,在工程根目录中生成的名为XXX_debug.app的应用文件就是打包后的EMSS应用。
如果想要查看应用运行效果,可以在Project Explorer中选中工程名,单击鼠标右键打开快捷菜单,选择“Run As & EMSS App”即开始运行应用。
以一个Blank模板的应用为例,其运行效果如图所示。
图11&nbspBlank应用的运行效果图
同时,EMSS Studio页面下方的“Console”页签显示应用安装的相关信息。
图12&nbsp应用运行后控制台显示的信息
“Devices”页签在应用下拉列表框中显示当前应用的包名。
图13&nbsp应用运行后Device页签的显示信息
开发者在准备调试EMSS应用时,可以选择不同的调试方式:
使用Studio自带的调试工具为了方便查看应用的占用资源等信息,EMSS Studio中集成了调试工具,可以用于分析应用所消耗的系统资源。
在Project Explorer中的工程名上单击鼠标右键打开快捷菜单,选择“Debug As & EMSS App”,之后“Console”页签里有日志输出。
图14&nbsp应用调试后控制台显示的日志信息
弹出调试窗口后,单击行号设置断点。
图15&nbsp页面调试
对应用中需要调试的界面进行操作,可结合“Sources”右侧的调试栏进行调试。调试栏如下图:
图16&nbsp调试栏
从左到右,各个图标为“Pause/Resume script execution”、“Step over next function call”、“Step into next function call”、“Step out of current function”、“Deactivate/Activate breakpoints”和“Pause on exceptions”。
表2&nbsp图标含义说明项目名
使用Chrome浏览器的调试工具在Project Explorer中的工程名上单击鼠标右键打开快捷菜单,选择“Debug As & Inspect on Chrome”,之后和上述步骤相同,使用这种方式的前提是开发者电脑上要安装有Chrome浏览器。
EMSS Studio默认读取Chrome浏览器(chrome.exe)路径为C:\Program Files (x86)\Google\Chrome\Application,如未在默认路径检测到chrome.exe,则会在选择“Debug As & Inspect on Chrome”后弹出“Preferences(Filtered)”窗口,如图。
图17&nbsp选择EMSS Studio读取的浏览器
单击右侧“New...”,弹出如图“Add External Web Browser”窗口,在“Name”项中填入名称,如“Chrome”;在“Location”项右侧单击“Browse...”并选择chrome.exe所在的路径;在“Parameters”项中填入参数(该项为可选项)。
图18&nbsp添加浏览器
EMSS Studio中集成的调试工具基于Chrome调试工具,本节和下一节主要对其中Timeline和Profiles这两个标签页的功能进行介绍。
通过Timeline页签,可以查看EMSS应用加载过程中各事件的耗时。Timeline能通过事件、帧和内存用量3个方面的数据来监测应用,使用这些数据,开发者可以方便的找出页面中存在问题的地方。
选择“Timeline”页签,界面如下。
图19&nbsp Timeline页签界面
它包含多个功能按钮和选项,下图从左往右依次是“Record”、“Clear recording”、“Capture”“NetWork”、“JS Profile”、“Screenshots”、“Memory”、“Paint”、“Collect garbage”和“CPU throttling”(默认是No CPU throttling,可选项有“High end device(2x slowdown)”、“Low end device(5x slowdown)”和“Set custom rate...”)。
图20&nbspTimeline页签的功能按钮和选项
“Record”用于录制应用运行的性能变化情况,记录时间通常在3-5秒,否则会增加分析难度。
所有事件,从加载资源到解析JavaScript、计算样式和重新绘制都呈现在Timeline上,这些事件被标记为以下颜色:Loading events(网络和HTML解析,蓝色)、Scripting(JavaScript,黄色)、Rendering(渲染,紫色)和Painting(绘制,绿色)。
图21&nbsp 事件颜色分类
一个页面执行效率不高,需要知道页面性能低下的原因是JavaScript执行问题,还是页面渲染问题,如下是对一个问题现象做Timeline视图分析。
首先录制需要被分析的操作,录制结束后预览Timeline,如图红色框内部分,不同的颜色表示不同的事件,一种颜色的区块越长,说明在处理该事件的耗时就越长。单击某一区块,可以在下面的“Summary”页签中看到详细的事件处理过程及耗时分布。
图22&nbspSummary页签界面
关于帧,目标是保证页面要有高于每秒60fps(帧)的刷新频率,这样就能保证页面有高流畅度的渲染。
如图,两段灰色虚线之间代表一帧所用的时间。
图23&nbsp一帧所用的时间
如图,红色框内的绿色部分越高,表示帧率越高,在“Summary”可以得到某帧具体的执行时间和帧率。
图24&nbsp Summary页签中帧的信息
如图,如果某帧耗时过长,把光标移动到红色帧上,会有弹窗提示信息,点击“Summary”中“Function”提示的文件信息,跳转至代码处。
图25&nbspSummary页签中帧耗时过长时的信息
内存视图以图像来呈现应用在录制的时间中占的内存、在内存中保存的文件数、DOM节点以及事件监听器的数目(没有被垃圾回收的)。
在最顶部的筛选条件处选择“Memory”后,蓝色的内存图将会出现,选取一段时间内内存的变化进行观察。
图26&nbsp图像呈现的内存视图
对应在内存视图中,参数有对应的线性图,还可选取(点击参数前带颜色的复选框)不同的参数进行单独观察线性变化。
图27&nbsp线性呈现的内存视图
参数后括号内的数字,在“-”前代表选取时间段内参数的最小值,在“-”后代表同时间段内参数的最大值。
“Profiles”页签集成了JavaScript CPU分析工具、堆的性能分析工具和对象分配跟踪器,这些工具可以显示出函数的执行时间、占用内存的变化情况等,对开发者优化性能、排查内存泄露有很大帮助。
以下为一段问题代码:在应用中添加了一个名为“begin to grow”的按钮,当在手机屏幕上点击按钮时页面会不断地创建DOM节点、JavaScript数组和JavaScript字符串,浏览器内存会不断增大。开发者可以使用Profiles帮助发现此类代码的性能问题。
&!DOCTYPE html&
&meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /&
&title&Profiles Test&/title&
&h1&Example 1&/h1&
&h1&Example 1: Watch the memory grow&/h1&
&li&Open DevTools&/li&
&li&Select Profiles Tab&/li&
&li&Select "Take Heap Snapshot"&/li&
&button onclick="grow()"&begin to grow&/button&
&button onclick="stop()"&stop growing&/button&
&button onclick="removeHeap()"&remove some heap&/button&
&div id="nodes"&&/div&
var x = [];
var nodes =
function createSomeNodes() {
var div, buf, i = 100,
frag = document.createDocumentFragment();
for (; i & 0; i--) {
div = document.createElement('div');
buf = document.createTextNode(i + ' - ' + new Date().toTimeString());
div.appendChild(buf);
frag.appendChild(div);
document.getElementById('nodes').appendChild(frag);
function grow() {
console.log("add one time");
x.push(new Array(1000000).join('x'));
nodes=document.getElementById('nodes');
if(!nodes){
nodes = document.createElement("div");
nodes.setAttribute("id","nodes");
document.body.appendChild(nodes);
createSomeNodes();
timer=setTimeout(grow, 1000);
function stop() {
clearTimeout(timer);
function removeHeap(){
nodes = document.getElementById('nodes');
if(nodes){
nodes.remove();
录制JavaScript CPU Profile
这个工具用来显示在指定时间内,各个函数的执行时间情况,开发者可以根据函数占用时长,分析函数的性能状况。
在“Profiles”中,选择“Record JavaScript CPU Profile”,单击下方的“Start”或左侧的“Start CPU profiling”(圆点图标),开始录制。
图28&nbsp开始录制JavaScript CPU Profile
进行一次代码执行的操作后单击左侧的“Stop CPU profiling”或下方的“Stop”,停止录制。
图29&nbsp停止录制JavaScript CPU Profile
在录制完成时,面板中间会生成视图,用户可以根据视图分析应用行为。单击“Clear all profiles”,可清空所有录制的CPU PROFILES。
图30&nbsp清空录制的CPU PROFILES
这里对上一步生成的视图进行介绍,它有三种模式。Heavy(Bottom UP)Heavy视图为录制完成后的默认视图,由Self、Total和Function三列数据构成。其显示了所有执行过的方法和它们花费的时间及占用时间的比例大小,通过列表的展示,可以很容易地找到占用时间最大的几个函数,并作出相应的优化。
图31&nbspHeavy视图
Heavy视图中列数据说明如下。
表3&nbspHeavy视图列名称说明项目名
ChartChart视图的顶部为随时间轴变化各方法的执行情况,图表中凸起部分代表了有方法执行,执行的方法越多,凸起部分的高度越高。
图32&nbspChart视图
Chart视图的下部分为详情页面,用于显示顶部栅栏选中区域内方法的详情,单击对应的方法(如grow)左下角会显示详细内容。
表4&nbspChart试图方法项目说明项目名
在Chart视图中,开发者可以更加直观地看到在录制的时间内,方法什么时候执行,单次执行花费时间等情况这有助于开发者更深入的剖析各个方法,为性能优化提供更加具体的数据支持。
Tree(Top Down)Tree视图与Heavy视图具有相同的数据显示页面,不过在Function的展示上,Tree使用了层级结构,单击对应方法的下拉箭头,可以看到方法内部调用的其他方法。通过Tree视图,可以更直观地看出方法之间的嵌套关系。
图33&nbspTree视图
“堆快照”可以记录当前的堆内存(heap)快照,并生成对象的描述文件,该描述文件为内存泄漏的排查提供了非常有用的信息,比如JavaScript运行时所用到的所有对象,以及这些对象所占用的内存大小、引用的层级关系等。
在“Profiles”页签中,选择“Take Heap Snapshot”后,单击下方的“Take Snapshot”或左侧的“Take heap snapshot”(圆点图标),开始录制。
图34&nbsp开始录制堆快照
录制完成后会生成录制文件,如Snapshot1。
图35&nbsp生成录制文件
继续单击“Take heap snapshot”进行录制,会生产Snapshot2,则可对Snapshot1和Snapshot2进行对比了。
图36&nbsp再次录制生成录制文件
单击“Clear all profiles”可清空所有录制的快照。
图37&nbsp清空录制快照
Take Heap Snapshot 提供了4种视图模式(如下表),根据不同的任务,点击红框处的黑色倒三角即可以快速选择并切换视图。
图38&nbsp堆快照视图选择与切换
表5&nbsp视图模式说明项目名
Summary视图
快照打开默认以Summary视图显示,可以看到对象总数以及展开显示的具体内容。展开一个总体行后,会显示所有的对象实例。每一个实例的直接占用内存和占用总内存都被相应显示。@符号后的数字是对象的唯一ID,有了它就可以逐个对象的在不同快照间作对比。
图39&nbspSummary视图
Summary视图还可以通过构造函数分组寻找对象(和对象的内存使用),对找出DOM内存泄漏很有帮助。列字段说明如下。
表6&nbspSummary视图列项目名称项目名
Summary视图中部分字段的含义如下表。
表7&nbspSummary视图字段说明项目名
Comparison视图
Comparison视图用来对比不同的快照来找到快照之间的差异,通过显示哪些对象内存被正确的回收了来搜寻内存泄漏,通常在一个操作前后记录两个(或更多)的内存使用快照。
图40&nbspComparison视图
它是通过查看释放的内存和引用数目的差异来查看是否有内存泄漏,并找到原因,证明对应用的某个操作没有造成泄漏(比如打开一个document,然后关闭,这样是不会造成泄漏的),可以按以下的步骤尝试:
在操作前拍一个堆快照。执行一个操作(做你认为会造成泄漏的动作)。撤消之前的操作(上一个操作相反的操作,多重复几次)。拍第二个快照,将视图切换成对照视图,并同快照1进行对比。在对照视图下,两个快照之间的不同就会展现出来了。当展开一个总类目后,增加和删除了的对象就显示出来了。
Containment视图
Containment视图展示了更好的对象结构,可以称作应用对象结构的“鸟瞰视图(bird's eyes view)”,它能让你查看Function内部,观察VM内部对象,帮助分析全局作用域(如window)中对象引用情况来找到是什么保留了这些对象;能帮助分析闭包并深入到对象更深层去查看应用的内存使用情况,如图所示。
图41&nbspContainment视图
部分名词解释如下。
表8&nbspObject下字段名称含义项目名
Statistic视图
Statistic视图统计了几种容易占用内存的对象内存,并以饼图的形式直观地展现了它们的内存占用情况。
图42&nbspStatistic视图
饼图各项指标说明如下。
表9&nbspStatistic视图各字段含义项目名
录制Allocation Timeline
对象跟踪器整合了Heap Profiler的快照增量更新分析和Timeline面板记录,和其它工具一样,记录对象的堆配置需要启动记录,执行一系列操作,然后停止记录进行分析。
在“Profiles”页签中,选择“Record Heap Allocations”,单击下方的“Start”或左侧的“Start recording heap profile(圆点图标)”,开始录制。
图43&nbsp开始录制Allocation Timeline
执行需要查看的代码,单击“Stop recording heap profile”,停止录制。
图44&nbsp停止录制Allocation Timeline
柱条表示在堆中生成的新对象。高度对应了相应对象的大小,颜色表示这个对象是否仍然在最后拍的那个快照中。蓝色柱表示这个对象最后还在Timeline中,灰色柱表示这个对象在Timeline中生成,但结束前已经被内存回收了。
图45&nbsp生成结果显示
上面的例子中,一个动作执行了8次,同一个程序保留了7个对象,所以最后7个蓝色柱条被保留了,但这最后留下的柱条存在潜在的问题,可以用Timeline上的滑动条缩小到那个特定的快照并找到这个分配的对象。
单击一个堆中的对象就能在堆快照的下面部分显示它的保留总内存树,检查这个对象的保留总内存树能够提供足够的信息来了解为什么这个对象没有被回收,然后就能对代码做相应的修改来去掉不必要的引用。
单击“Clear all profiles”可清空所有录制的快照。
图46&nbsp清空录制的profiles
EMSS API 整体上采用了最新 W3C 函数定义风格:函数调用使用 Promise 异步方式;事件监听使用增加和删除 Listener 的方式。
EMSS&函数定义
Promise&String& atelier.audiorecorder.start(OBJECT)
为了明确表述函数的参数定义和异常处理的错误码等,在每个函数中以下面的描述字段列出了函数的详细信息。
表1 函数描述字段说明描述字段
输入参数描述
定义函数输入参数的详细信息。如果输入参数为上述 start 函数中的 OBJECT 数据格式,则需描述该参数的类型、是否必填、说明等;如果输入参数为上述 start 函数中的 OBJECT 数据格式,则需描述该参数的类型、是否必填、说明等;
输出参数描述 / 输出描述
定义函数输出参数的详细信息。
如果输出参数为上述 start 函数中的 String 等标准数据格式,则只需要说明该参数的用途如果输出参数为上述 start 函数中的 String 等标准数据格式,则只需要说明该参数的用途;如果输出参数为 OBJECT 数据格式,则根据具体函数详细定义。
输出异常参数描述 /输出异常描述
EMSS&事件监听定义
atelier.audiorecorder.onRecordingListener.addListener(CALLBACK)
atelier.audiorecorder.onRecordingListener.removeListener(CALLBACK)
为了使接口表述简便,使用了下面两个类型表述符号:
CALLBACK:表示一个回调函数,用在事件监听场合,CALLBACK 所携带的参数在每个函数中具体定义,可在函数描述字段“输出参数描述”中查看
OBJECT:JavaScript 对象,具体内容看参数定义
需要用到的W3C标准定义关键字:
Promise:异步函数定义
sequence:表示一个 OBJECT 的队列
any:表示任意类型的数据
基本数据类型:Boolean、String、Number、Date
在输入和输出参数中,如果可以明确该参数中只需要单个参数,可以用 W3C 标准数据类型(如 String 类型)来表示;如果该参数中将来有可能有多个子参数,则定义为 OBJECT 类型。
本文档主要适用于以下人员:
EMSS 应用开发人员
EMSS 应用性能优化和调试人员
表2 修订记录说明产品
应用生命周期管理相关 API
在 EMSS 中,Web 应用程序的状态分为如下四大类
CREATED:刚创建完成,无法执行任何 JavaScript 代码
FOREGROUND,BACKGROUND:运行状态,分前台和后台模式,可以执行 JavaScript 代码
SUSPENDING,SUSPENDED:挂起状态,无法执行 JavaScript 代码
CLOSING,CLOSED:结束状态
监听 resume 事件
当应用从 SUSPENDED 状态返回到可运行状态,则触发该事件。在此事件中,用户可以调用恢复状态的操作。
atelier.appman.onResume.addListener(CALLBACK)
atelier.appman.onResume.removeListener(CALLBACK)
不需要权限
输出参数描述
var myResumeListener = function(){
console.log("onResume");
atelier.appman.onResume.addListener(myResumeListener);
// Do something ...
atelier.appman.onResume.removeListener(myResumeListener);
onBackground
监听 app 切换到后台的事件
当 app 处于 BACKGROUND 状态,可以执行 JavaScript 程序,以及网络操作等。后台运行的程序应尽量避免过多占用 CPU,否则会影响前台应用。
atelier.appman.onBackground.addListener(CALLBACK)
atelier.appman.onBackground.removeListener(CALLBACK)
不需要权限
输出参数描述
var myBackgroundListener = function(){
console.log("onBackground");
atelier.appman.onBackground.addListener(myBackgroundListener);
// Do something ...
atelier.appman.onBackground.removeListener(myBackgroundListener);
onForeground
监听 app 切换到前台的事件
atelier.appman.onForeground.addListener(CALLBACK)
atelier.appman.onForeground.removeListener(CALLBACK)
不需要权限
输出参数描述
var myForegroundListener = function(){
console.log("onForeground");
atelier.appman.onForeground.addListener(myForegroundListener);
// Do something ...
atelier.appman.onForeground.removeListener(myForegroundListener);
监听 suspend 事件
在此事件中,用户可以做相关清除操作,并且保存应用程序状态。用户可以保存相关信息,以便于在接收到 onResume 事件后恢复到之前的状态。
atelier.appman.onSuspend.addListener(CALLBACK)
atelier.appman.onSuspend.removeListener(CALLBACK)
不需要权限
输出参数描述
var mySuspendListener = function(){
console.log("onSuspend");
atelier.appman.onSuspend.addListener(mySuspendListener);
// Do something ...
atelier.appman.onSuspend.removeListener(mySuspendListener);
监听 close 事件
atelier.appman.onClose.addListener(CALLBACK)
atelier.appman.onSuspend.removeListener(CALLBACK)
不需要权限
输出参数描述
var myCloseListener = function(){
console.log("onClose");
atelier.appman.onClose.addListener(myCloseListener);
// Do something ...
atelier.appman.onClose.removeListener(myCloseListener);
代码样例下载
notification
notification 用于描述如何发送通知消息到系统状态栏和通知应用中,以及如何管理通知消息,其支持以下相关功能。
第三方应用通知操作:发送、获取、更新、删除等功能
通知相关的内容,包括标题、内容、呼吸灯、振动、提示音功能
每个应用最多允许发送8条通知消息,达到上限后则需要先删除通知消息才能继续发送,或更新已有通知消息。
Promise&OBJECT& atelier.notification.post(OBJECT)
atelier.permission.NOTIFICATION
输入参数描述
表1&nbsppost 方法输入参数
输出参数描述
表2&nbsppost 方法输出参数
输出异常参数描述
表3&nbsppost 方法输出异常参数
atelier.notification.post({
title: "My Notification",
content: "Hello World!",
vibration: true,
// 开启振动
ledColor: "Red",
// 设置呼吸灯为红色
sound: true
// 开启提示音
}).then(function(res){
console.log('id: ' + res.id);
// Do something with the id
}).catch(function(res){
console.log('errorCode: ' + res.errorCode);
// Do something with the errorCode
注:在 post 调用成功后,会返回表示该通知的 id,该 id 会被 remove 和 update 接口所使用,用于操作已经发送的通知。
获取应用已发送的通知
Promise&OBJECT& atelier.notification.get(Number id)
atelier.permission.NOTIFICATION
输入参数描述
表1&nbspget 方法输入参数
输出参数描述
表2&nbspget 方法输出参数
输出异常参数描述
表3&nbspget 方法输出异常参数
// 首先创建一个通知消息
var postedId;
atelier.notification.post({
title: "My Notification",
content: "Hello World!",
vibration: true,
// 开启振动
ledColor: "Red",
// 设置呼吸灯为红色
sound: true
// 开启提示音
}).then(function(res){
console.log('id: ' + r}

我要回帖

更多关于 华为手机右上角显示hd 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信