做好产品
——《Facebook效应》读后
全书概括
这一阵子匆匆看完了一遍大卫的《Facebook效应》,从书中我们可以一窥Facebook这个传奇产品的启动和发展历程。整本书差不多是按照时间顺序来展开的,书的架构大致可以分为:
前面十一章主要记叙了Facebook这个产品和公司从无到有的发展历程;
十二、十三两章主要描述Facebook最大的盈利来源广告的来历及其广告形式;
十四、十五章重点描述了Facebook的全球化和馈赠型经济理念;
十六章概述了2009年一年中Facebook的发展;
十七章重点描述了马克和Facebook贯穿始终的产品理念。
以上划分仅仅是我粗读一遍的小结,本非大卫的官方说法,为此仅供参考。
产品的三条线
阅读这本书,可以让我这个从来没有用过Facebook的人大致了解Facebook公司和产品的三条线:产品线、运营线和融资线(价值线)。
产品线脉络
2004年2月4日第一次上线产品,功能仅包含照片、简介,添加好友和捅人;
2004年9月,增加留言墙和群组功能;
2005年夏,开放高中生注册,但独立运营,与大学部不互通;
2005年11月左右,上线图片功能;
2006年2月,高中部和大学部互联;
2006年5月,职场网络首次登场,遭遇冷场;
2006年9月5日,上线动态新闻和涂鸦墙;
2006年9月26日,面向全世界开放注册;
2007年5月24日F8大会的举办,意味着开放平台的上线(6个月后,注册的开发者达到25万,运行的应用程序达到了2万5千个);
2007年11月6日,灯塔和自助在线广告项目上线;
2008年末,facebook联谊会启动;
2009年3月,开始仿Twitter;
2009年4月,开放信息流API(stream API),意味着Facebook的发展,将逐渐走出Facebook网站(按我的理解,已经上升到产品精神阶段)。
运营线脉络
2004年2月4日在哈佛上线,其后逐步分批地在常春藤院校中铺开,在第一批用户的发展上做得相当成功(产品运营6月后,刚满20岁的马克,就有风投愿意估值1000万美元,不过也只是意向而已);
2004年,在面临众多校园同类产品时,马克采用了“包围策略”;
2004年11月30日,运营10个月后迎来百万注册用户;
2004年12月,苹果公司折扣推广广告活动,为公司初期带来了稳定的大额营收;
2005年夏,公司总裁帕克被解职,意味着Facebook的运营将走向更加职业化(这是我自己的理解);
2005年10月,注册用户达到500万;
2005年,达到500万之后几个星期,团队从用户们频繁更改简介图片,引发图片功能上线(起初扎克伯格并未同意,但最终被说服,到2009年末,facebook拥有300亿张图片,成为拥有最多图片的网站);
2006年9月5日上线动态新闻和涂鸦墙(图片功能的成功,让他们挖掘成功的原因,借鉴RSS,构建了全新的动态新闻,不过后来遭遇了危机,甚至游行,扎克伯格等人看到了动态新闻的轰动效应,更加坚定了动态新闻的正确性,并且48小时内攻坚增加了隐私控制功能以让用户设定哪些动态可以发布出去);
2006年9月26日,在平息动态新闻后,开放注册,意味着对除了大学、高中和职场后,面向全社会开放;
2006年10月,每日注册数达5万,注册用户达到了1000万(12月份,活跃用户为1200万);
2007年5月24日,活跃用户达2400万,每天有15万人加入(一年后,这个数据达到了7000万);
2007年11月6日上线的灯塔灾难,没有被及时处理,直到11月29日才改进产品,但显然为时已晚了,该事件最终导致首席运营官在2008年3月由谢丽尔替代范,这一高管变动,使Facebook走上了强劲的广告和盈利之路,并一举迈入了;
2008年8月,活跃用户达1.45亿;
2009年2月份的新服务条款事件,从起初的反对到最后演变成赞许;
2009年8月,FaceBook花5000万美元收购Friendfeed(一家由google前明星级程序员员工开发的产品);同时当月的活跃用户达到2.75亿;
2010年7月,活跃用户数为5亿。
融资(价值)线脉络
起初学院派的融资,马克和萨维林各投入1万美元运作,2004年夏由于萨维林的冻结账户,马克后来借债自掏腰包近10万用于运作;
2004年秋季开学前,被泰尔等估值500万美元,泰尔等人注资60万美元;
2005年5月前后,被阿克塞尔合伙公司估值1亿美元,吉姆.布雷耶联合公司投资1370万美元;
2006年4月,被估值5亿美元,接受了2750万美元的第三轮投资,从而缓解了当时五六月份被维亚康姆8亿美元收购的可能,不过由于进军职场网络的失意,在10亿美元售出的决定中摇摆,很可惜雅虎在马克等人犹豫不决时没有一鼓作气拿下);
2007年10月24日,被估值150亿美元,微软投入2.4亿美元购得1.6%股份,李嘉诚投入6000万美元购得0.4%股权,后来,李嘉诚追加了6000万美元的投资,再加上其他的投资,这一轮融资达到3.75亿美元,为Facebook后来迎接08年开始的次贷危机引发的经济萧条中积累了大量资源。
书中咸少提到盈利,一方面Facebook前期发展还是主要靠融资而非盈利,二来我觉得融资和盈利都是反应产品价值线方向的东西,所以就没做梳理。另外由于成书在2010年,所以Facebook飞速发展的最近两年都没有囊括进去,我也比较懒,不想去详细追求一些近来的数据追加到这三条线的后面,不过从书中所叙述,已经详尽地展示了Facebook从无到有,从小到大的发展历程,对于我们了解Facebook的产品发展,已经足够了。
小品
通过这本书,让我感悟到产品与产品经理的一些东西:
Facebook这个产品的成功,跟其一直有马克主导有关系,相比较与其它很多大公司收购一些成功的产品,最后都走向衰退甚至销声匿迹(诸如雅虎曾经收购的很多新颖的产品,以及本书中提到的MySpace的收购)。产品的主将或者产品主将的思想或者梦想很重要,自己从事软件开发也有很长一段时间了,接触过很多产品的开发,特别是在网易的经历,让我更加深信一个好的产品,必须要有一个一以贯之的理念或思想,否则很容易跑题或者成为一个四不像,一个起初很不错的最小产品,最后都会迭代成一个用户压根不想用的臃肿产品。
产品的立意要明确而深远,产品的执行要循序渐进。Facebook起初的功能很少,很多同类产品可能做的比Facebook还要早还要好,但是Facebook之所以能够超越别的产品,后来者居上,与马克坚持的“做最好的、最简单的、让用户以最方便的方式分享信息的产品”的明确立意和Facebook一步一步扎实地在大学院校扩展到全世界有着必然的关系。作为一个人呢难免会动摇意志,特别是在遭遇困难和利诱时,Facebook就曾经差点被卖给雅虎,当时具有长远梦想的马克也屈服了,但是命运之神还是眷顾马克,让彼时的雅虎遭遇了下挫折,如果那个时候Facebook真的卖给了雅虎,或许现在的Facebook就没有那么成功了。另外马克还有一个梦想,就是期许Facebook能够达到“教人从善”和“让FaceBook成为使世界变得更小的工具”,这个梦想远远超过了其盈利和开办一家企业的动力,成为Facebook这个产品生生不息的产品精神。
本书十二、十三两章,重点描述谢丽尔加入Facebook后在广告方面的方案,这两章提到了很多诸如条幅广告、对接式广告、交互式广告、满足需求的广告、产生需求的广告和在线自助广告等等广告概念的提出,让我这个同样厌恶广告的人,也对广告产生了兴趣,让我相信其实广告作为一种信息也是可以达到双赢的效果,而并非以前看电视剧时那么反感广告了(但是很多广告方面的知识真的匮乏,希望有人能够指点一下,或者介绍些资料)。
本书虽然并非小说,但是让人看起来就像是看小说一样,很多人物,诸如马克、莫斯科维茨、肖恩、吉姆和泰尔等等人物都很丰满,对我留下深刻影响的还是肖恩,作为一个争议人士,正是由于他的杰出工作,帮助马克他们团队真正从小打小闹,迈向了企业化进程,肖恩这个角色扮演的绝对是Facebook发展历程中使得校园产品脱颖而出的基石。
目前就暂时小品到这里吧,这本书还是相当不错的,感谢火花网提供电子版本。
posted @
2012-05-28 02:33 frank.sunny 阅读(578) |
评论 (0) |
编辑 收藏
Part One
原文路径:http://devmaze.wordpress.com/2011/01/18/using-com-android-internal-part-1-introduction/
Android有两种类型的API是不能经由SDK访问的。
第一种是位于com.android.internal包中的API。我将称之为internal API。第二种API类型是一系列被标记为@hide属性的类和方法。从严格意义上来讲,这不是一个单一的API,而是一组小的被隐藏的API,但我仍将其假设为一种API,并称之为hidden API。
Hidden API 例子
你可以查看一下android的源码,并能找到一些变量、函数和类等,都被@hide属性标记了。
下面的例子就是在WifiManager(API 10源码)中隐藏的变量。
![image_thumb13_thumb](file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtml1/01/clip_image002.gif)
另一个例子是在WifiManager(API 10源码)中隐藏了setWifiApEnabled函数。
![image_thumb15_thumb](file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtml1/01/clip_image004.gif)
因此,只要你看到@hide属性,那你看到的就是hidden API。
Internal和hidden API的区别
Hidden API之所以被隐藏,是想阻止开发者使用SDK中那些未完成或不稳定的部分(接口或架构)。举个例子,Bluetooth API在API 5(Android
2.0)上才开放;在API 3 和4上都是用@hide属性隐藏了。当这些API被验证和清理后,Google的开发者会移除@hide属性,并让其在API 5官方化。很多地方在API 4 和5之间发生了变化。如果你的程序依赖某些隐藏的API,当其部署到新的平台上时,就有可能陷入困境。
对于internal API来说,从来都没有计划将其开放出来。它就是Android的“内部厨房”,对开发者来说,应该将其视作黑盒。凡事都会有变化的。如果你依赖某些internal
API,也有可能在新的Android release上,这些internal API发生变化,从而令你失望。
总结一下区别:
Hidden API = 进行中的工作;
Internal API = 黑盒;
Internal和hidden API的编译时 vs. 运行时
当你使用Android SDK进行开发的时候,你引用了一个非常重要的jar文件——android.jar。它位于Android
SDK平台的文件夹中(SDK_DIR/platforms/platform-X/android.jar,其中,X表示API等级)。这个android.jar移掉了com.android.internal包中所有的类,也移掉了所有标记有@hide的类,枚举,字段和方法。
但当你在设备上启动应用程序时,它将加载framework.jar(简单来说,它和android.jar等同),而其未移掉internal
API和hidden API。(但它对开发者来说,并不能友好地访问,因此,我将向大家展示不通过反射如何使用这些API)。
关于internal API,还有一件事需要说明。Eclipse的ADT插件增加了一个额外的规则,那就是禁止使用com.android.internal包中的任何东西。所以,即便是我们可以拿到最原始的android.jar(未删减版),也没有轻松的办法通过Eclipse使用这些internal API。
你可以亲自检查一下。创建一个新的Android工程(或者使用已有的)。查看一下它引用的类库(右击project Properties
–> Java Build Path –> Libraries)。
![image_thumb30_thumb](file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtml1/01/clip_image006.gif)
![image_thumb29_thumb](file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtml1/01/clip_image008.gif)
重要的总结:internal和hidden API在SDK中是按照一样的方式处理的(都从android.jar中移除了),但internal API更惨的是,还被Eclipse的ADT插件显式禁止了。
不通过反射使用internal和hidden API
这些文章的终极目标是让开发者能够不通过反射使用Internal和Hidden API。如果你完成了接下来部分中描述的步骤,你将能使用这些Internal和Hidden API,如同公开的API。你不再需要使用反射。
注:如果你正在使用这些非公开的API,你必须知道,你的程序有着极大的风险。基本上,无法保证在下一次的Android OS更新时,这些API不被破坏,也无法保证不同的运营商有着一致的行为。你自己决定吧。
接下来有三个场景:
1. Internal 和hidden
API都可用(场景A)
2. 只Hidden
API可用(场景B)
3. 只Internal
API可用(场景C)
场景A是B、C的总和。场景B是最简单的一个(不需要对Eclipse的ADT修改)。
场景A:阅读Part1, 2, 3, 4, 5
场景B:阅读Part1, 2, 3, 5
场景C:阅读Part1, 2, 3, 4, 5
Part 2
原文路径:http://devmaze.wordpress.com/2011/01/18/using-com-android-internal-part-2-hacking-around/
在上一篇中,我解释了为什么我们不通过反射就会很难使用internal和hidden API。这是因为android.jar中就没包含这些API,因此,没人能够在编译时引用这些类。
这篇文章将描述如何还原最初的android.jar。这将允许我们像使用公开的API那样使用internal和hidden API。
如何得到原版android.jar?
我们需要修改android.jar,这样它才能包含所有的*.class文件(包括internal和hidden API类)。有两种办法:
1) Android是一个开源工程。我们可以下载源码并搭建编译环境,这样它就不能移除那些internal和hidden的类了。这个办法比较困难;
2) 每个模拟器或真机在运行时都会有一个等同android.jar的东西。我们可以从这里拿到jar文件,提取出原始的.class文件,并拷贝到Android SDK的android.jar中。
我将采用方案2。它易于开始,还不需要搭建Linux环境及编译环境等。
从设备上获取framework.jar
你可以使用命令行(adb pull)从模拟器或设备上下载文件,或者使用DDMS(借助Eclipse或SDK中的应用)。
注意:模拟器通常在.dex文件中包含代码,而真机一般在优化版的dex文件中包含代码——odex文件。操作odex文件比较困难,这也是为什么我选择模拟器的原因。
与Android SDK中的android.jar等同的文件是framework.jar。这个文件位于设备的:/system/framework/framework.jar
adb pull /system/framework/framework.jar
当framework.jar从设备上下下来之后,重命名为framework.zip并解压到独立的文件夹中,看起来是这个样子的:
classes.dex正是我们需要的。
创建framework-classes.zip
首先,我们需要把.dex文件转换成.jar格式。你可以使用通用的工具dex2jar。只需要运行:
dev2jar classes.dex
当转换结束时,你应该得到了classes.dex.dex2jar.jar文件。重命名为framework-classes.zip。使用zip查看器,进入到framework-classes.zip/com/android/internal/:
![image](file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtml1/01/clip_image012.gif)
恭喜你,你已经拥有了所有的.class文件,包括internal和hidden
API(尽管截图只确认了internal部分)。
创建original-android.jar
Android SDK的android.jar位于ANDROID_SDK/platforms/android-X/android.jar(X表示API等级)。
拷贝android.jar成custom-android.jar。解压至custom-android文件夹。将framework-classes.zip中所有的.class文件拷贝到custom-android文件夹中(你需要覆盖所有已经存在的.class文件)。
然后,压缩custom-android文件成original-android.zip。重命名为original-android.jar。
步骤总结
1. 选择你的目标平台X
2. 创建目标平台X的模拟器
3. 启动模拟器,下载/system/framework/framework.jar
4. 重命名framework.jar
-> framework.zip
5. 从framework.zip中抽取classes.dex
6. 使用dex2jar工具,将其转换成classes.jar
7. 重命名classes.jar
-> framework-classes.zip
8. 拷贝android.jar –> custom-android.zip
9. 解压custom-android.zip至custom-android文件夹
10. 将framework-classes.zip中所有文件拷贝至custom-android文件夹(覆盖存在的文件)
11. 压缩custom-android文件夹成original-android.zip
12. 重命名original-android.zip
-> original-android.jar
打完收功。
总结
我们还原了android.jar,使其包含所有的internal和hidden
API的.class文件。这只是第一步。下一步将创建定制的android平台,使其使用未删节版的android.jar,并将其添加到Android SDK platforms文件夹中。
Part 3
原文路径:http://devmaze.wordpress.com/2011/01/18/using-com-android-internal-part-3-custom-android-platform/
在上一篇中,我已经展示了如何创建一个包含所有internal和hidden API的original-android.jar。
接下来的工作就是要修改已经存在的Android平台(SDK_DIR/platforms/platform-X/android.jar,X表示API等级)。你可以直接使用Part2中创建的original-android.jar替换android.jar。但这样的话,你的所有工程都将直接使用internal和hidden API而没有任何限制。这不够方便,因为在多数的工程中你不希望这样。甚至,你可能更希望禁止这些API(ADT/android.jar的默认行为)。但对于一些特定的工程,你希望能够使用这些internal和hidden API。
为了达到这样的灵活性,你需要创建一个新的自定义的Android平台。当不需要访问internal和hidden
API时,你只需使用原有的Android平台。当你使用这些API时,你使用自定义的Android平台。
Android SDK文件夹结构
让我们看一下Android
SDK树是如何组织的:
![image](file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtml1/01/clip_image014.gif)
我们需要“platforms”文件夹。看一下里面:
![image](file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtml1/01/clip_image016.gif)
这里列出了支持的Android平台。
现在,我们看一下它是如何与Eclipse设定关联的。选择你的工程,右击–> Properties –> Android。你将会看到一组支持的Android平台(与…/platforms/folder相似)。下面是截图:
![image](file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtml1/01/clip_image018.gif)
创建新的平台
为了创建一个新的平台,我们需要拷贝android-9文件夹 -> android-9-internals。让我们做一些修正:
1. 删除其中的android.jar
2. 拷贝original-android.jar,并改名为android.jar
3. 修改build.prop文件:
…
ro.build.version.sdk=9 -> ro.build.version.sdk=-9
…
ro.build.version.release=2.3 ->
ro.build.version.release=2.3.extended
…
重启Eclipse。并确认你能看到新的平台。下面是我所看到的:
![image](file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtml1/01/clip_image020.gif)
为什么我选择API等级为-9?这是因为它必须是一个数字,而且它不能是9(或者其它已经存在的API等级)。否则,你自定义的平台将不能被使用(它在列表里可见,但选中后也不能正常工作,编译时仍然使用相应API等级的原始平台)。
下面是引用类库的截图(当前工程选中了自定义的平台):
![image](file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtml1/01/clip_image022.gif)
总结
在上一篇中,我已经告诉你如何创建一个未删节版的android.jar。在这一篇中,我向你展示了如何创建一个自定义的Android平台,并在其中使用original-android.jar。这对于hidden API来说已经足够了。但对于internal
API来说,还需要另一步。这是因为ADT仍然不允许使用com.android.internal包中的类(参见上图中的“forbidden”访问规则)。下一节我将向你展示如何定制ADT来允许使用internal包中的类。
============华丽的分割线=============
在实际的操作过程中,我创建的自定义的android.jar(API 10)不能被Eclipse成功加载,会出现以下的错误框,如同网站上其它人操作的结果一样,期待解决方案。
![clip_image012](file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtml1/01/clip_image023.jpg)
不过,作者提供了可用的自定义的android.jar,如果不想自己尝试的话,可以直接从网站下载,地址将在Part5中给出,稍等。
Part 4
原文路径:http://devmaze.wordpress.com/2011/01/18/using-com-android-internal-part-4-customizing-adt/
在上一篇文章里,我描述了如何创建一个自定义的original-android.jar,以及如何创建一个自定义的Android平台来使用这个original-android.jar。这对Hidden API来说足够了。但对Internal
API来说,仍然还有一个包袱:Eclipse的ADT插件。它限制使用com.android.internal包中的任何类。
![](file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtml1/01/clip_image025.gif)
有几种方法可以解决这个访问限制。
1) ADT源码可以下载。因此,删除/修改代码中的某些代码,从而编译出一个新的ADT是可以的。麻烦的是你需要搭建64位Linux系统,下载源码,编译等。它需要花费一些时间。当有新的ADT版本时,你需要重来一遍。
2) 另外的方法就是修改ADT的字节码。用一个类似于“com/android/internax/**”的字符串替换“com/android/internal/**”。
第二种方法可以用脚本实现。并且不需要访问源码以及可在Windows上操作。这也是为什么我在这篇中选用第二种解决方案的原因。
修改ADT的字节码
进入Eclipse的plugins文件夹。找到文件名看起来像“com.android.ide.eclipse.adt_*.jar”的文件。备份一下这个文件(以防中间有错误发生)。并拷贝这个文件到一个“experimental”文件夹,在这里,我们要完成对其字节码的修改。
重命名*.jar为*.zip。解压这个文件到单独的文件夹。参看以下图片:
![image](file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtml1/01/clip_image027.gif)
现在,进入到com/android/ide/eclipse/adt/internal/project子文件夹。
找到AndroidClasspathContainerInitializer.class文件。
![image](file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtml1/01/clip_image029.gif)
这个文件包含“com/android/internal/**”字符串。接下来就是要替换这个字符串,例如“com/android/internax/**”。改变字符串的长度理论上是安全的,但最好还是替换其中的一个字母,并保持长度一致。
我使用notepad++修改的,它支持非可印刷字符,因此在对其修改时,不要触碰修改非可印刷字符。
![image](file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtml1/01/clip_image031.gif)
当做完这个,保存文件。压缩这个文件夹,保证文件名与原始文件一模一样。在我这里,文件名是:com.android.ide.eclipse.adt_8.0.1.v201012062107-82219.zip。
注意:确保压缩文件的正确性。比较原始文件和修改文件的根文件结构。
现在,用修改后的版本替换原来的ADT的*.jar文件。然后,启动Eclipse。
在使用库窗口,你应该看到下面的样子,一切都变得那么的美好:
![image](file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtml1/01/clip_image033.gif)
步骤总结
1. 关闭Eclipse
2. 从Eclipse的plugin文件夹中拷贝出ADT插件的jar文件
3. 重命名.jar
-> .zip,然后解压至独立的文件夹
4. 找到com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerInitializer.class文件
5. 用“com/android/internax/**”替换“com/android/internal/**”
6. 压缩这个文件夹
7. 重命名 .zip
-> .jar
8. 用修改后的jar替换原始的ADT jar文件
9. 启动Eclipse
结论
这是不使用反射也能使用Internal
API的最后一步。
Part 5
原文路径:https://devmaze.wordpress.com/2011/01/19/using-com-android-internal-part-5-summary-and-example/
为了能够使用Internal和Hidden API,你需要:
1. 创建自定义的original-android.jar,包含所有的.class文件
2. 创建自定义的Android平台来使用original-android.jar
3. 修改ADT插件,允许使用com.android.internal包(只为Internal API)
4. 创建新的工程,引用自定义的Android平台(本文中的例子)
在本文中,我将向你们展示如何使用那些Internal和Hidden API。
此外,在本文的结尾,我列出了一些自定义的Android平台,它们都包含Internal和Hidden
API。我附带了它们,是为了可能你不想花太多时间在这方面,但又想快速的尝试什么。
例子
创建一个新工程,选择2.3.extender平台:
![](file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtml1/01/clip_image035.jpg)
下面是代码:
![](file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtml1/01/clip_image037.jpg)
这个代码使用了Internal
API(PowerProfile)和Hidden API(isWifiApEnabled)。我不用使用反射就能编译并运行这些代码。
自定义平台
下面有些平台,是我为自己创建的。只用拷贝它们到SDK_DIR\platforms文件夹下。这只是让Hidden API可用。对于Internal
API,你需要修改你的ADT插件。
API 3:http://www.megaupload.com/?d=S1F2MKYZ
API 4:http://www.megaupload.com/?d=VUCTRI3Y
API 7:http://www.megaupload.com/?d=7ITNILBK
API 8:http://www.megaupload.com/?d=EXT5FKKT
API 9:http://www.megaupload.com/?d=EXT5FKKT
API 10:http://www.megaupload.com/?d=FCV78A9M
==============华丽的分割线=============
我尝试了其中的几个自定义平台,发现,internal 和hidden API真的是可用了,但也有一些意外的问题,如AlertDialog.Builder(Context
context)居然说Context参数是多余的。。
没花时间去研究为什么会这样,如果哪位童鞋知道原因,告诉我哈~~
@import url(http://www.cppblog.com/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);
posted @
2011-09-26 17:52 frank.sunny 阅读(3244) |
评论 (3) |
编辑 收藏
完整文档下载地址http://www.cppblog.com/Files/franksunny/RNotifier.7z
如何在S60非UI框架程序中弹提示信息
在非依赖于UI的S60程序中,也就是不建立在控件环境基础上的程序,比如控制台应用程序,独立的线程等。在这些程序中需要弹提示信息的时候,就不能直接用基于CCoeControl的任何UI类了,因为这些程序中没有供养CCoeControl生存的CCoeEnv环境,当然不嫌繁琐,在程序的main函数中像自己创建活动规划器一样去创建CCoeEnv环境也是一个可行的方法,但是这超出本文的涉及范围,本文提出的是不创建CCoeEnv环境情况下,通过RNotifier或RNotifier的派生类来实现弹提示信息。
RNotifier简单应用
其实RNotifier和RFs一样都是派生自RSessionBase,所以使用起来也是类似的,下面给出一个最简单的例子代码
RNotifier vNotifier;
User::LeaveIfError(vNotifier.Connect());
CleanupClosePushL(vNotifier);
//title and context
TBuf<256> title;
TBuf<256> context;
title.Copy(_L("info"));
context.Copy(_L("data"));
// Button text
_LIT(KYesButton, "Yes");
_LIT(KNoButton, "No");
// Display the dialog
TInt button;
TRequestStatus status;
vNotifier.Notify(title, context, KYesButton, KNoButton, button, status);
User::WaitForRequest(status);
// destroy notifier
CleanupStack::PopAndDestroy();
运行上述代码可以得到如下的对话框提示
RNotifier本身和RFs是基于Symbian OS的,而非专属于S60平台的,所以在UIQ等平台上继续可以使用RNotifier,这在跨平台开发上相当的便利,省去了移植的苦恼。
RNotify复杂应用
上面例子代码是最简单的一种RNotifier的应用,为了开发的方便和提高开发效率,S60封装了一套CAknGlobal*和RAknKeyLock等的类供第三方开发者使用,由于在UIQ平台上我没有涉及过,而且目前借助S60的开源代码,我就拿一个S60中的相关类CAknGlobalConfirmationQuery来说明下吧,在源代码sf\mw\classicui\uifw\AvKon\notifsrc路径下面有多个类似类的源代码。其实CAknGlobalConfirmationQuery除了二阶段构造外,最主要的就是ShowConfirmationQueryL、UpdateConfirmationQuery、CancelConfirmationQuery三个函数,这三个函数的代码罗列如下
/**
* Shows global Confirmation query synchronously
*
* @param aStatus TRequestStatus which will be completed when user
* selects one item from the list query.
* @param aPrompt Prompt text
* @param aSoftkeys Softkey resource
* @param aAnimation Animation resource
* @param aTone Tone id
* @param aDismissWithAllKeys If set ETrue the query gets dismissed with all
* keypresses
*/
EXPORT_C void CAknGlobalConfirmationQuery::ShowConfirmationQueryL(
TRequestStatus& aStatus,
const TDesC& aPrompt,
TInt aSoftkeys,
TInt aAnimation,
const TDesC& aImageFile,
TInt aImageId,
TInt aImageMaskId,
CAknQueryDialog::TTone aTone,
TBool aDismissWithAllKeys )
{
delete iBuffer;
iBuffer = NULL;
iBuffer = CBufFlat::NewL(KBufferGranularity);
RBufWriteStream bufStream;
bufStream.Open(*iBuffer);
CleanupClosePushL(bufStream);
bufStream.WriteInt32L(KAKNNOTIFIERSIGNATURE);
if ( aDismissWithAllKeys )
{
bufStream.WriteInt8L( ETrue );
}
else
{
bufStream.WriteInt8L( EFalse );
}
bufStream.WriteInt32L(aSoftkeys);
bufStream.WriteInt32L(aAnimation);
bufStream.WriteInt16L(aImageId);
bufStream.WriteInt16L(aImageMaskId);
bufStream.WriteInt16L(aTone);
bufStream.WriteInt16L(aPrompt.Length());
bufStream << aPrompt;
bufStream.WriteInt16L(aImageFile.Length());
if (aImageFile.Length())
{
bufStream << aImageFile;
}
bufStream.WriteInt32L(iSkinsMajorId);
bufStream.WriteInt32L(iSkinsMinorId);
if (iAknSDData)
{
bufStream.WriteInt8L(ETrue);
bufStream << *iAknSDData;
}
else
{
bufStream.WriteInt8L(EFalse);
}
iBufferPtr.Set(iBuffer->Ptr(0));
iNotify.StartNotifierAndGetResponse(aStatus, KAknGlobalConfirmationQueryUid,
iBufferPtr, iResultBuf);
CleanupStack::PopAndDestroy(); // bufStream
}
该函数用于显示对话框。其主要的实现就是调用RNotifier的StartNotifierAndGetResponse函数。
EXPORT_C void CAknGlobalConfirmationQuery::UpdateConfirmationQuery( TInt aSoftkeys )
{
iSoftkeys = aSoftkeys;
iCmd = EAknUpdateGlobalQuery;
TPckgBuf<SAknNotifierPackage<SAknGlobalMsgQueryParams> > pckg;
pckg().iParamData.iCmd = iCmd;
pckg().iParamData.iSoftkeys = iSoftkeys;
TPckgBuf<TInt> ret;
iNotify.UpdateNotifier( KAknGlobalConfirmationQueryUid, pckg, ret);
}
该函数用于对话框产生后更新对话框,其功能就是使用函数RNotifier::UpdateNotifier。
EXPORT_C void CAknGlobalConfirmationQuery::CancelConfirmationQuery()
{
if (iBuffer)
{
iNotify.CancelNotifier(KAknGlobalConfirmationQueryUid);
delete iBuffer;
iBuffer = 0;
}
}
该函数用于对话框产生后程序取消对话框,其功能就是使用函数RNotifier::CancelNotifier。
RNotifier的实现跟踪
通过以上两个代码,我们差不多对RNotifier类的使用了解了,但是这个RNotifier到底是如何实现弹出一个对话框呢?
其实RNotifier的真正实现是通过Symbian OS的C/S架构来实现的,这个在文章开篇提到RNotifier和RFs一样派生自RSessionBase就已经埋下了伏笔。
RNotifier的源代码实现位于sf\os\kernelhwsrv\kernel\eka\euser\us_ksvr.cpp,这个代码中还有RChunk、RDevice和RHandleBase等等基础类的实现代码。
RNotifier的服务器类CNotifierServer和服务器会话通道类CNotifierSession以及相关的其他类则位于sf\os\kernelhwsrv\kernel\eka\ewsrv\ws_main.cpp中。这些类的声明则位于sf\os\kernelhwsrv\kernel\eka\include\twintnotifier.h文件中。
再深入进去,就会了解到RConsole类,这个类的声明位于sf\os\kernelhwsrv\kernel\eka\include\e32twin.h中,代码实现位于sf\os\kernelhwsrv\kernel\eka\ewsrv\co_cli.cpp中。搞了半天又遇到一个C/S架构,这个Client的Server是CWsServer,其通道为CWsSession,在CWsSession内最主要的类是CWsWindow,这几个类的声明位于sf\os\kernelhwsrv\kernel\eka\include\ws_std.h,而这几个类的实现代码则又绕回到sf\os\kernelhwsrv\kernel\eka\ewsrv\ws_main.cp中去了。
好了,自己暂时只能走到这一步了,上面只是简单给出一些源代码的路径,有兴趣的同学可以去一探究竟,我才疏学浅就只能点到为止了。
欢迎对其有更深入挖掘的同学能够发布新的小结,到时记得分享到我的邮箱frank.sunny@163.com,当然假如我文中有什么错误也希望能够告知我一下,谢谢。
posted @
2010-12-17 21:26 frank.sunny 阅读(1932) |
评论 (0) |
编辑 收藏
关于监控摄像头拍照与摄像
由于工作中需要用到类似于像新浪微薄一样,监控拍照后弹出照片是否上传分享的要求,为此就小试了下监控拍照和摄像。
一开始没有头绪,都不知道搜索什么关键字,茫无目的下居然发现论坛有人推荐陈子腾写的wiki,具体wiki链接如下
检测内置相机应用程序新拍摄的照片和视频片段
其实参考陈子腾的方法很容易就做好一个监控功能了,在这里就不多说。
之所以想小写下博文,是因为这种方式实际上涉及到Symbian OS提供的Publish&Subscribe这一特殊的进程间通信机制,我之前使用的进程间通信除了C/S和RMsgQue之外,就是使用AppUi框架通过TApaTask::SendMessage的方法来实现,至于RProcess::SetParameter不能在进程间实时的传输消息,只能是开启进程时传递一些信息(比如同步用的信号量等)。这次总算是接触了下PS进程间通信,就自己也尝试了这种方式。
SDK中的描述是
Publish & Subscribe is a new API provided by the real-time kernel (EKA2). It allows publisher processes to define and update a set of properties; other processes, called subscribers, can listen for changes to a property, and get property values. The process that defines a property can specify access rights for both reading and writing. Rights can be defined in terms of either requiring a particular security capability, by a process SID, or by a process VID. Publish & Subscribe replaces System Agent and the usage of temporary Shared Data keys.
也就是说发布者定义或更新一套属性,然后订阅者开启监听的情况下就能接受到更新,然后可以去获取属性值的更改。
定义属性
在这里最主要的是在发布者定义属性时,一定要用发布者程序的SID也就是UID3,否则会报-46的错误,也即下面代码
RProperty::Define(KPSUidCameraCfg, KCameraCfgModify, RProperty::EInt);
KPSUidCameraCfg必须是你发布程序的UID3或者你另外在mmp中定义的SID值,至于后面的KCameraCfgModify属性和类型值就根据要求来设置了。
监控属性
监控属性需要绑定到具体的属性然后开启一个Subscribe的异步方法
iProperty.Attach(KPSUidCameraCfg, KCameraCfgModify);
iProperty.Subscribe(iStatus);
SetActive(); // Tell scheduler a request is active
通常监控属性是一个异步过程,所以我们会为其专门写一个活动对象类,用以异步监控
修改属性
虽然属性定义是有安全性要求,但是更新属性,就没那么严格了,可以直接通过RProperty的静态方法来修改
RProperty::Set(KPSUidCameraCfg, KCameraCfgModify, 1 );
读取属性
订阅者当收到属性有更改时,也可以直接通过RProperty的静态方法来读取
RProperty::Get(KPSUidCameraCfg, KCameraCfgModify, val);
删除属性
由于属性值在手机重启前会一直存在,所以属性没有用时,我们要求将其删除,删除也可以通过RProperty的静态方法简单实现,具体如下
RProperty::Delete(KPSUidCameraCfg, KCameraCfgModify);
结合PS这一进程间通讯的方法和系统摄像头应用程序中的使用,我们可以显然知道,该方法适用于开发一些通用的底层控件,可以给第三方开发者需要时监控用,发布者类似于一个广播系统。
感觉不是很复杂,就简单小结如上吧,以后使用遇到问题再更新。
posted @
2010-12-10 19:58 frank.sunny 阅读(3059) |
评论 (1) |
编辑 收藏
摘要: 做为Symbian开源的新平台,Symbian ^3发布已经有一阵子了,N8和C7推向市场也有些日子,但是目前诺基亚基于这个平台的SDK才出到了0.9,可以说还没有出正式版本啊,不过看到好多人都在用新的SDK了,我也小小尝下鲜,将尝鲜结果汇聚在下面,以备后用。
阅读全文
posted @
2010-11-04 15:33 frank.sunny 阅读(2057) |
评论 (0) |
编辑 收藏
摘要: 文档中涉及到贴图,懒得搞,就附带一份word文档吧,需要的可以下下 http://www.cppblog.com/Files/franksunny/camera.rar
摄像头编程预研
目前使用摄像头编程,网上用的最多的都是直接调用手机自带的照相/摄像程序来完成,不过使用这种方式,可控性就显得弱一些,为此近期对直接使用ECAM API进行了下简单预研。
照相流程
因为本次预研主...
阅读全文
posted @
2010-11-03 14:55 frank.sunny 阅读(2753) |
评论 (0) |
编辑 收藏
摘要:
获取手机短信和彩信号码的方法
短信号码的提取
之前写了一篇Symbian端彩信读取初探,将过多的期望放在了未知的CMmsHeaders上面,最近需要将系统收件箱、发件箱和草稿箱内部的数据统统备份出来,突然遇到瓶颈了。最后问题得以解决之后发现其实系统已经提供了丰富的API,以下就是罗列了一种获取系统收件箱、发件箱和草稿箱里面短信和彩信号码的方法。
我们先看一段短信读取号码的代码...
阅读全文
posted @
2010-09-25 17:44 frank.sunny 阅读(3410) |
评论 (3) |
编辑 收藏
摘要:
Symbian端彩信读取初探
上周由于项目需要对彩信读取进行了预研,虽然并未涉及彩信的拦截,只是对Symbian S60手机收件箱中的彩信和彩信通知的内容进行提取并分析,但是也算是趟了下之前一直没有搞过的彩信这浑水,小结一下。为了体现分析过程的逻辑性,下文并非是由简入繁,而是采用由已知到未知的说明过程来进行。
彩信和彩信通知的区别
正如前面博文《Symbian...
阅读全文
posted @
2010-07-28 21:16 frank.sunny 阅读(2527) |
评论 (0) |
编辑 收藏