理解OAuth 2.0

OAuth是一个关于授权(authorization)的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版。

本文对OAuth 2.0的设计思路和运行流程,做一个简明通俗的解释,主要参考材料为RFC 6749

OAuth Logo

一、应用场景

为了理解OAuth的适用场合,让我举一个假设的例子。

有一个”云冲印”的网站,可以将用户储存在Google的照片,冲印出来。用户为了使用该服务,必须让”云冲印”读取自己储存在Google上的照片。

云冲印

问题是只有得到用户的授权,Google才会同意”云冲印”读取这些照片。那么,”云冲印”怎样获得用户的授权呢?

传统方法是,用户将自己的Google用户名和密码,告诉”云冲印”,后者就可以读取用户的照片了。这样的做法有以下几个严重的缺点。

(1)”云冲印”为了后续的服务,会保存用户的密码,这样很不安全。

(2)Google不得不部署密码登录,而我们知道,单纯的密码登录并不安全。

(3)”云冲印”拥有了获取用户储存在Google所有资料的权力,用户没法限制”云冲印”获得授权的范围和有效期。

(4)用户只有修改密码,才能收回赋予”云冲印”的权力。但是这样做,会使得其他所有获得用户授权的第三方应用程序全部失效。

(5)只要有一个第三方应用程序被破解,就会导致用户密码泄漏,以及所有被密码保护的数据泄漏。

OAuth就是为了解决上面这些问题而诞生的。

二、名词定义

在详细讲解OAuth 2.0之前,需要了解几个专用名词。它们对读懂后面的讲解,尤其是几张图,至关重要。

(1) Third-party application:第三方应用程序,本文中又称”客户端”(client),即上一节例子中的”云冲印”。

(2)HTTP service:HTTP服务提供商,本文中简称”服务提供商”,即上一节例子中的Google。

(3)Resource Owner:资源所有者,本文中又称”用户”(user)。

(4)User Agent:用户代理,本文中就是指浏览器。

(5)Authorization server:认证服务器,即服务提供商专门用来处理认证的服务器。

(6)Resource server:资源服务器,即服务提供商存放用户生成的资源的服务器。它与认证服务器,可以是同一台服务器,也可以是不同的服务器。

知道了上面这些名词,就不难理解,OAuth的作用就是让”客户端”安全可控地获取”用户”的授权,与”服务商提供商”进行互动。

三、OAuth的思路

OAuth在”客户端”与”服务提供商”之间,设置了一个授权层(authorization layer)。”客户端”不能直接登录”服务提供商”,只能登录授权层,以此将用户与客户端区分开来。”客户端”登录授权层所用的令牌(token),与用户的密码不同。用户可以在登录的时候,指定授权层令牌的权限范围和有效期。

“客户端”登录授权层以后,”服务提供商”根据令牌的权限范围和有效期,向”客户端”开放用户储存的资料。

四、运行流程

OAuth 2.0的运行流程如下图,摘自RFC 6749。

OAuth运行流程

(A)用户打开客户端以后,客户端要求用户给予授权。

(B)用户同意给予客户端授权。

(C)客户端使用上一步获得的授权,向认证服务器申请令牌。

(D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。

(E)客户端使用令牌,向资源服务器申请获取资源。

(F)资源服务器确认令牌无误,同意向客户端开放资源。

不难看出来,上面六个步骤之中,B是关键,即用户怎样才能给于客户端授权。有了这个授权以后,客户端就可以获取令牌,进而凭令牌获取资源。

下面一一讲解客户端获取授权的四种模式。

五、客户端的授权模式

客户端必须得到用户的授权(authorization grant),才能获得令牌(access token)。OAuth 2.0定义了四种授权方式。

  • 授权码模式(authorization code)
  • 简化模式(implicit)
  • 密码模式(resource owner password credentials)
  • 客户端模式(client credentials)

六、授权码模式

授权码模式(authorization code)是功能最完整、流程最严密的授权模式。它的特点就是通过客户端的后台服务器,与”服务提供商”的认证服务器进行互动。

授权码模式

它的步骤如下:

(A)用户访问客户端,后者将前者导向认证服务器。

(B)用户选择是否给予客户端授权。

(C)假设用户给予授权,认证服务器将用户导向客户端事先指定的”重定向URI”(redirection URI),同时附上一个授权码。

(D)客户端收到授权码,附上早先的”重定向URI”,向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。

(E)认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。

下面是上面这些步骤所需要的参数。

A步骤中,客户端申请认证的URI,包含以下参数:

  • response_type:表示授权类型,必选项,此处的值固定为”code”
  • client_id:表示客户端的ID,必选项
  • redirect_uri:表示重定向URI,可选项
  • scope:表示申请的权限范围,可选项
  • state:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。

下面是一个例子。

GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz
        &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com

C步骤中,服务器回应客户端的URI,包含以下参数:

  • code:表示授权码,必选项。该码的有效期应该很短,通常设为10分钟,客户端只能使用该码一次,否则会被授权服务器拒绝。该码与客户端ID和重定向URI,是一一对应关系。
  • state:如果客户端的请求中包含这个参数,认证服务器的回应也必须一模一样包含这个参数。

下面是一个例子。

HTTP/1.1 302 Found
Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA
          &state=xyz

D步骤中,客户端向认证服务器申请令牌的HTTP请求,包含以下参数:

  • grant_type:表示使用的授权模式,必选项,此处的值固定为”authorization_code”。
  • code:表示上一步获得的授权码,必选项。
  • redirect_uri:表示重定向URI,必选项,且必须与A步骤中的该参数值保持一致。
  • client_id:表示客户端ID,必选项。

下面是一个例子。

POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb

E步骤中,认证服务器发送的HTTP回复,包含以下参数:

  • access_token:表示访问令牌,必选项。
  • token_type:表示令牌类型,该值大小写不敏感,必选项,可以是bearer类型或mac类型。
  • expires_in:表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。
  • refresh_token:表示更新令牌,用来获取下一次的访问令牌,可选项。
  • scope:表示权限范围,如果与客户端申请的范围一致,此项可省略。

下面是一个例子。

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
  "access_token":"2YotnFZFEjr1zCsicMWpAA",
  "token_type":"example",
  "expires_in":3600,
  "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
  "example_parameter":"example_value"
}

从上面代码可以看到,相关参数使用JSON格式发送(Content-Type: application/json)。此外,HTTP头信息中明确指定不得缓存。

七、简化模式

简化模式(implicit grant type)不通过第三方应用程序的服务器,直接在浏览器中向认证服务器申请令牌,跳过了”授权码”这个步骤,因此得名。所有步骤在浏览器中完成,令牌对访问者是可见的,且客户端不需要认证。

简化模式

它的步骤如下:

(A)客户端将用户导向认证服务器。

(B)用户决定是否给于客户端授权。

(C)假设用户给予授权,认证服务器将用户导向客户端指定的”重定向URI”,并在URI的Hash部分包含了访问令牌。

(D)浏览器向资源服务器发出请求,其中不包括上一步收到的Hash值。

(E)资源服务器返回一个网页,其中包含的代码可以获取Hash值中的令牌。

(F)浏览器执行上一步获得的脚本,提取出令牌。

(G)浏览器将令牌发给客户端。

下面是上面这些步骤所需要的参数。

A步骤中,客户端发出的HTTP请求,包含以下参数:

  • response_type:表示授权类型,此处的值固定为”token”,必选项。
  • client_id:表示客户端的ID,必选项。
  • redirect_uri:表示重定向的URI,可选项。
  • scope:表示权限范围,可选项。
  • state:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。

下面是一个例子。

GET /authorize?response_type=token&client_id=s6BhdRkqt3&state=xyz
    &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com

C步骤中,认证服务器回应客户端的URI,包含以下参数:

  • access_token:表示访问令牌,必选项。
  • token_type:表示令牌类型,该值大小写不敏感,必选项。
  • expires_in:表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。
  • scope:表示权限范围,如果与客户端申请的范围一致,此项可省略。
  • state:如果客户端的请求中包含这个参数,认证服务器的回应也必须一模一样包含这个参数。

下面是一个例子。

HTTP/1.1 302 Found
Location: http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA
          &state=xyz&token_type=example&expires_in=3600

在上面的例子中,认证服务器用HTTP头信息的Location栏,指定浏览器重定向的网址。注意,在这个网址的Hash部分包含了令牌。

根据上面的D步骤,下一步浏览器会访问Location指定的网址,但是Hash部分不会发送。接下来的E步骤,服务提供商的资源服务器发送过来的代码,会提取出Hash中的令牌。

八、密码模式

密码模式(Resource Owner Password Credentials Grant)中,用户向客户端提供自己的用户名和密码。客户端使用这些信息,向”服务商提供商”索要授权。

在这种模式中,用户必须把自己的密码给客户端,但是客户端不得储存密码。这通常用在用户对客户端高度信任的情况下,比如客户端是操作系统的一部分,或者由一个著名公司出品。而认证服务器只有在其他授权模式无法执行的情况下,才能考虑使用这种模式。

密码模式

它的步骤如下:

(A)用户向客户端提供用户名和密码。

(B)客户端将用户名和密码发给认证服务器,向后者请求令牌。

(C)认证服务器确认无误后,向客户端提供访问令牌。

B步骤中,客户端发出的HTTP请求,包含以下参数:

  • grant_type:表示授权类型,此处的值固定为”password”,必选项。
  • username:表示用户名,必选项。
  • password:表示用户的密码,必选项。
  • scope:表示权限范围,可选项。

下面是一个例子。

POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

grant_type=password&username=johndoe&password=A3ddj3w

C步骤中,认证服务器向客户端发送访问令牌,下面是一个例子。

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
  "access_token":"2YotnFZFEjr1zCsicMWpAA",
  "token_type":"example",
  "expires_in":3600,
  "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
  "example_parameter":"example_value"
}

上面代码中,各个参数的含义参见《授权码模式》一节。

整个过程中,客户端不得保存用户的密码。

九、客户端模式

客户端模式(Client Credentials Grant)指客户端以自己的名义,而不是以用户的名义,向”服务提供商”进行认证。严格地说,客户端模式并不属于OAuth框架所要解决的问题。在这种模式中,用户直接向客户端注册,客户端以自己的名义要求”服务提供商”提供服务,其实不存在授权问题。

客户端模式

它的步骤如下:

(A)客户端向认证服务器进行身份认证,并要求一个访问令牌。

(B)认证服务器确认无误后,向客户端提供访问令牌。

A步骤中,客户端发出的HTTP请求,包含以下参数:

  • granttype:表示授权类型,此处的值固定为”clientcredentials”,必选项。
  • scope:表示权限范围,可选项。
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials

认证服务器必须以某种方式,验证客户端身份。

B步骤中,认证服务器向客户端发送访问令牌,下面是一个例子。

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
  "access_token":"2YotnFZFEjr1zCsicMWpAA",
  "token_type":"example",
  "expires_in":3600,
  "example_parameter":"example_value"
}

上面代码中,各个参数的含义参见《授权码模式》一节。

十、更新令牌

如果用户访问的时候,客户端的”访问令牌”已经过期,则需要使用”更新令牌”申请一个新的访问令牌。

客户端发出更新令牌的HTTP请求,包含以下参数:

  • granttype:表示使用的授权模式,此处的值固定为”refreshtoken”,必选项。
  • refresh_token:表示早前收到的更新令牌,必选项。
  • scope:表示申请的授权范围,不可以超出上一次申请的范围,如果省略该参数,则表示与上一次一致。

下面是一个例子。

POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA

(完)

Mysql常用命令及语句

1、连接mysql服务器 mysql -u root -p -S /mnt/data/mysql/mysql.sock 2、创建用户 insert into mysql.user(Host,User,Password) values (‘%’, ‘test’, password(‘test123’)); 3、创建数据库 create database testdb 4、设置权限 grant all privileges on testdb.* to testdb@’%’ identified by ‘123’; 5、刷新权限 FLUSH PRIVILEGES

有关设计的网站

创意公司,有关创意的网站 www.adworks.com www.bertha.com www.bcreative.com www.jdgdesign.com www.collierreps.com www.lightpage.com www.virtualaccesscorp.com 有关美术指导的网站 www.aiga.org 美国平面艺术学院 www.graphic-design.com 设计与出版公司 www.digitaldirectory.com 数字名录 www.portpolios.com 在线公文箱 www.gocreate.com 智慧工作 文案部分,有关文案的网站 www.copychef.com www.copywriting.com www.theslot.com http://www.tjcec.net http://www.designer8.net http://www.designdb.com 经常用到的图片网 http://creative.gettyimages.com/source/search/ http://www.fortunecookie500.com/ ; http://www.bciusa.com/index9.html ; http://gimp-savvy.com/PHOTO-ARCHIVE/ ; http://www.ditto.com/ http://www.freefoto.com/ ; http://www.freeimages.co.uk/ ; http://www.freestockphotos.com/ ; http://creative.gettyimages.com/imagebank/ ; http://www.gograph.com/ ; http://www.archivephotos.com/ ; http://www.istockphoto.com/ ; http://www.photo.net/ ; http://www.photobox.ru/ ; http://www.photonica.com/load_home.htm ; http://www.pictor.com/ ; http://www.pro-visions.com/ http://www.naturephotographers.net/... ;passCookie=true http://www.snagyourpic.org/ ; http://www.snap-shot.com/ ; http://www.visipix.com/index.htm ; http://www.webshots.com/homepage.html ; 设计公司 现代杂志设计 http://www.frostdesign.co.uk/ 朗涛设计 http://www.landor.com.hk/ 瑞士NIKLAUS TROXLER设计 http://www.troxlerart.ch/ 韩家英设计 http://www.hanjiaying.com/ 王粤飞设计 http://www.wyuefei.com/ 沈浩鹏设计 http://www.hopesun.com/ 肖勇设计 http://www.xiaoyong.com 王序设计 http://www.wangxu.com.cn/ 靳与刘设计顾问 http://www.kanandlau.com 陈幼坚设计 http:www.alanchandesign.com 北京西东商业设计有限公司 http://www.cbsdesign.com/ 正达平面设计 http://www.chenzhengda.com/ 正邦品牌识别设计 http://www.zhengbang.com.cn 互动国际设计 http://www.itsworld.net/ 李素雅的个人网站 www.soanala.com 上海吉品堂设计顾问机构[http://www.poledesign.com.cn/ 香港一个工作室的sixstation http://www.sixstation.com/mix.htm 火凤凰设计 http://www.hope-phoenix.com 金脑袋设计 http://www.gbdo.com http://www.limon.com.cn http://www.tjcec.net 设计网 兰色理想-网站设计与开发人员之家http://www.blueidea.com/ 七色鸟设计论坛 http://www.colorbird.com/ 中国平面设计师http://www.fgdesign.net 产品设计论坛 http://bbs.billwang.net/ 视觉中国 http://www.chinavisual.com 北京设计网 http://www.beijingdesign.com 艺术中国设计网 http://zzx.shangdu.com/ 设计在线 http://www.dolcn.com 视觉互动 http://www.interv.com.cn/ 中国设计之窗 http://www.333cn.com 中国国际设计网 http://www.ccii.com.cn 设计教育网 http://www.desi 周玲(周玲) 2010-02-03 13:36:16 gnedu.org 设计联盟 http://www.szdesign.org/ 设计联合网 http://www.dunec.com 龙行设计 http://www.chinavi.net 中国品牌网 http://www.chinabrandweb.com 江南大学设计星空 http://www.sodcn.com 江南大学 http://www.wxuli.edu.cn 中国美术学院 http://www.chinaacademyofart.com 媒介平面设计大赏 http://www.media.com.hk 国际平面设计社团协会 http://www.icograda.org 国际海报设计网Rene Wanner’s Poster Page 中国设计网(包装印刷设计加工平台)http://www.cndgn.com IconFactory(著名的更改系统图标布景软件 CandyBar 的厂家 http://www.iconfactory.com http://www.posterpage.ch/ 国际精品网站赏析 http://www.lakmus.com 国际精品网站赏析-中华设计俱乐部 http://www.dizainer.com/ 澳门设计师协会 http://www.adm.org 环境图形设计协会 http://www.segd.org 香港设计师协会 http://www.hkda.org.hk 第二届华人平面设计大赛 http://www.design4china.com 中国工业设计网 http://www.indesigner.net/ 中国工业设计网 http://www.sino-id.com AIGA国际设计网 http://www.aiga.org 国际平面设计社团协会 ICOGRADA http://www.icograda.org 英国设计者与艺术指导协会(British Design&Art DirectionD&AD) http://www.dandad.com 澳大利亚图形设计协会Australian Graqphic Design Association [AGDA]http://www.agda.asn.au 加拿大图形设计师协会 Society of Graphic Designers of Canada[SGDC] http://www.gdc.net 美国图形艺术研究协会 American Institute of Graphic Arts [AIGA] http://www.aiga.org 国际设计竞赛网 http://www.dolcn.com/competition 国际设计网 http://www.bjidesign.com 集美工业设计 http://www.jimeidesign.com 北京迪欧设计 http://www.togodesign.com.cn 设计共和「Design Republic」 http://www.designrepublic.org.tw 台湾最热门的设计信息网之一,每日的设计动态、专栏文章。世界各地的竞赛,以及上千个设计连结。包括设计公司、博物馆、设计组织、设计杂志等 Graphic Artists Guild http://www.gag.org 以平面设计、网站设计、多媒体设计师为主的联谊性质社团,在美国钗h大城市皆据点 世界优秀设计/World Good Design http://www.w-g-d.net 世界各国优秀设计竞赛评选获奖作品展示大全。在G标志主办者财团法人日本产业设计振兴会的协助下,以”Good Design is Good Business”目标的具体化,世界上的优秀设计情报的详细说明以及商品知识的普及为目的。 BillWang.net设计论坛 http://bbs.billwang.net 人气不一般的设计讨论区,下载设计资料的最好地方,甚至有一些设计图书整书扫描下载。 亚洲CI论坛 http://bbs.asiaci.com 自由设计新家园 http://www.wswin.com 设计杂志 《艺术与设计》 http://www.artdesign.com.cn 《包装&设计》 http://www.package-design.net 《CG杂志》 http://www.newcg.com/ 《i.Ge Conception》 www.ige-cn.com 其他 世界500强管理案例 http://www.mmrc.net/doc/management/world500/ 壁纸好站发布区http://www.yourthink.com 中国设计网(包装印刷设计加工平台)http://www.cndgn.com IconFactory(著名的更改系统图标布景软件 CandyBar 的厂家 www.iconfactory.com 安瑞索思多媒体 http://www.energysource-cn.com/ 中国数码CG http://www.cgchina.com/ 中国青年出版社 http://www.21books.com 中国数码视频在线 http://www.chinadv.com/ 热浪墙纸 http://www.32pic.com/ 软件开发下载 http://www.zdnet.com.cn/ 天堂网络游戏 http://www.tiantang.com.cn 游戏在线 http://online-game.com.cn game-sega游戏网 http://sega.my163.com/ 街头文化 http://www.sharpfans.com/ cg艺术 http://www.cgchannel.com 世纪前线 http://www.avl.com.cn 电影院 http://www.pighome.org 音乐殿堂 http://91music.com MTV音乐电视频道 http://www.mtv.com channel[V]音乐电视频道 http://www.vchinese.com 中华行知网 http://www.sotrip.com 典藏艺术网 http://www.artouch.com/ 世纪在线中国艺术网

CSS字体大小: em与px、pt、百分比之间的对比

CSS样式最混乱的一个方面是应用程序中文本扩展的font-size属性。在CSS中,你可以用四个不同的单位度量来显示在web浏览器中的文本大小。这四个单位哪一种最适合Web? 这个问题引起了广泛的争论。找到一个确定的答案是困难的, 因为这个问题,本身就是如此难以回答。

  接触这些单位

  1. “Ems”(em):“em”是一个可伸缩的单位, 用于web文档媒体展示。一个em等于当前的字体大小,例如,如果文档的字体大小是12 pt,1 em等于12 pt。Ems在本质上是可伸缩的,所以2 em相当于24 pt,.5 em相当于6 pt等。ems由于其可伸缩性和适应移动设备的特性在web文档中正变得越来越受欢迎。 2. 像素(px):像素是固定大小的单元,用于屏幕媒体(即在电脑屏幕上读取)。一个像素等于电脑屏幕上的一个点 (是你屏幕分辨率的最小分割)。许多网页设计师在web文档使用像素单位以生产浏览器渲染的像素完美呈现的网站。像素单元的一个问题是,它没有为视障读者的扩展,以适应移动设备。 3. 点(pt):点通常用于印刷媒体(任何打印在纸上的媒体,等等)。一个点等于一英寸的1/72。点更像像素,他们是固定大小的单位,不能伸缩。 4. 百分比(%):百分比单位更像“em”单位,除了一些根本性的差异。首先,当前的字体大小等于100%(比如12 pt = 100%)。当使用百分比单位,你的文字在移动设备上仍然保持完全的可伸缩性和可访问性。

  那么,区别是什么呢?

当你动态地观察他们就很容易理解字体大小单位之间的区别。一般来说,1 em = 12 pt = 16 px = 100%。当使用这些font-size, 增加基础本字体大小(使用CSS选择器)从100%到120%,让我们看看会发生什么。 正如你所看到的, 随着基本字体大小增加em和百分比单位变大,但px 和 pt 没有变化。为你的文本设置一个绝对的大小很容易,但它是更容易在你的访客使用可伸缩的文本,可以显示任何设备或机器上。出于这个原因,em和百分比单位是web文档文本的首选。

  Em和percent的对比

我们定义点和像素单位不一定是最适合web文档,留下来是的em和百分比单位。在理论上,em和百分比单位是相同的,但在应用程序中,他们实际上有一些细微的差别需要着重考虑。 在上面的示例中,我们使用百分比单位作为我们的基础字体大小(body上标记)。如果你把你的字体大小从百分比变为ems(即body{font-size:1 em;}),你可能不会注意到有什么差别。让我们看看当“1 em”是我们的body字体大小,当客户端改变浏览器的“文字大小”设置(这在一些浏览器中可用,如Internet Explorer)会发生什么。 当客户端的浏览器文本大小设置为“medium”, ems和百分比之间没有区别。然而,当设置改变,差别是相当大的。“Smallest”设置,ems比百分比小得多,而当设置为“Largest”,这时恰恰相反,ems比百分比显示地更大。有些可能会说,当真正去扩展时em单位在扩展,在实际的应用程序中,em文本尺度变化太大, 在一些客户端机器上最小的文本变得不是很清晰。

  结论

理论上,em单位是网上新的和即将到来的字体大小标准,但在实践中,百分比单位似乎给用户提供一个更加一致的和可访问的显示。当客户端设置改变,百分比文本以合理的比例扩展,允许设计师保持可读性,可访问性和视觉设计

  赢家:百分比(%)。

  附录(2011年1月)

从我写这篇文章已经有几年了,我想总结那段时间所发生的讨论。一般来说,当我开始一个新的设计,我将在body元素上使用百分比(body { font-size: 62.5%; }),然后使用em单位大小来比较。只要body使用百分比单位设置,您可以选择使用百分比或ems或其他任何CSS规则和选择器并且在你的基础字体大小上保持使用百分比的好处。在过去的几年中,这确实成为设计的标准。 像素现在被认为是可接受的字体大小单位(用户可以使用浏览器的“缩放”功能读小文本),虽然由于移动设备非常高的密度屏幕(一些Android和iPhone设备每英寸超过200到 300个像素,让你11 - 12-pixel字体很难看到!)他们也开始引起一些问题。

Android常用知识点总汇

一、系统上安装了多种浏览器,能否指定某浏览器访问指定页面?请说明原由。 如果在你的android系统上安装了多种浏览器,能否指定某浏览器访问指定页面?答案当然是:肯定的。 具体方法如下: Intent intent = newIntent(); intent.setAction(“android.intent.action.VIEW”); Uri content_uri_browsers = Uri.parse(“http://isomobile.com"); intent.setData(content_uri_browsers); intent.setClassName(“com.android.browser”, “com.android.browser.BrowserActivity”); startActivity(intent); 问题的关键在于我们设置了class name,也就是我们想要跳转的pakcage的activity。如果你想要跳转到其它的浏览器,只需要修改一下这个函数就OK了。 好,我们现在来让刚刚的思路来指导我们的实践。假如我们现在要直接启动UC浏览器,那么我们该怎么做呢?让我们step by step吧。 1)下载UC apk: http://i-uc.net/read.php?2 2)用7zip解压apk文件,得到classes.dex文件 3)下载反编译dex文件工具: http://nchc.dl.sourceforge.net/project/dedexer/dedexer/1.5/ddx1.5.jar(Dedexer 项目主页:http://dedexer.sourceforge.net/ ) 4)执行命令:java -jar ddx1.5.jar -o -D -d c: c:classes.dex 5)得到package name是:com.uc.browser,启动的activity是:com.uc.browser.ActivityUpdate(补充:当我在这里选择采用ActivityBrowser的时候发觉权限不够,报permiss denied 异常,而且也不是我们要的那个activity,幸运的是在第二次尝试用ActivityUpdate,刚好能满足要求) 6)修改上面的代码为intent.setClassName(“com.uc.browser”,”com.uc.browser.ActivityUpdate”); 二、请解释下Android程序运行时权限与文件系统权限的区别 要区分apk运行时的拥有的权限与在文件系统上被访问(读写执行)的权限两个概念。apk程序是运行在虚拟机上的,对应的是Android独特的权限机制,只有体现到文件系统上时才使用linux的权限设置。 (一)linux文件系统上的权限 -rwxr-x–x system system 4156 2010-04-30 16:13 test.apk 代表的是相应的用户/用户组及其他人对此文件的访问权限,与此文件运行起来具有的权限完全不相关。比如上面的例子只能说明system用户拥有对此文件的读写执行权限;system组的用户对此文件拥有读、执行权限;其他人对此文件只具有执行权限。而test.apk运行起来后可以干哪些事情,跟这个就不相关了。千万不要看apk文件系统上属于system/system用户及用户组,或者root/root用户及用户组,就认为apk具有system或root权限 (二)Android的权限规则 (1)Android中的apk必须签名 这种签名不是基于权威证书的,不会决定某个应用允不允许安装,而是一种自签名证书。重要的是,android系统有的权限是基于签名的。比如:system等级的权限有专门对应的签名,签名不对,权限也就获取不到。默认生成的APK文件是debug签名的。获取system权限时用到的签名,见:如何使Android应用程序获取系统权限 (2)基于UserID的进程级别的安全机制 大家都知道,进程有独立的地址空间,进程与进程间默认是不能互相访问的,是一种很可靠的保护机制。Android通过为每一个安装在设备上的包(apk)分配唯一的linux userID来实现,名称为”app_”加一个数字,比如app_43不同的UserID,运行在不同的进程,所以apk之间默认便不能相互访问。Android提供了如下的一种机制,可以使两个apk打破前面讲的这种壁垒。在AndroidManifest.xml中利用sharedUserId属性给不同的package分配相同的userID,通过这样做,两个package可以被当做同一个程序,系统会分配给两个程序相同的UserID。当然,基于安全考虑,两个package需要有相同的签名,否则没有验证也就没有意义了。(这里补充一点:并不是说分配了同样的UserID,两程序就运行在同一进程, 下面为PS指令摘取的,显然,system、app_2分别对应的两个进程的PID都不同,不知Android到底是怎样实现它的机制的) User PID PPID system 953 883 187340 55052 ffffffff afe0cbcc S system_server app_2 1072 883 100264 19564 ffffffff afe0dcc4 S com.android.inputmethod. system 1083 883 111808 23192 ffffffff afe0dcc4 S android.process.omsservi app_2 1088 883 156464 45720 ffffffff afe0dcc4 S android.process.acore (3)默认apk生成的数据对外是不可见的 实现方法是:Android会为程序存储的数据分配该程序的UserID。借助于Linux严格的文件系统访问权限,便实现了apk之间不能相互访问似有数据的机制。 例:我的应用创建的一个文件,默认权限如下,可以看到只有UserID为app_21的程序才能读写该文件。 -rw——- app_21 app_21 87650 2000-01-01 09:48 test.txt 如何对外开放? 使用MODE_WORLD_READABLE and/or MODE_WORLD_WRITEABLE 标记。 When creating a new file with getSharedPreferences(String, int), openFileOutput(String, int), or openOrCreateDatabase(String, int, SQLiteDatabase.CursorFactory), you can use the MODE_WORLD_READABLE and/or MODE_WORLD_WRITEABLE flags to allow any other package to read/write the file. When setting these flags, the file is still owned by your application, but its global read and/or write permissions have been set appropriately so any other application can see it. (4)AndroidManifest.xml中的显式权限声明 Android默认应用是没有任何权限去操作其他应用或系统相关特性的,应用在进行某些操作时都需要显式地去申请相应的权限。 一般以下动作时都需要申请相应的权限: A particular permission may be enforced at a number of places during your program’s operation: At the time of a call into the system, to prevent an application from executing certain functions. When starting an activity, to prevent applications from launching activities of other applications. Both sending and receiving broadcasts, to control who can receive your broadcast or who can send a broadcast to you. When accessing and operating on a content provider. Binding or starting a service. 在应用安装的时候,package installer会检测该应用请求的权限,根据该应用的签名或者提示用户来分配相应的权限。在程序运行期间是不检测权限的。如果安装时权限获取失败,那执行就会出错,不会提示用户权限不够。大多数情况下,权限不足导致的失败会引发一个 SecurityException, 会在系统log(system log)中有相关记录。 (5)权限继承/UserID继承 当我们遇到apk权限不足时,我们有时会考虑写一个linux程序,然后由apk调用它去完成某个它没有权限完成的事情,很遗憾,这种方法是行不通的。前面讲过,android权限是经营在进程层面的,也就是说一个apk应用启动的子进程的权限不可能超越其父进程的权限(即apk的权限),即使单独运行某个应用有权限做某事,但如果它是由一个apk调用的,那权限就会被限制。实际上,android是通过给子进程分配父进程的UserID实现这一机制的。 (三)常见权限不足问题分析 首先要知道,普通apk程序是运行在非root、非system层级的,也就是说看要访问的文件的权限时,看的是最后三位。另外,通过system/app安装的apk的权限一般比直接安装或adb install安装的apk的权限要高一些。 言归正传,运行一个android应用程序过程中遇到权限不足,一般分为两种情况: (1)Log中可明显看到权限不足的提示。 此种情况一般是AndroidManifest.xml中缺少相应的权限设置,好好查找一番权限列表,应该就可解决,是最易处理的情况。有时权限都加上了,但还是报权限不足,是什么情况呢?Android系统有一些API及权限是需要apk具有一定的等级才能运行的。比如 SystemClock.setCurrentTimeMillis()修改系统时间,WRITE_SECURE_SETTINGS权限好像都是需要有system级的权限才行。也就是说UserID是system。 (2)Log里没有报权限不足,而是一些其他Exception的提示,这也有可能是权限不足造成的。比如:我们常会想读/写一个配置文件或其他一些不是自己创建的文件,常会报java.io.FileNotFoundException错误。系统认为比较重要的文件一般权限设置的也会比较严格,特别是一些很重要的(配置)文件或目录。如 -r–r—– bluetooth bluetooth 935 2010-07-09 20:21 dbus.conf drwxrwx–x system system 2010-07-07 02:05 data dbus.conf好像是蓝牙的配置文件,从权限上来看,根本就不可能改动,非bluetooth用户连读的权利都没有。/data目录下存的是所有程序的私有数据,默认情况下android是不允许普通apk访问/data目录下内容的,通过data目录的权限设置可知,其他用户没有读的权限。所以adb普通权限下在data目录下敲ls命令,会得到opendir failed, Permission denied的错误,通过代码file.listfiles()也无法获得data目录下的内容。 上面两种情况,一般都需要提升apk的权限,目前我所知的apk能提升到的权限就是system(具体方法见:如何使Android应用程序获取系统权限), 至于是否有root级的,如何提升至root级不得而知,知道的朋友劳烦告知,感激不尽。 三、AIDL的全称是什么?如何工作?能处理哪些类型的数据? 详情请参看:http://buaadallas.blog.51cto.com/399160/372090 部分概念: 在Android中, 每个应用程序都可以有自己的进程. 在写UI应用的时候, 经常要用到Service. 在不同的进程中, 怎样传递对象呢? 显然, Java中不允许跨进程内存共享。因此传递对象, 只能把对象拆分成操作系统能理解的简单形式, 以达到跨界对象访问的目的。在J2EE中,采用RMI的方式, 可以通过序列化传递对象。在Android中, 则采用AIDL的方式。 理论上AIDL可以传递Bundle,实际上做起来却比较麻烦. AIDL(AndRoid接口描述语言)是一种借口描述语言; 编译器可以通过aidl文件生成一段代码,通过预先定义的接口达到两个进程内部通信进程的目的. 如果需要在一Activity中, 访问另一个Service中的某个对象, 需要先将对象转化成AIDL可识别的参数(可能是多个参数), 然后使用AIDL来传递这些参数, 在消息的接收端, 使用这些参数组装成自己需要的对象。 AIDL的IPC的机制和COM或CORBA类似, 是基于接口的,但它是轻量级的。它使用代理类在客户端和实现层间传递值. 如果要使用AIDL, 需要完成2件事情: 1. 引入AIDL的相关类; 2. 调用aidl产生的class。 AIDL的创建方法: AIDL语法很简单,可以用来声明一个带一个或多个方法的接口,也可以传递参数和返回值。由于远程调用的需要, 这些参数和返回值并不是任何类型.下面是些AIDL支持的数据类型: 1. 不需要import声明的简单Java编程语言类型(int,boolean等) 2. String, CharSequence不需要特殊声明 3. List, Map和Parcelables类型, 这些类型内所包含的数据成员也只能是简单数据类型, String等其他比支持的类型。 四、请解释下在单线程模型中Message、Handler、Message Queue、Looper之间的关系。 1. Android进程 在了解Android线程之前得先了解一下Android的进程。当一个程序第一次启动的时候,Android会启动一个LINUX进程和一个主线程。默认的情况下,所有该程序的组件都将在该进程和线程中运行。同时,Android会为每个应用程序分配一个单独的LINUX用户。Android会尽量保留一个正在运行进程,只在内存资源出现不足时,Android 会尝试停止一些进程从而释放足够的资源给其他新的进程使用,也能保证用户正在访问的当前进程有足够的资源去及时地响应用户的事件。Android会根据进程中运行的组件类别以及组件的状态来判断该进程的重要性,Android会首先停止那些不重要的进程。按照重要性从高到低一共有五个级别: 前台进程 前台进程是用户当前正在使用的进程。只有一些前台进程可以在任何时候都存在。他们是最后一个被结束的,当内存低到根本连他们都不能运行的时候。一般来说,在这种情况下,设备会进行内存调度,中止一些前台进程来保持对用户交互的响应。 可见进程 可见进程不包含前台的组件但是会在屏幕上显示一个可见的进程是的重要程度很高,除非前台进程需要获取它的资源,不然不会被中止。 服务进程 运行着一个通过startService() 方法启动的service,这个service不属于上面提到的2种更高重要性的。service所在的进程虽然对用户不是直接可见的,但是他们执行了用户非常关注的任务(比如播放mp3,从网络下载数据)。只要前台进程和可见进程有足够的内存,系统不会回收他们。 后台进程 运 行着一个对用户不可见的activity(调用过 onStop() 方法).这些进程对用户体验没有直接的影响,可以在服务进程、可见进程、前台进程需要内存的时候回收。通常,系统中会有很多不可见进程在运行,他们被保存在LRU (least recently used) 列表中,以便内存不足的时候被第一时间回收。如果一个activity正确的执行了它的生命周期,关闭这个进程对于用户体验没有太大的影响。 空进程 未运行任何程序组件。运行这些进程的唯一原因是作为一个缓存,缩短下次程序需要重新使用的启动时间。系统经常中止这些进程,这样可以调节程序缓存和系统缓存的平衡。Android 对进程的重要性评级的时候,选取它最高的级别。另外,当被另外的一个进程依赖的时候,某个进程的级别可能会增高。一个为其他进程服务的进程永远不会比被服务的进程重要级低。因为服务进程比后台activity进程重要级高,因此一个要进行耗时工作的activity最好启动一个service来做这个工作,而不是开启一个子进程――特别是这个操作需要的时间比activity存在的时间还要长的时候。例如,在后台播放音乐,向网上上传摄像头拍到的图片,使用service可以使进程最少获取到“服务进程”级别的重要级,而不用考虑activity目前是什么状态。broadcast receivers做费时的工作的时候,也应该启用一个服务而不是开一个线程。 2. 单线程模型 当一个程序第一次启动时,Android会同时启动一个对应的主线程(Main Thread),主线程主要负责处理与UI相关的事件,如用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理。所以主线程通常又被叫做UI线程。在开发Android应用时必须遵守单线程模型的原则: Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。 2.1 子线程更新UI Android的UI是单线程(Single-threaded)的。为了避免拖住GUI,一些较费时的对象应该交给独立的线程去执行。如果幕后的线程来执行UI对象,Android就会发出错误讯息 CalledFromWrongThreadException。以后遇到这样的异常抛出时就要知道怎么回事了! 2.2 Message Queue 在单线程模型下,为了解决类似的问题,Android设计了一个Message Queue(消息队列), 线程间可以通过该Message Queue并结合Handler和Looper组件进行信息交换。下面将对它们进行分别介绍: 1. Message Message消息,理解为线程间交流的信息,处理数据后台线程需要更新UI,则发送Message内含一些数据给UI线程。 2. Handler Handler处理者,是Message的主要处理者,负责Message的发送,Message内容的执行处理。后台线程就是通过传进来的 Handler对象引用来sendMessage(Message)。而使用Handler,需要implement该类的 handleMessage(Message)方法,它是处理这些Message的操作内容,例如Update UI。通常需要子类化Handler来实现handleMessage方法。 3. Message Queue Message Queue消息队列,用来存放通过Handler发布的消息,按照先进先出执行。 每个message queue都会有一个对应的Handler。Handler会向message queue通过两种方法发送消息:sendMessage或post。这两种消息都会插在message queue队尾并按先进先出执行。但通过这两种方法发送的消息执行的方式略有不同:通过sendMessage发送的是一个message对象,会被 Handler的handleMessage()函数处理;而通过post方法发送的是一个runnable对象,则会自己执行。 4. Looper Looper是每条线程里的Message Queue的管家。Android没有Global的Message Queue,而Android会自动替主线程(UI线程)建立Message Queue,但在子线程里并没有建立Message Queue。所以调用Looper.getMainLooper()得到的主线程的Looper不为NULL,但调用Looper.myLooper() 得到当前线程的Looper就有可能为NULL。 对于子线程使用Looper,API Doc提供了正确的使用方法: 这个Message机制的大概流程: 1)在Looper.loop()方法运行开始后,循环地按照接收顺序取出Message Queue里面的非NULL的Message。 2)一开始Message Queue里面的Message都是NULL的。当Handler.sendMessage(Message)到Message Queue,该函数里面设置了那个Message对象的target属性是当前的Handler对象。随后Looper取出了那个Message,则调用该Message的target指向的Hander的dispatchMessage函数对Message进行处理。 在dispatchMessage方法里,如何处理Message则由用户指定,三个判断,优先级从高到低: 1) Message里面的Callback,一个实现了Runnable接口的对象,其中run函数做处理工作; 2) Handler里面的mCallback指向的一个实现了Callback接口的对象,由其handleMessage进行处理; 3) 处理消息Handler对象对应的类继承并实现了其中handleMessage函数,通过这个实现的handleMessage函数处理消息。 由此可见,我们实现的handleMessage方法是优先级最低的! 3. Handler处理完该Message (update UI) 后,Looper则设置该Message为NULL,以便回收! 在网上有很多文章讲述主线程和其他子线程如何交互,传送信息,最终谁来执行处理信息之类的,个人理解是最简单的方法——判断Handler对象里面的Looper对象是属于哪条线程的,则由该线程来执行! 1)当Handler对象的构造函数的参数为空,则为当前所在线程的Looper; 2) Looper.getMainLooper()得到的是主线程的Looper对象,Looper.myLooper()得到的是当前线程的Looper对象。 五、注册广播有几种方式,这些方式有何优缺点?请谈谈Android引入广播机制的用意。 Android 的广播机制 在 Android 里面有各种各样的广播,比如电池的使用状态,电话的接收和短信的接收都会产生一个广播,应用程序开发者也可以监听这些广播并做出程序逻辑的处理。下面我画一张粗略的图来帮助大家理解广播的运行机制。 Android 中有各式各样的广播,各种广播在Android 系统中运行,当系统/应用程序运行时便会向 Android 注册各种广播,Android 接收到广播会便会判断哪种广播需要哪种事件,然后向不同需要事件的应用程序注册事件,不同的广播可能处理不同的事件也可能处理相同的广播事件,这时就需要 Android 系统为我们做筛选。 案例分析: 一个经典的电话黑名单,首先通过将黑名单号码保存在数据库里面,当来电时,我们接收到来电广播并将黑名单号码与数据库中的某个数据做匹配,如果匹配的话则做出相应的处理,比如挂掉电话、比如静音等等… Demo 分析: 下面通过一个小DEMO 来讲解一下广播在Android 中如何编写,在Demo中我们设置了一个按钮为按钮设置点击监听通过点击发送广播,在后台中接收到广播并打印LOG信息。代码如下: //BroadCastActivity 页面代码 publicclassBroadCastActivity extendsActivity { publicstaticfinalString ACTION_INTENT_TEST = “com.terry.broadcast.test”; @Override publicvoidonCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Button btn = (Button) findViewById(R.id.Button01); btn.setOnClickListener(newOnClickListener() { @Override publicvoidonClick(View v) { //TODO Auto-generated method stub Intent intent = newIntent(ACTION_INTENT_TEST); sendBroadcast(intent); } }); } } //接收器代码如下: publicclassmyBroadCast extendsBroadcastReceiver { publicmyBroadCast() { Log.v(“BROADCAST_TAG”, “myBroadCast”); } @Override publicvoidonReceive(Context context, Intent intent) { //TODO Auto-generated method stub Log.v(“BROADCAST_TAG”, “onReceive”); } } Android 广播的生命周期 在上面的接收器中,继承了BroadcastReceiver 并重写了它的onReceive 并构造了一个函数,下面通过图片来一步一步认识 Android 广播的生命周期。当我点击一下按钮,它向Android 发送了一个广播,如下图: 这时我们再点击一下按钮,它还是会再向 Android 系统发送广播,此时日志信息如下: 下面本人画一张图像,描述了Android 中广播的生命周期,其次它并不像Activity 一样复杂,运行原理很简单如下图: 下面来看一下SDK给出的解释: 大意为:如果一个广播处理完onReceive 那么系统将认定此对象将不再是一个活动的对象,也就会finished掉它。 至此,大家应该能明白 Android 的广播生命周期的原理,代码也不用多介绍,很简单的一个发送广播并处理广播的Demo。 Android 如何判断并筛选广播? 前 面说过 Android 的广播有各式各样,那么Android 系统是如何帮我们处理我们需要哪种广播并为我们提供相应的广播服务呢?这里有一点需要大家注意,每实现一个广播接收类必须在我们应用程序中的 manifest 中显式的注明哪一个类需要广播,并为其设置过滤器,如下图: Tip:action 代表一个要执行的动作,在Andriod 中有很action 比如 ACTION_VIEW,ACTION_EDIT 那么有些人会问了,如果我在一个广播接收器中要处理多个动作呢?那要如何去处理? 在Android 的接收器中onReceive 以经为我们想到的,同样的你必须在Intent-filter 里面注册该动作,可以是系统的广播动作也可以是自己需要的广播,之后你之需要在onReceive 方法中,通过intent.getAction()判断传进来的动作即可做出不同的处理,不同的动作。具体大家可以去尝试测试一下。 小结: 在Android 中如果要发送一个广播必须使用sendBroadCast 向系统发送对其感兴趣的广播接收器中。 使用广播必须要有一个intent 对象必设置其action动作对象 使用广播必须在配置文件中显式的指明该广播对象 每次接收广播都会重新生成一个接收广播的对象 在BroadCast 中尽量不要处理太多逻辑问题,建议复杂的逻辑交给Activity 或者 Service 去处理 Android广播机制(两种注册方法) 在android下,要想接受广播信息,那么这个广播接收器就得我们自己来实现了,我们可以继承BroadcastReceiver,就可以有一个广播接受器了。有个接受器还不够,我们还得重写BroadcastReceiver里面的onReceiver方法,当来广播的时候我们要干什么,这就要我们自己来实现,不过我们可以搞一个信息防火墙。具体的代码: publicclassSmsBroadCastReceiver extendsBroadcastReceiver { @Override publicvoidonReceive(Context context, Intent intent) { Bundle bundle = intent.getExtras(); Object[] object = (Object[])bundle.get(“pdus”); SmsMessage sms[]=newSmsMessage[object.length]; for(inti=0;i<object.length;i++) { sms[0] = SmsMessage.createFromPdu((byte[])object[i]); Toast.makeText(context, “来自”+sms[i].getDisplayOriginatingAddress()+” 的消息是:”+sms[i].getDisplayMessageBody(), Toast.LENGTH_SHORT).show(); } //终止广播,在这里我们可以稍微处理,根据用户输入的号码可以实现短信防火墙。 abortBroadcast(); } } 当实现了广播接收器,还要设置广播接收器接收广播信息的类型,这里是信息:android.provider.Telephony.SMS_RECEIVED。我们就可以把广播接收器注册到系统里面,可以让系统知道我们有个广播接收器。这里有两种,一种是代码动态注册: //生成广播处理 smsBroadCastReceiver = new SmsBroadCastReceiver(); //实例化过滤器并设置要过滤的广播 IntentFilter intentFilter = newIntentFilter(“android.provider.Telephony.SMS_RECEIVED”); //注册广播 BroadCastReceiverActivity.this.registerReceiver(smsBroadCastReceiver, intentFilter); 一种是在AndroidManifest.xml中配置广播 package=”spl.broadCastReceiver” android:versionCode=”1” android:versionName=”1.0”> android:label=”@string/app_name”> 两种注册类型的区别是: 1)第一种不是常驻型广播,也就是说广播跟随程序的生命周期。 2)第二种是常驻型,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行。 BroadcastReceiver用于监听被广播的事件 必须被注册,有两种方法: 1、在应用程序的代码中注册 注册BroadcastReceiver: registerReceiver(receiver,filter); 取消注册BroadcastReceiver: unregisterReceiver(receiver); 当BroadcastReceiver更新UI,通常会使用这样的方法注册。启动Activity时候注册BroadcastReceiver,Activity不可见时候,取消注册。 2、在androidmanifest.xml当中注册 使用这样的方法注册弊端:它会始终处于活动状态,毕竟是手机开发,cpu和电源资源比较少,一直处于活动耗费大,不利。 六. Android Service和Binder、AIDL? 作为Android重要的后台服务,这些每个Android开发者都应该掌握,这也算是和Java SE最大的不同了,具体的实例大家可以查看Android音乐播放器的源代码Music.git中的,这里不再赘述。

常用Linux命令

1、查看sshd是否已经是系统服务

# chkconfig –list |grep sshd

会显示:

sshd 0:off 1:off 2:off 3:off 4:off 5:off 6:off

使用如下命令设置sshd服务自动启动:

# chkconfig –level 5 sshd on

2、检查服务是否启用

ps -ef | grep ssh

3、安装服务

yum install openssh-server

4、安装完之后开启

/etc/init.d/sshd start

5、

以安装成服务的tomcat环境设置远程调试功能

此种情况就是针对将tomcat安装成本地服务了,但是又想进行远程调试的情况,别说改catalina.bat,就是应为改不了才尝试其他办法的。 你可以通过如下方式来编辑Tomcat的服务启动参数来调试安装成Tomcat服务后的web应用程序。 打开命令提示符 设置CATALINA_HOME环境变量,把其指向tomcat主目录 例如:set CATALINA_HOME=d:tomcat6 运行如下命令:%CATALINA_HOME%bintomcat6w.exe //ES//<这里填写你的Tomcat的服务名> 当然了,我的服务名是自定制过的,不是tomcat6 弹出了一个服务编辑框神马的,然后再在“属性”框上选择 Java 标签,打开后有很多配置的选项,神马都是浮云,不用理会 直接找到Java Options文本输入区域,然后在里头添加如下两行: -Xdebug -Xrunjdwp:transport=dt_socket,address=9999,server=y,suspend=n 如果你只想在本机进行tomcat的调试,那么将address=9999的9999替换成127.0.0.1:9999,这样你就只能在本机调试了,上述的方法是保证你在任何能访问到的机器上都能调试你的tomcat的配置哦,小心点为妙。 点击”Apply”,然后点击”OK”关闭对话框 重启你的Tomcat服务器,然后检查一下,是不是发现9999端口监听了呢?通过netstat -an命令何以查看哦。 使用IDE通过9999端口连接到Tomcat,开始你的远程调试之旅吧 如果你已经使用了Eclipse,你可以在Remote Debugging with Eclipse获得更多信息。

软件开发计划

1、后台服务 通讯服务器与客服系统分离,通讯服务器可将服务发到客服服务器,一台客服服务器也能接收多个通讯服务器的服务。 通讯服务器分布式,集群化。可集群控制。 后台服务器更新自动化。 服务业务的可扩展化。 服务路由化。 2、客服系统。 服务统计。 2、医生系统 模板判断系统。 3、统计系统 1、能按月,日,年、周统计出服务人次,服务时间。 2、能对有关热点问题进行统计。 4、计费系统 5、后台服务API 6、考核系统。随机抽样,对服务样本事后检验。 加强对公司业务的支持,不断用计算机技术解决公司业务层面的问题,提高业务效率。(如:打印模板,统计多参数系统使用率,统计坐席业务量,统计用户使用时间表,使用我们系统测量心电时,测量出现问题统计) 公司产品平台的统一化,业务平台的统一化, 我们是否尝试网络营销,我们是否要创建电子商务,我们是否要打造健康论坛。 我们是否要简化登录,支持账号与设备的分离。 我们能做什么,我们还有多少事要做,