franksunny的個人技術空間
獲得人生中的成功需要的專注與堅持不懈多過天才與機會。 ——C.W. Wendte |
——《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發展歷程中使得校園產品脫穎而出的基石。
目前就暫時小品到這里吧,這本書還是相當不錯的,感謝火花網提供電子版本。
原文路徑: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源碼)中隱藏的變量。
另一個例子是在WifiManager(API 10源碼)中隱藏了setWifiApEnabled函數。
因此,只要你看到@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)。
重要的總結: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
原文路徑: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/:
恭喜你,你已經擁有了所有的.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文件夾中。
原文路徑: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樹是如何組織的:
我們需要“platforms”文件夾??匆幌吕锩妫?/p>
這里列出了支持的Android平臺。
現在,我們看一下它是如何與Eclipse設定關聯的。選擇你的工程,右擊–> Properties –> Android。你將會看到一組支持的Android平臺(與…/platforms/folder相似)。下面是截圖:
創建新的平臺
為了創建一個新的平臺,我們需要拷貝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。并確認你能看到新的平臺。下面是我所看到的:
為什么我選擇API等級為-9?這是因為它必須是一個數字,而且它不能是9(或者其它已經存在的API等級)。否則,你自定義的平臺將不能被使用(它在列表里可見,但選中后也不能正常工作,編譯時仍然使用相應API等級的原始平臺)。
下面是引用類庫的截圖(當前工程選中了自定義的平臺):
總結
在上一篇中,我已經告訴你如何創建一個未刪節版的android.jar。在這一篇中,我向你展示了如何創建一個自定義的Android平臺,并在其中使用original-android.jar。這對于hidden API來說已經足夠了。但對于internal API來說,還需要另一步。這是因為ADT仍然不允許使用com.android.internal包中的類(參見上圖中的“forbidden”訪問規則)。下一節我將向你展示如何定制ADT來允許使用internal包中的類。
============華麗的分割線=============
在實際的操作過程中,我創建的自定義的android.jar(API 10)不能被Eclipse成功加載,會出現以下的錯誤框,如同網站上其它人操作的結果一樣,期待解決方案。
不過,作者提供了可用的自定義的android.jar,如果不想自己嘗試的話,可以直接從網站下載,地址將在Part5中給出,稍等。
原文路徑: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包中的任何類。
有幾種方法可以解決這個訪問限制。
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。解壓這個文件到單獨的文件夾。參看以下圖片:
現在,進入到com/android/ide/eclipse/adt/internal/project子文件夾。
找到AndroidClasspathContainerInitializer.class文件。
這個文件包含“com/android/internal/**”字符串。接下來就是要替換這個字符串,例如“com/android/internax/**”。改變字符串的長度理論上是安全的,但最好還是替換其中的一個字母,并保持長度一致。
我使用notepad++修改的,它支持非可印刷字符,因此在對其修改時,不要觸碰修改非可印刷字符。
當做完這個,保存文件。壓縮這個文件夾,保證文件名與原始文件一模一樣。在我這里,文件名是:com.android.ide.eclipse.adt_8.0.1.v201012062107-82219.zip。
注意:確保壓縮文件的正確性。比較原始文件和修改文件的根文件結構。
現在,用修改后的版本替換原來的ADT的*.jar文件。然后,啟動Eclipse。
在使用庫窗口,你應該看到下面的樣子,一切都變得那么的美好:
步驟總結
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的最后一步。
原文路徑: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平臺:
下面是代碼:
這個代碼使用了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.shnenglu.com/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);
在非依賴于UI的S60程序中,也就是不建立在控件環境基礎上的程序,比如控制臺應用程序,獨立的線程等。在這些程序中需要彈提示信息的時候,就不能直接用基于CCoeControl的任何UI類了,因為這些程序中沒有供養CCoeControl生存的CCoeEnv環境,當然不嫌繁瑣,在程序的main函數中像自己創建活動規劃器一樣去創建CCoeEnv環境也是一個可行的方法,但是這超出本文的涉及范圍,本文提出的是不創建CCoeEnv環境情況下,通過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,這在跨平臺開發上相當的便利,省去了移植的苦惱。
上面例子代碼是最簡單的一種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.WriteInt
if ( aDismissWithAllKeys )
{
bufStream.WriteInt
}
else
{
bufStream.WriteInt
}
bufStream.WriteInt
bufStream.WriteInt
bufStream.WriteInt
bufStream.WriteInt
bufStream.WriteInt
bufStream.WriteInt
bufStream << aPrompt;
bufStream.WriteInt
if (aImageFile.Length())
{
bufStream << aImageFile;
}
bufStream.WriteInt
bufStream.WriteInt
if (iAknSDData)
{
bufStream.WriteInt
bufStream << *iAknSDData;
}
else
{
bufStream.WriteInt
}
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的真正實現是通過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,當然假如我文中有什么錯誤也希望能夠告知我一下,謝謝。
由于工作中需要用到類似于像新浪微薄一樣,監控拍照后彈出照片是否上傳分享的要求,為此就小試了下監控拍照和攝像。
一開始沒有頭緒,都不知道搜索什么關鍵字,茫無目的下居然發現論壇有人推薦陳子騰寫的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這一進程間通訊的方法和系統攝像頭應用程序中的使用,我們可以顯然知道,該方法適用于開發一些通用的底層控件,可以給第三方開發者需要時監控用,發布者類似于一個廣播系統。
感覺不是很復雜,就簡單小結如上吧,以后使用遇到問題再更新。