2009年12月28日

黑的部落

或許這就是世界,我每天都會這樣想起這樣屬於自己的話。
白天、黑夜!日復一日的天一樣的不停止的模仿自己,然後起落升降那般的毫不自知自己的意義。
或許那就是黑夜跟白晝的宿命,我想每個人都會有像是固定與非固定的宿命。
你就是這樣的需要又不需要的活著! 常常我聽見這個聲音告訴我,或許那是在夢中也或許那是在白天的風裡傳來的。
我就是知道有這樣的聲音存在這世界上。
曾經我認為那種聲音就是讓很多人勇敢的跳下橋,跳下樓的推手。
像是在不知道為甚麼那樣的被推了一把!不怎樣用力,但是卻很足夠,不需伸出腳也不需要伸出手那樣的就像外星人般的心智控制力的玄。

今年已經33歲了!
是阿~不知不覺得體驗了這個世界33年,或許一直以來我就像這樣子不斷的走著、跳著、躺著、翻滾著。
不過事實上來說那些意義都漸漸的消失在這個空間裡頭,如果!當然是說真有空間這樣的事情存在所謂的真實的領域裏頭的話。
我沒有絕望的想自殺的念頭,只是陰暗的影子隱藏在內心與腦袋裡頭罷了!
或多或少就是那樣的想起一些甚麼、感受到一些甚麼,不過卻又不怎樣在乎的甩開然後走一兩步。
睜開眼睛看著鬧鐘,然後爬起床! 是爬而不跳,我也很好奇為甚麼不是習慣跳起床,那樣的話我會覺得這個世界有很多不同正在發生著。
發生著是很重要的,像是你不小心跳進了一個擁有小白點的黑暗方塊裡頭那樣的感覺。
感受到黑暗的刺激與精采但是又看到小白點不斷的被用大拇指給推進去,是按進去吧!那樣的用力!

我是工程師,每天做著得是一種呼吸般的生活。
不是那樣重要的呼吸著,而是不得不那樣的呼吸著,用鼻子也用嘴巴,但是嘴巴少了點!
因為我不怎樣喜歡張著嘴巴活著的過一世紀的動物,雖然我的世界裡頭這樣的動物很多。
有時候會覺得似乎這樣的生活很棒過嗎? 已經記不得了,儘管這份工作給了我不錯的薪水也給了不錯的環境,但是總是缺少了很大的一點些甚麼那樣的感受到不足與無奈。

....................

10個提升新陳代謝的方法



10個提升新陳代謝的方法



你從沒想過新陳代謝的快慢有何差別,但若是以下煩惱你都有

──拼命節食還是瘦不下來﹔

──換了新的保養品,皮膚狀況依舊沒有改善﹔

──體力越來越差,動不動就覺得倦怠

恐怕就得好好想想該怎樣提升新陳代謝的速度了!

常常納悶為什麼飲食和生活作習並沒有改變,可是體重卻一直增加、體力越來越差,甚至連偶爾冒出的痘痘也越來越慢,其實這些潛移默化的改變,有一個重要關鍵便是新陳代謝的速度變慢了。

雖然體質、遺傳及年齡等因素都會影響新陳代謝的快慢,不過天生緩慢的人並不多,多數人都能藉由後天的調整,來達到有效提升新陳代謝的目的,記住以下幾個小訣竅,並落實到你的生活裏,就能輕鬆擁有健康美麗。

◆1.有氧運動是提升代謝最快速的快捷方式。

增加運動的質與量的確是加速新陳代謝最直接快速的方法,並且至少要達到“每週3次、每次30分鐘、運動後每分鐘心跳達130下以上”才能有助於健康。

千萬別小看這短短30分鐘的運動量,它除了可以幫助消耗熱量、減輕體重外,更大的好處是運動之後,能將氧氣帶到全身各部位,大大提升新陳代謝率、有效燃燒脂肪,效果並會持續數個小時之久。所以,丟掉沒時間運動的藉口吧!

日常步行就是最佳的運動良機,不妨利用每天午餐後的休息時間,在公司附近走走逛逛,以均勻的速度步行,不一定要流得滿身大汗,就能提高代謝,同時幫助消化、預防便秘。

◆2.加入重量訓練,增加肌肉組織。

你知道嗎?人體內的肌肉組織越多,越能燃燒更多熱量,使新陳代謝加速,可惜的是,當我們過了30歲之後,肌肉會逐漸流失,進而導致代謝下降,因此,若想維持良好的代謝速度,就必須趕緊鍛鍊,以增加日漸減少的肌肉量。

賀寶芙醫學諮詢委員會及科技研發部資 深副總裁amieMcManus博士表示,對肌肉組織較少的女性來說,舉重這類可以幫助增加肌肉的重量訓練運動,就顯得格外重要,因為增加肌肉數量就能增 加新陳代謝。一旦肌肉量增加了,一天將可以增加消耗100到300卡洛裏,甚至更多。

不必擔心肌力訓練做多了,會練出一身“健美的肌肉”,因為男女的肌肉組織並不相同,況且,健美選手也不是這麼容易就能練成的!

◆3.多喝礦泉水。

基本上,光是多喝水就能促進腸胃蠕 動,並透過流汗或排尿,把體內多餘的毒素和廢物排出來,加速新陳代謝,如果喝的又是優良水源地的礦泉水,對健康的助益則更多,能在喝水同時順帶補充身體所 需礦物質,市面上有些進口礦泉水之所以號稱“窈窕之水”,並非胡亂吹噓,而是因其富含能加速代謝熱量的鎂(Magnesium),多多飲用不僅沒有熱量, 還能加速新陳代謝,可算是一舉兩得。

◆4.停止無效的節食,多攝取蛋白質。

別再漫無目的的節食了,否則你會發現身體越來越糟,體重卻是一動也不動!因為當大腦接收到饑餓的訊息後,為了維持正常身體機能,便會自動調節使新陳代謝的速度變慢,雖然吃得少,但消耗能力同時也變少了,這種方式當然行不通,只會造成身體的傷害。

相反地,改變飲食內容,加強攝取纖維 素與蛋白質,才是提升新陳代謝的安全方式,其中以含豐富蛋白質的藻類是值得推薦,因為藍藻中含有容易消化的蛋白質及高含量的次亞麻油酸,能幫助於提升熱量 的代謝;而綠藻中的葉綠素並能凈化血液,提供絕佳的排毒效果,至於最常食用的海藻類,則富含近30種的礦物質,能促進水分代謝,並加強免疫功能。

◆5.深呼吸。

別懷疑,呼吸的方式的確會對新陳代謝有所影響。呼吸的目的是為了把新鮮的氧氣送進肺部,經由氣體交換後,再把不要的廢物及二氧化碳排出體外,達成凈化血液、促進代謝與迴圈的功效。

可惜大多數的呼吸都是無自主意識下的 反射動作,每次吸氣送進肺部的容量實在有限,而事實上,呼吸頻率越快表示呼吸量越小,好不容易吸進的氧氣,還來不及發揮作用,就又被送出去了,加上短促的 呼氣,更使得廢棄的二氧化碳繼續殘留在肺裏,所以啰!與其做一百次短促的呼吸,不如經常提醒自己做做深呼吸,幫助加速體內廢棄物的代謝。

◆6.捐血好處多,利人又利己。

捐血一袋不僅能救人一命,還可以大大促進自身新陳代謝的能力,不但不傷身,定期捐血反倒也是維持健康的方法之一。

因此,只要你是年滿17歲、體重45公斤以上的熱血女性,歡迎捲起袖子,帶著你的身份證熱情捐血去。

◆7.想喝有味道的飲料,就選能促進代謝的綠茶、杜仲茶。

日本人愛喝綠茶不是沒有道理的,綠茶中的氨基酸、多酚素、維生素等成分,可以幫助降低血脂和膽固醇,並能調節脂肪代謝,除此之外,茶中的咖啡因同時具有利尿和刺激胃液分泌的作用,還可增加肌肉的呼吸速度及工作量,因此多喝不含糖的綠茶有益健康。

而具有利尿作用的杜仲茶,也有順暢血液迴圈、提高新陳代謝、改善體質的功效,經常飲用對於皮膚粗糙、消除浮腫、改善便秘等都有所助益。

另外,每天早晨喝杯醋,也可提高人體新陳代謝,因為含有氨基酸的醋,能促進消耗體內過多脂肪,加強蛋白質和醣類的代謝,因此也有人拿來用在減重方面。

◆8.利用正確的按摩手法也能促進代謝。

體內淋巴液與血液迴圈是否通暢,會影響身體對於廢物、毒素等物質的排除速度,因此透過正確的按摩手法,也能維持血液迴圈的順暢,加速代謝,順利處理體內廢物。

所謂按摩並非隨意壓一壓、捏一捏就可 行了,如果你做過沙龍的塑身療程,不妨仔細回想一下,美容師的按摩手勢是不是都是由末梢往心臟進行?這種從四肢末梢朝心臟方向按摩的「求心性按摩」,對於 推動淋巴及血液的流動才有所助益,能使肌肉的代謝更加旺盛,提供細胞更多促進代謝的營養素與幫助脂肪燃燒的氧氣,同時加速排除廢物,每天看電視的時候順便 做做按摩,輕輕鬆鬆就能更健康。

◆9.洗洗三溫暖、泡泡熱水澡就能變美麗。

泡澡是另一個促進新陳代謝最簡單的方法之一,利用高溫反復入浴的方式,促進血管收縮、擴張,並刺激汗腺發汗,每次泡澡3分鐘,休息5分鐘再入浴的迴圈重復三次,就能在不知不覺中消耗大量能量,效果相當於慢跑1000公尺。

同時,泡澡也能促進老舊角質更新,保 持肌膚光滑細緻。必須注意的是,心臟不好的人並不適合洗三溫暖或常泡熱水澡,此時不妨以傳統的保健良方-熱水泡腳來取代,這不只能使腳部微血管擴張,促進 全身血液迴圈,還可增加細胞通透性,提高新陳代謝,同時達到健身祛痰的作用,並且改善雙腳冰冷的情況。

◆10.補充維他命B群,不要成為夜貓子!

經常熬夜或作息不正常的人不僅老得特 別快,連健康也耗損得特別兇,奉勸你能不熬夜就儘量別熬夜吧!如果是因為工作需要、非不得已的話,就得多多補充富含維他命B群的食物,因為它們是促進人體 新陳代謝的必要因子,卻因屬水溶性維生素而不易儲存在體內。維他命B群通常指的是維他命B1、B2、B6、B12及葉酸、煙鹼酸等,它們在促進新陳代謝、 提供能量、保護神經組織細胞等方面,都有很大的幫助。

如果你三餐正常且不偏食,其實並不需額外補充維生素B群,不過,忙碌的現代人通常都不太注意自己是否吃的均衡,再加上用餐時間不固定,常常導致維生素B群的缺乏,並降低代謝、影響健康,因此,固定補充維生素B群藥丸,不失為是外食族方便省事的辦法。

2009年12月13日

時光

時間是神奇的抽象, 一種虛無但是卻又實際的被感受到的不知名的物品。
當在一個時空裡進入另一個距離下會感受到以前、現在與以後。
感受著也品嘗著,體驗著也淺嘗著那種酸甜苦辣。
辛辣的回憶、甜美的回憶與落寞的感覺混雜著像是一種花式的食物,
沒有味覺但卻飽足。

開著車奔馳在快速道路上感覺到了一年前,兩年前,三年前也感受了四年前。
一邊承受著泉湧般的感受衝擊拍打著自己,一邊專注又恍神的看著車前方的那段延伸到世界盡頭的路。
突然間希望路一直延伸下去,不斷的延伸下去,一邊知道這是不可能實現的想法。

或許這就是空間、物體的生存法則與無奈,就這樣被推著走下去。
這就是我嗎?有時候自己會懷疑自己存在這世界上,或許更確切的說是自己是否真的存在一個空間,而那個空間是自己所想像我在那的空間。
其實我常想像著自己看著自己的那種凝視,凝視著自己的表情是嚴肅又充滿憐惜的無奈。
我想我用了太多的無奈,用了太多的虛幻。
但是確實無法知道是真實或是虛幻!

或許我就是我,一本書的主角與作者,無奈的提著筆或敲打著字那樣的正在寫著自己像是:
[我正坐在自己的黑色筆記電腦前敲打著幾個字...]。
這樣想有點的悲哀,因為分裂的自己處在多個空間中,就像是在四面都是鏡子的電梯裏頭的自己看著鏡子中那樣的無限的延伸出多個自己。

不管怎樣時間就這樣過去了不是嘛!
是啊! 在淡水考試的我,開車在從真理大學下課行經快速道路的我,
幼稚的生氣的我,一個人站在大稻埕看著護牆河的我。
一切的我都在不同的時間點裡坐著不同的事情感受著不同的色彩!

這是時間與物體的多層次! 而感受無奈與傷感~

2009年11月30日

(轉載)資料庫系統是鎯頭; MapReduce 則是螺絲起子

資料庫系統是鎯頭; MapReduce 則是螺絲起子

| | Comments (0) | TrackBacks (0)

最近年來不管是雲端運算(Cloud computing) 或是網格運算(Grid computing) 都相當的熱門, 而它們的核心技術 MapReduce 則受到相當程度的重視. 04 年時 MapReduce 一開始被 Google 所提出來, 它本身是一種程式開發的模式,可以用來處理大量的資料. 但因為長時間以來, 不管是在學校或是業界, 資料庫系統都廣泛的被教授和使用,很多人一看到"處理大量的資料", 自然的就會把 MapReduce 跟資料庫系統放在一起, 拿來做比較等等, 因而產生一些錯誤的認知.

目前任職在 Google 的 Mark 日前撰寫了一篇文章 Databases are hammers; MapReduce is a screwdriver 來說明這兩者的不同. 這篇文章是我個人認為目前在網路上說明 MapReduce 文章當中寫的最貼切且淺顯易懂的. 經過原作者的同意 , 我把它翻譯成中文, 希望讓更多人藉著閱讀這篇文章來瞭解 MapReduce.

資料庫系統是鎯頭; MapReduce 則是螺絲起子.

有一群朋友們把一篇關於批評MapReduce的文章傳 給我. 我曾經猶豫是否我應該寫些什麼, 因為目前為止有很多關於MapReduce的東西正被開發中, 而且被Google的同仁們廣泛的使用. 但是這篇文章真的是有點惱人, 我理當回應它. 所以我在這邊先做個澄清, 我並不是以一位Google員工的身份來評論它.(事實上, 我在工作上並沒有使用MapReduce). 這純粹是用我自己的時間, 發表我個人的意見. 如果您覺得這是一篇愚蠢的文章, 那是我個人的錯跟Google無關. 如果您覺得這篇文章寫的很出色, 那也是我個人的榮幸跟Google無關. Google 並沒有要求我寫這篇文章, 同樣的我也沒有要求 Google 的同意. 這僅只是我個人的行為, 瞭解嗎?

我會對這件事情有興趣的原因是因為這跟我的博士研究有關. 回想起研究所那時候, 我的研究是關於在非科學性的應用軟體上, 將平行運算的技術應用在結構化資料上.這差不多是 MapReduce 所要做的事情. 而我所提出的解決方案是某種階層式的分散(scatter)/收集(gather)運算子,非常接近 MapReduce 運作的方式. 那最大的差異在哪邊? 那就是 MapReduce 打敗了我, 設計MapReduce 的人注意到我沒注意到的東西, 並且利用了它使得 MapReduce 程式更乾淨, 更容易開發.

讓我們從頭開始, MapReduce 是啥, 它能做什麼?

今天假設你在工作, 你需要做某件事情, 但是它在你的電腦上會跑蠻久的時間, 你不想等但是你也不想要花幾百萬去買台超級電腦, 那你要怎樣才能夠讓它跑快一點? 有一個方法是買一群便宜的電腦, 讓它們同時一起幫你執行工作. 另一方面你也注意到辦公室內有很多台電腦, 幾乎每個員工座位上都有一台, 在某個時間點上大部分的電腦並沒有在做太多事情, 那為何不利用它們呢? 當你的機器不甚忙碌的時候, 你允許你的同事借用你當下並不在使用的資源, 當你需要的時候也可以借用他們的機器. 所以當你需要執行大的工作的時候, 你可以輕鬆的找到好幾打的機器來用.

這個方法最大的問題在於, 大部分的程式並沒撰寫成可以在多台的機器上執行的模式, 它們被寫成只能在一臺機器上執行. 而要把一件困難的工作分到很多台機器執行上是件困難的事情.

MapReduce 是一種程式語言開發模式, 它讓你可以透過撰寫特有的制式化程式, 輕鬆的把工作分散到一群機器上面去執行. 它基本的概念在於你把工作分成兩部份 Map 以及 Reduce. Map 基本上負責把問題分成好幾份並且送到不同的機器去,所以它們可以同時被處理. Reduce 則接收每份問題的處理的結果, 最後組合成單一的答案.

MapReduce 運作的關鍵在於, 把輸入的資料在概念上當成成一個包含很多筆紀錄的 list, 透過 map 這些紀錄被分配到不同的機器上去處理. map 計算的結果是一個包含許多 key/value 組的 list. Reduce 會取得同樣 key 值的所有 value, 並組合成為最後的單一 value. 也就是 Map 將資料產生成為 key/value 組, reduce 則組合結果給你單一的結果. 你無法知道這個工作是被分成100份或是2份, 而最後應該是跟只有單一 map 時候是一樣的. (這就是 MapReduce 和我博士研究方法不同的地方, 領悟到把結果當作是 key/value map 來看,你會得到非常乾淨且一致的歸納流程. 雖然我的 scatter/gather 模式是同樣的概念 , 但是我的 reduces 跟 Map/Reduce 比起來相對的醜陋許多.)

MapRedue 美麗的地方在於撰寫它是多麼的簡單, 在平行處理程式語言的領域上從來沒這麼簡單過.

回到該篇批評 MapReduce 的文章, 他們批評 MapRduce, 其實基本上並不是基於關聯式資料庫(Relational databaes) 的概念.

當我第一次花時間學習關聯式資料庫的時候, 我的老闆告訴我ㄧ個關於資料庫專家的故事, 是我認為最貼切的故事. 這個故事是說關聯式資料庫的專家們發現了這個世界上最美麗,最漂亮,最完美的鎯頭, 完美到不會太重,也不會太輕,恰到好處的可以把釘子給釘進去,它的柄是根據所有者手的角度去特製化的, 所以釘一整天也不會起水泡.同時它也是裝飾的很漂亮的鎯頭, 上面有寶石鑲嵌跟黃金的工飾在適當的地方, 絲毫不會減損鎯頭的功能. 它真的是一個最偉大的鎯頭.資料庫專家們熱愛他們的鎯頭, 因為它是多麼美妙的工具. 而且他們真的利用這個工具做出許偉大的東西. 他們是如此的喜歡關聯式資料庫,以致於認為這是這是他們唯一需要的工具. 如果你給他們一個螺絲釘, 他們會把它當成釘子一樣的釘進去. 當你提醒他們說, 嘿這樣會弄壞東西 , 那是螺絲釘不是釘子, 他們會說"我知道阿, 但是我有這隻極好的鎯頭 , 你不能期待我使用那鱉腳的小螺絲起子"

現況是這樣, 他們有關聯式資料庫, 關聯式資料庫絕對是個傑出的東西, 是個驚人的工具可以幫我們做出驚人的軟體. 我自己曾經利用關聯式資料庫完成許多工作, 沒有它, 我將沒有辦法完成一些令我感到驕傲的事情. 我完全不想貶低關聯式資料庫, 它真的是偉大的系統.但是不是每件事情都是關聯式資料庫, 不是每件事情天生就適合把它當作關聯來看待. 所有對 Mapreduce 的指責都源於"這不是我們關聯式資料庫會做的方式", 而並沒有瞭解到事情的重點. 關聯式資料庫的平行處理並不是很好, 你知道有多少關聯式資料庫可以有效的把工作分給 1,000 台普通機器? 關聯式資料庫無法很好的處理非表格式的資料, 像是面對遞迴資料的處理上可以說是是聲名狼藉. MapReduce 不是為了要取代關聯式資料庫, 它是提供一種輕便的程式語言開發方式, 讓大家可以快速且以平行的方式在一群機器上面執行, 僅止於這樣.

2009年11月28日

MYSQL 權限賦予語法

GRANT ALL ON *.* TO root@192.168.1.52 IDENTIFIED BY '123123' WITH GRANT OPTION;
GRANT SELECT,INSERT,UPDATE,DELETE,CREATE ON opencms.* TO root@192.168.1.52 IDENTIFIED BY '123123';
GRANT ALL PRIVILEGES ON *.* TO root@192.168.1.52 IDENTIFIED BY '123123' WITH GRANT OPTION;
FLUSH PRIVILEGES;

  • GRANT ALL ON *.* TO 'username'@'localhost' IDENTIFIED BY 'passowrd'
  • ALL: 授權的權限(SELECT, INSERT .... etc)
  • *.*: Table Name, Db_name.*, 指定可以存取哪些Db/Table
  • username: 要新增的 username
  • localhost: 可從哪邊來存取, 可用 %
  • password: 密碼

2009年11月22日

Postgresql Notes

1.How to change the pssword of postgres
-> sudo -u postgres psql template1
-> alert user postgres with pasword [Password]
-> create user [UserName] with password [Password]

2009年11月21日

Bugzilla Install guideline

Precondition:
1. you need have perl, apache, MTA
2. Install Perl (the verison depend on what version of Bugzilla release)

Step:
1. Download Bugzilla from offical website
2. Install Apache and Apache-dev, mod_perl
3. untar Bugzilla tar ball
4. mv Bugzilla to your www root dir , optional you could install bugzilla to where you prefer and make symbol link
5. change to directory of bugzilla
6. run ./checksetup.pl --check-modules and follow the instruction
7. install perl module for database, mysql is my so [/usr/bin/perl install-module.pl DBD:mysql]
8. add user named 'bugs
and login to mysql and create table name 'bugs' which depend on the localconfig(generate from checksetup command)

Note:
Edit {apache_root}\site-enabled\000-default to add Directory for Bugzilla

2009年10月19日

他夏了夏天



他夏了夏天
詞曲/吳青峰

幾點鐘 結束夢 他按下鬧鐘
如往常 開始了一天生活
忙工作 忙收穫 早餐吃什麼
他和他 維持齒輪的脈搏

汗水在他的身上化成了彩虹
步伐的節奏延續生命的河流
默默在崗位戰鬥的每個小小英雄
富有和貧窮、卑微和偉大相同
他從不害怕自己被人群淹沒
中午吃便當是他最大享受

幾點鐘 也許是 月出的時候
如往常 結束了一天工作
他心中 幻想著 晚餐吃什麼
家裡的 讓他不怕往前衝

疲累在他的身上化成了笑容
步伐的節奏開始不那麼沈重
輕輕旋轉著夏天地面悶熱的晚風
平凡或特別、笨拙或聰明相同
他從不擔心自己被世界折磨
甜蜜的負荷是他最大依託

帶著笑容的睡意化成了彩虹
在他夢中一口氣走上了星空
喧囂地亮起整個夏天渴望的揮霍
清淡與濃烈、好與壞他都嚐過
他從不介意自己被命運作弄
按下了鬧鐘開啟另一個夢

2009年10月1日

[PHP] Parse xml file

為了幫女朋友寫一個XML的讀寫工具所以今天花了不少時間,
又因為是第二次寫PHP所以一些基本的語法老摸不清楚,每種語言的特性真的差異不小。
像是語法的宣告、陣列的操作、CLASS的結構、參數的存取...ext 都有很大的差異。
但是基本上後來發現PHP有很多部分都很接近C,像是sizeof、函示呼叫方式都很像,可能PHP的作者寫C寫很久吧!
不過寫過JAVA、DOT NET、C、C++、VB後我發現還是SCRIPT LANGUAGE簡單多了!
也因為可以很快的上手(像一天可以入門)進而可以很快的得到成就感。
EMBEDDED就沒辦法了,一個小小的測試都需要花上很多的時間,OK!不費話。


這個XML 的CLASS用了XMLReader來做,主要是讀取一個XML檔案,將檔案內的Entity解讀出來然後加入到一個陣列中再回傳給UI LAYER使用。因此這是個UTILITY CLASS。

檔案的範例:
天使魔鬼/亡命快劫租片5折
2009/05/15
blockbuster_ecoupon_01.jpg
blockbuster_ecoupon_01.jpg
block
living

PHP的程式碼:


header("Content-type: text/html;charset=utf8");
// 儲存單筆資料的物件
class CouponEnt{
var $name;
var $period;
var $photo;
var $pic;
var $brand;
var $category;
}
// 讀取XML並把資料轉為ARRAY
class CouponBank
{
private $file_name;
private $xml_reader;
private $ent_bank;
public static $test = "test
";
private $top_node = "coupon";
public function __construct($file_name)
{
$this->file_name = $file_name;
$this->xml_reader = new XMLReader();
$this->ent_bank = array();
$this->loadFile($this->file_name);
$this->parseFile($this->xml_reader);
}
// load file
private function loadFile($file)
{
$this->xml_reader->open($file);
}
// parset xml
private function parseFile($xml_reader)
{
while($xml_reader->read())
{
if($xml_reader->nodeType == XMLReader::ELEMENT && $xml_reader->name == $this->top_node)
{
$coupon = $this->getElement($xml_reader);
array_push($this->ent_bank, $coupon);
}
}
return $ent_bank;
}
// translate xml element to object
private function getElement($xml_reader)
{
$coupon = new CouponEnt();
while(true)
{
// stop loop on tag end
if($xml_reader->nodeType == XMLReader::END_ELEMENT && $xml_reader->name=="coupon")
{
break;
}
// set element value to entity
if($xml_reader->nodeType == XMLReader::ELEMENT)
{
switch($xml_reader->name)
{
case "name":
$xml_reader->read();
$coupon->name = $xml_reader->value;
$xml_reader->read();
break;
case "period":
$xml_reader->read();
$coupon->period = $xml_reader->value;
$xml_reader->read();
break;
case "photo":
$xml_reader->read();
$coupon->photo = $xml_reader->value;
$xml_reader->read();
break;
case "pic":
$xml_reader->read();
$coupon->pic = $xml_reader->value;
$xml_reader->read();
break;
case "brand":
$xml_reader->read();
$coupon->brand = $xml_reader->value;
$xml_reader->read();
break;
case "category":
$xml_reader->read();
$coupon->category = $xml_reader->value;
$xml_reader->read();
break;
default:
$xml_reader->read();
break;
}
}
else
{
$xml_reader->read();
}
}
return $coupon;
}
public function getCoupons()
{
return $this->ent_bank;
}
public function release()
{
$this->xml_reader->close();
$this->xml_reader = null;
}
public function debug()
{
if(sizeof($this->ent_bank)<=0)
echo "debug : element array is empty.
";
else
{
print_r($this->ent_bank);
}
}
}

// 建立新的物件
$bank = new CouponBank("data.xml");
// 讀取內容,回傳的是陣列,內容物是CouponEnt
$cps = $bank->getCoupons();

for($i = 0; $i <>
{
echo " =================== [Coupon information]
";
echo " name = ".$cps[$i]->name."
";
echo " period = ".$cps[$i]->period."
";
echo " photo = ".$cps[$i]->photo."
";
echo " pic = ".$cps[$i]->pic."
";
echo " brand = ".$cps[$i]->brand."
";
echo " category = ".$cps[$i]->category."
";
echo " =======================================
";
}

$bank->release();

//$bank->debug();
?>

2009年9月26日

夢的那悟

不知道是因為這個空間有生物所以存在而或是因為這個空間之外的空間存在。
最近常夢見自己,夢見自己像似般的未來。
或許是因為是種徵兆、也或許這是一種真實未來。
或許開始認為滿足是一種簡單也很複雜的事情因此而感到疲倦,
或許是一個夢夢見自己感受了那樣的迷失而覺得頓時需要做出一些什麼。
不過這就是世界的想法突然間出現在身體裡的某個地方,
可能會像是一種簡單的低語,低沉的呢喃在自己的身體某個可以發出回聲的地方。

漸漸的了解這個世界變成了一種左腳右腳的結界石,
開始覺得不是因為如此而那樣。
開始覺得應該會是這樣。

儘管的讓這世界變成世界,也讓空間變成空間,影像變成影像吧!
為了生活而活,而不要為了活而生活!
或許是因為我而誤解、也或許是因為世界而世界,
不過說穿了都是個故事罷了!


2009年9月11日

WINCE Configuration Files

Configuration Files

Binary Image Builder (.Bib)
The .bib files describe the memory structure (ROM/RAM) and specify what files need to be included into the image; they also contain additional configuration parameters related to the memory. A merged .bib file named CE.bib, which the Romimage.exe utility uses for forming a monolithic image, contains the following .bib files:

BSP files (Config.bib, Platform.bib).
Files of selected Windows CE components (Common.bib, and so on).
OS design files (Project.bib and subproject .bib files).

Object Store Initialization Files (.Dat)
The .dat files are used for initializing a file system in the memory (RAM file system). During the MAKEIMG stage, .data files are merged into the InitObj.dat file. The resulting InitObj.dat file is used by Filesys.dll for creating a directory tree of the file system in the memory. Entries in the DAT files have the following format:

Registry Initialization Files (.Reg)
Registry files form the initial registry of the operating system. The format of Windows Embedded CE registry files is similar to that of the desktop version of Windows. During the MAKEIMG stage, all registry files in the build directory (_FLATRELEASEDIR) are merged into the RegInit.ini file

Database Initialization Files (.Db)
During the MAKEIMG stage, .db files are merged into an InitDB.ini file and are used for initializing EDB databases that are included in the image. The entry format is described in detail in the supplied .db files.

WinCE Interrupt (中斷)

WinCE Interrupt (中斷)

關於中斷的相關名詞解釋: (參照書本及網路資源)
1.何謂IRQ:
IRQ 即所謂的中斷請求(Interrupt Request) ,是指硬體裝置發給CPU的訊號,要求CPU暫時先放下目前正在處理的工作,將工作的優先權暫時先讓給此發送訊號的硬體裝置,如Touch panel, keyboard, audio的I/O處理... 等等相關硬體裝置,都是透過IRQ來對CPU發出中斷請求。

2.何謂ISR:
ISR 即所謂的中斷服務常式(Interrupt Service Routine),指用來回應硬體裝置發出IRQ的軟體常式,屬於OAL層,主要的工作為判斷硬體發生中斷的原因,並回傳一特定的Interrupt ID(SYSINTR)給Kernel,Kernel則會喚醒IST對中斷做進ㄧ步的處理。

3.何謂Interrupt ID:
Interrupt ID即所謂邏輯中斷識別碼(Interrupt Identifier),每個都是獨一無二的值,代表著某個硬體裝置發出的邏輯中斷要求。藉由Interrupt ID,kernel可以表示是否該處理可以完成,或是需要IST (Interrupt Service Threads)進ㄧ步處理該中斷請求。

4.何謂IST:

•Created by a device driver
•IST is a thread that does most of the interrupt processing
•IST performs necessary I/O operations in the device to collect the data and process it
•IST must associate an event object with an interrupt identifier by InterruptInitialize
•IST can boost themselves to a higher priority by calling CeSetThreadPriority


interrupt step flow:


針對 data page 的 Copy-on-Write/XIP (轉貼紀錄)

October 23, 2006

針對 data page 的 Copy-on-Write/XIP

CE Linux Forum 的 wiki 上,有一篇由 Panasonic Mobile Communications Co., Ltd. 所提出的新技術 [Data Read-In-Place],不同以往針對 Linux kernel 的大幅修改,這個作法只修改了 dynamic loader (ld-linux.so)。在描述這個作法之前,先來觀察 ELF 執行檔的 .data section 在執行時期的行為,參考下圖:

對多數應用程式來說,Dynamic loader 必須將 .data section 予以 linear mapped 至 virtual address 中,於是我們可以發現,當進行 read 或 write 動作時,行為如下圖:

而 Application-level XIP (eXecution-In-Place) 會希望 .data section 能永久保存於 Flash 儲存裝置中,除非 .data section 本身發生變化 (被 write),所以 [Data Read-In-Place] 提出一個簡單有效的機制,將原本 mmap 的範圍從:
    mmap(..., PROT_READ | PROT_WRITE);
修改為:
    mmap(..., PROT_READ);
    mprotect(..., PROT_READ | PROT_WRITE);
當進行 mmap ELF segment 時,捨棄 PROT_WRITE 這個 bit,這使得 cramfs 行為變成「如同 XIP .text 一般,去 mapping 每個在 .data section 的 page 到 ROM pages」,而透過 mprotect 去設定 PROT_WRITE,這使得:
  • vm_area_struct 允許作 write
  • PTE (page table entry) 的 write 權限則被抑制
換言之,我們已經實做了 CoW (Copy-on-Write),這之間微妙的變化,可參考之前的翻譯文章 [探索 Linux Memory Model (上)] 與 [探索 Linux Memory Model (下)]。在上述機制引入後,系統呈現的效果如下圖:

Panasonic Mobile Communications Co., Ltd. 提出的方法,在最低程度的修改下,實做 In-Place 的 data read,依據官方說法:
    "The total effect for one system measured by Panasonic was a reduction of 26% of the page cache allocated to processes, when the product was in the stand-by state."
不過,這也引來額外的 ROM access latency,會導致程式執行時間拉長,對一般的消費性電子裝置來說是可以接受的 (single executable)。wiki 上提供的 patch 似乎不太完整,所以我弄了新 patch:[glibc-2.3.2-DRIP.patch],加入 [OpenEmbedded] 一類的工具中即可。

cramfs 的 XIP 有許多可最佳化的空間,或許有機會等某個產品設計完畢後,可試著整理,而我也開始觀察 [AXFS - Advanced XIP File System] 的實做,相關的討論可見 LKML [RFC: Advanced XIP File System]。
由 jserv 發表於 October 23, 2006 10:07 PM

2009年9月9日

PASCAL and WINAPI 的用意

VC有兩種函數調用方式 一種是__stdcall,另一種是__cdecl,函數的調用方式有兩種一種是PASCAL調用方式,另一種是C調用方式。
使用PASCAL調用方式,函數在返回到調用者之前將參數從棧中刪除。
使用C調用方式,參數的刪除是調用者完成的。

WinMain函數是由系統調用的,Windows系統規定由系統調用的函數都遵守PASCAL調用方式,但是VC中函數的缺省調用方式是__cdecl,也就是C調用方式所以在WinMain前顯示的聲明:
int PASCAL WinMain(HINSTANCE hInstC, HINSTANCE hInstP, LPSTR lpCmdLine, int nCmdShow)
{
//
}

在Windows編程中將遇到很多聲明修飾符,如CALLBACK,WINAPI,PASCAL這些在IntelCPU的計算機上都是__stdcall,詳細的聲明細節可以參考windef.h :

#define CALLBACK __stdcall
#define WINAPI __stdcall
#define WINAPIV __cdecl
#define APIENTRY WINAPI
#define APIPRIVATE __stdcall
#define PASCAL __stdcall

然而 __stdcall 只能擁有固定的參數數量,若需要使用可變動的參數就要使用 __cdecl。

MPEG 2 Layer 3與MPEG 1 Layer 3的區別

MPEG Audio分為MPEG1和MPEG2兩大類。
而MPEG1中主要有Layer1、Layer2和Layer3三種。

MPEG1 Audio編碼方式之間的主要區別從外部看來主要是對音頻文件的壓縮率和要求播放媒體提供數據的速率不同,
並且內部採取的算法也有很大的不同,基本上是隨Layer數增大而越來越複雜。
經Layer1編碼的音頻文件後綴為MP1,另外兩種分別為MP2和MP3。

MPEG-1和MPEG-2用同一個家族的聲音多媒體數字信息編碼器,不管是第一層,第二層還是第三層。
MPEG-2的新的音頻特性是「低採樣頻率的擴展」和「多聲道擴展」。
「低採樣頻率的擴展」是指為那些限制了帶寬需求的位速率非常低的應用系統服務的場合,
新的採樣頻率是16,22.05或24kHz,位速率擴展到8kbps以下。
「多聲道擴展」是指服務於那些擁有5個主要聲道(左、右、中置、左環繞和右環繞)的環繞聲系統,
有的環繞聲系統甚至還要額外加一個低頻增進聲道來處理低頻音信號,對這種系統,「多聲道擴展」允許包含直至7個聲道。

2009年9月7日

.NET CTS (Common Type System)

.NET CTS (Common Type System)

作者:蔡學鏞

2004 年 5 月

CTS 非常重要,CTS (Common Type System) 是 .NET CLR 的核心,這樣的說法並不為過。任何 .NET 的程式員都需要對於 CTS 有最基本的認識,也因此本文的目的在於讓大家瞭解 CTS,但是由於 CTS 的定義相當繁瑣,所以本文章只能包含一小部分。請注意,由於 CTS 的定義中有一些地方很容易造成學習上的混淆,所以本文並未完全依照 CTS 的定義來解說。

任何程式語言,如果想要在 .NET CLR 上執行,就必需提供一個編譯器,將此語言的程式編譯成 .NET CLR 所認識的 metadata 以及 IL,符合 CTS 的規定。

並非所有的語言都能和 C# 一樣符合 CTS 的規範,畢竟許多語言出現在先,CTS 出現在後,所以有一些舊的語言未能符合 CTS 的規定。這類的程式語言在 .NET 中有下列三種因應方式:

  1. 改變語言本身以符合 CTS 的規定。例如 Visual Basic 6 就為此做了大幅度的擴充與改變,加上繼承的特性,這使得新版的 Visual Basic .NET 差不多可以算是一個全新的語言了,猶如當年 C 衍生出 C++ 一樣。
  2. 擴 充語言本身以接近 CTS 的規定,但仍保留不相容於 CTS 的語法,如此一來,程式中符合 CTS 規定者以 CTS 的方式編譯,不符合 CTS 規定者則以傳統的方式編譯成 native code。例如 C++ with Managed Extension (簡稱 MC++) 就是如此。
  3. 語 言本身儘量維持不變,一切都是透過超強的編譯器設計來達成和 CTS 的相容,Eiffel 就是如此的作法。例如,CTS 不支援多重繼承 (multiple inheritance),但是 Eiffel 支援多重繼承,透過 Eiffel 的編譯器可以利用 interface 以及 attribute 來達到多重繼承 (這樣的作法相當巧妙,有興趣的讀者不妨去研究一下)。

我 們可以將 CTS 看成是所有 .NET 語言的 superset (union),而符合 CTS 的各種不同的語言,其實都只是 CTS 的 subset (intersection)。這些語言所寫出來的程式,如果想要有最佳的相容性,以便互相調用 (invoke) 或繼承,這些語言之間就必需取得一個共同的 subset,有共同遵守的規範,這就是 CLS (Common Language Specification,或 Common Language Subset)。

圖一是整個概念的示意圖。

對 1︹cts 姓其召語言的裧咳
圖 1:CTS 和其他語言的關係

資料的內容稱為值 (Value),每個值一定屬於某些型別 (Type),而最完整地表達此值的型別稱為 Exact Type (精準型別)。

根據 Value 是否可以再切割,Value 分成兩種:

  • Simple Value (簡單值):指的是不可再分割 (atomic) 的資料。
  • Complex Value:Complex Object 指的是由多個 Simple Value 所組成的資料。

根據 Value 是否可以直接使用,Type 分成兩類:

  • Value Type:直接使用 Value Type 的 Value。
  • Reference Type:不能直接使用 Reference Type 的 Value,只能間接存取資料。

Value Type 和 Reference Type 的處置方式很不同:Value Type 是直接存取資料,Reference Type 則是間接存取資料。Reference Type 對程式員來說是最方便的作法,但其實 Value Type 的效率比較好,所以兩者各有適合的地方。請參考圖二。

有些時候,我們需要把 Value Type 當成 Reference Type 來使用,所以我們需要一套機制來將 Value Type 的值裝箱 (box) 成 Reference Type 的值,這套機制就叫做 Boxing。當 Boxed Value 需要再度從 Reference Type 被轉回原來的 Value Type,這套機制就叫做 Unboxing (拆箱)。

對 2︹守綿剛的邕樹樥配置方式
圖 2:各種值的記憶體配置方式

CTS 對於 Type 有一個很詳細的分類,但是它的分類方式我不喜歡,因為容易造成混淆。所以我對其做了一些修改,我所做的修改包括了:

  • 將 Interface 自 Reference Type 中獨立出來,因為 Interface 可以是 Value Type,也可以是 Reference Type。(這點和 Java 不同)
  • 將 Pointer 自 Reference Type 中獨立出來,因為 Pointer 其實和 Reference Type 的差異很大。
  • 將 Build-in Value Type 之下的 Integer、Floating Point、Typed Reference 刪除,因為這三者未能包含 Boolean 以及 Void。
  • 將 Build-in Value Type 改名為 Primitive Type,新名稱乃是遵循 IL Assembly 設計者 Serge Lidin 的稱呼方式。(這也是 Java 慣用的稱呼方式)
  • 將 Enumeration 自 User Defined 中獨立出來。將 User Defined 改名為 Structure。這乃是根據繼承的父類別來分類。
  • 刪除 Build-in Reference Type 以及其子節點 String 與 Object。因為 String 與 Object 只有在 Signature 中和其他 Class 不同之外,並無特殊之處。我認為不需要特別分成一類。
  • 將 Self-Describing 及其子節點大幅更動,因為原來的方式太混亂。更動後的版本等下會看到。
  • 將 Box Value Type 及其子節點刪除,因為 Box Value Type 只是一個概念,並非真的存在,其實使用的還是原來的 Type。
  • 將 Array 再細分成兩種,分別是 Vector 以及 Multi-Dimensional Array。(請注意,這裡的 Vector 和 Java 的 Vector 是不同的意思)

如果你對於原本的 CTS 分類方式感興趣,請自行參考相關資料,這裡不再列出。下面列出的是我修改後的版本。

如果你對於原本的 CTS 分類方式感興趣,請自行參考相關資料,這裡不再列出。下面列出的是我修改後的版本。

  • Value Type
    • Structure (Extended From ValueType directly or indirectly)
    • Enumeration (Sealed, and Extended From ValueType directly)
  • Reference Type
    • Regular Class
    • Array (Sealed, and Extended From Array directly and implicitly, Don't have a type name)
    • Delegate (Sealed, and Extended From MulticastDelegate directly)
  • Interface
  • Pointer
    • Function Pointer
    • Managed Data Pointer
    • Unmanaged Data Pointer

後面會一一解說這些型別。圖三是這些型別的繼承架構。

對 3︹cts 的一函重要型吶
圖 3:CTS 的一些重要型別

除了 Interface 以及 Pointer 之外,所有的型別都是直接或間接繼承自 System.Object。這張圖所列出來的每個型別都很特別,對於 CTS 而言都有特殊的意涵。

所有的 Value Type 都直接或間接地繼承自 System.ValueType。若不繼承自 System.ValueType,則一定是 Reference Type。

Enumeration 一定是直接繼承自 System.Enum;而 Structure 一定是直接繼承自 System.ValueType。Structure 型別中有一些很特殊者,在 .NET CLR 中有特殊的處理方式,這些 Structure 被稱為 Primitive Type,詳列於下面的表格:

Code

Type Name

Comments

0x01

Void

0x02

Boolean

Single-byte value, true = 1, false = 0

0x03

Char

2-byte unsigned integer, presenting a Unicode character

0x04

Sbyte

Signed 1-byte integer, the same as char in C/C++

0x05

Byte

Unsigned 1-byte integer

0x06

Int16

Signed 2-byte integer

0x07

UInt16

Unsigned 2-byte integer

0x08

Int32

Signed 4-byte integer

0x09

UInt32

Unsigned 4-byte integer

0x0A

Int64

Signed 8-byte integer

0x0B

UInt64

Unsigned 8-byte integer

0x0C

Single

4-byte floating-point

0x0D

Double

8-byte floating-point

0x16

TypedReference

Typed reference, carrying both reference to a type and information identifying the referenced type

0x18

IntPtr

Pointer-size integer; size dependent on the underlying platform.

0x19

UIntPtr

Pointer-size unsigned integer

Delegate 是一種物件導向的 Function Pointer (不同於傳統的 Function Pointer)。所有的 Delegate 都必需直接繼承自 System.MulticastDelegate。

任 何 Array 都必需直接繼承自 System.Array,但是這樣的繼承關係是「由 .NET CLR 內部自行認定如此」,我們並不會在 .NET PE 檔內部看到這樣的繼承關係。事實上,在 .NET PE 檔內部連 Array 型別 (這裡指的不是 System.Array) 的定義都不可能出現,因為 Array 型別的存在完全是透過 Signature,而非 TypeDef 或 TypeRef 的 Metadata Table (關於 Array 的許多特點,.NET 的作法很類似 Java)。

還有一點值得我們注意的,CTS 支援兩種 Array,分別是:

  • Vector:是一維的 Array,且 index 值由 0 開始。
  • Multi-Dimension Array:是多維的 Array (當然也可以當一維使用),且 index 值可以不必由 0 開始。

千萬不要輕忽這兩種 Array 的差別,兩者在彈性和效率上各有擅場,你甚至可以組合出許多的變化,例如:利用「Vector 的 Vector」,製作出非矩形的 Array。

任何直接或間接繼承自 System.Object,且未直接或間接繼承自 System.ValueType、System.Delegate、System.Array 者,都是屬於 Regular Class。

Interface 是一種很特殊的型別,Interface 不能當作 Exact Type,所以由其當時的 Exact Type 來決定 Value 所屬的型別是 Value Type 或 Reference Type。

CTS 也支援三種 Pointer,分別是:

  • Function Pointer:記憶體位址,指向一個 function 的起點。請注意:C#「不」支援 Function Pointer。
  • Managed Data Pointer:記憶體位址,指向一個 managed data。請注意:C#「不」支援 Managed Data Pointer。
  • Unmanaged Data Pointer:記憶體位址,指向一個 unmanaged data。請注意:C#「支援」Unmanaged Data Pointer,但不常用,也不鼓勵使用。

Value Type、Reference Type、以及 Interface,可以有兩種不同的 Accessibility (請參考圖四)。分別是:

  • Public:開放所有的程式使用此 Type。
  • Assembly:也就是 C# 的 Internal,是預定的。僅開放同一個 Assembly 的程式使用此 Type。

對 4︹value type}reference type}去止 interface 的 accessibility
圖 4:Value Type、Reference Type、以及 Interface 的 Accessibility

如果是 Nested Type (定義在另一個 Type 內部的 Type) 的話,則有比較多種 Accessibility。如下圖五所示:

對 5︹nested type 的 accessibility
圖 5:Nested Type 的 Accessibility

  • Public:開放所有的程式使用此 Type。
  • Family-or-Assembly:開放給同一個 Assembly 的程式,或者 Sub-Type (繼承自此 Type 的 Type) 的程式。在 C# 中稱為 Protected Internal (或 Internal Protected 亦可)。
  • Family:開放給 Sub-Type 的程式。在 C# 中稱為 Protected。
  • Assembly:開放給同一個 Assembly 的程式。在 C# 中稱為 Internal。
  • Family-and-Assembly:開放給同一個 Assembly 的 Sub-Type 程式。C# 不支援這種 accessibility。
  • Private:僅能在同一個 Enclosing Type (包含此 Nested Type 的外部 Type) 程式內使用。這也是 C# 的預定。

關於 CTS,如果讀者需要更進一步的資料,可以參考下面三本書:

  1. The Common Language Infrastructure Annotated Standard (by James S. Miller ) Addison Wesley
  2. Inside Microsoft .NET IL Assembler (by Serge Lidin) Microsoft Press
  3. Compiling for the .NET Common Language Runtime (by John Gough) Prentice Hall

2009年9月4日

DirectShow實踐經驗雜談

DirectShow實踐經驗雜談
流媒體的處理,以其複雜性和技術性,一向廣受工業界的關注。


特別伴隨著英特網的普及,流媒體在網路上的廣泛套用,怎樣使流媒體的處理變得簡單而富有成效逐漸成為了焦點問題。選項一種合適的套用方案,事半功倍。此時,微軟的DirectShow,給了我們一個不錯的選項。

DirectShow是微軟公司提供的一套在Windows平台上進行流媒體處理的開發包,與DirectX開發包一起發佈。目前,DirectX最新版本為8.1。


那麼,DirectShow能夠做些什麼呢?


且看,DirectShow為多媒體流的捕捉和回放提供了強有力的支持。運用DirectShow,我們可以很方便地從支持WDM驅動模型的採集卡上捕獲資料,並且進行相應的後期處理乃至存儲到文件中。


它廣泛地支持各種媒體格式,包括Asf、Mpeg、Avi、Dv、Mp3、Wave等等,使得多媒體資料的回放變得輕而易舉。


另外,DirectShow還集成了DirectX其它部分(譬如DirectDraw、DirectSound)的技術,直接支持DVD的播放,視瀕的非線性編輯,以及與數字攝像機的資料交換。


更值得一提的是,DirectShow提供的是一種開放式的開發環境,我們可以根據自己的需要定製自己的元件。


接下去,我們需要對DirectShow系統有個整體的印象。參見以下DirectShow的系統示意圖:

圖中央最大的一塊即是DirectShow系統。


DirectShow使用一種叫Filter Graph的模型來管理整個資料流的處理程序;參與資料處理的各個功能模組叫做Filter;各個Filter在Filter Graph中按一定的順序連接成一條「流水線」協同工作。


大家可以看到,按照功能來分,Filter大致分為三類:Source Filters、Transform Filters和Rendering Filters。


Source Filters主要負責取得資料,資料來源可以是文件、英特網、或者電腦裡的採集卡、數字攝像機等,然後將資料往下傳輸;Transform Fitlers主要負責資料的格式轉換、傳輸;Rendering Filtes主要負責資料的最終去向,我們可以將資料送給音效卡、顯示卡進行多媒體的演示,也可以輸出到文件進行存儲。值得注意的是,三個部分並不是都只 有一個Filter去完成功能。恰恰相反,每個部分往往是有幾個Fitler協同工作的。


譬如,Transform Filters可能包含了一個Mpeg的解碼Filter、以及視瀕色彩空間的轉換Filter、音瀕採樣頻率轉換Filter等等。


除了系統提供的大量Filter外,我們可以定製自己的Filter,以完成我們需要的功能。下圖是一條典型的Avi文件回放Filter Graph鏈路:

在DirectShow系統之上,我們看到的,即是我們的應用程式(Application)。


應用程式要按照一定的意圖建立起相應的Filter Graph,然後通過Filter Graph Manager來控制整個的資料處理程序。


DirectShow能在Filter Graph執行的時候接收到各種事件,並通過消息的方式傳送到我們的應用程式。這樣,就實現了應用程式與DirectShow系統之間的交互。下圖給出了DirectShow應用程式開發的一般程序:


以上簡單介紹了DirectShow的系統結構,希望大家對這個鑽勁的套用框架已經有了大概的認識。如果你有興趣,可以詳細研究DirectX的說明 我的文件。


DirectShow是一個強大的開發包;另外,它是關於COM的,因此要求程序員具有COM編程的一些基本知識。關於如何深入學習DirectShow 套用結構以及開發自己的Filter,請參閱筆者的後續文章。筆者將從編程的角度,詳細講述來源於實際工作中的經驗之談。


在上一講中,筆者介紹了DirectShow的總體系統框架。從這一講開始,我們要從程序員的角度,進一步深入探討一下DirectShow的套用以及Filter的開發。
在這之前,筆者首先要特別提一下微軟提供的一個Filter測試工具——GraphEdit,它的路徑在DXSDK\bin\DXUtils\GraphEdit.exe。

(如果您還沒有安裝DirectX SDK,請到微軟的網站上去下載。)通過這個工具,我們可以很直觀地看到Filter Graph的執行及處理流程,方便我們進行程序偵錯。


(如果您手邊就有電腦,還等什麼,馬上體驗一下吧:
執行GraphEdit,執行File->Render Media File…選項一個媒體文

件;當Filter Graph構建成功後,按下工作列的執行按鈕;您就能看到剛才選項的媒體文件被回放出來了!看到了吧,寫一個媒體播放器也就這麼回事!)
接下去,我們開講Filter的開發。

學習DirectShow Filter的開發,不外乎以下幾種方法:看說明 我的文件、看示例程式碼和看SDK基類來源碼。


看說明 我的文件,應著重於總體概念上的理解;看示例程式碼應與基類來源碼的研究同步進行,因為自己寫Filter,關鍵的第一步是選項一個合適的Filter基 類和Pin的基類。對於Filter的把握,一般認為要掌握以下三方面的內容:Filter之間Pin的連接、Filter之間的資料傳輸以及流媒體的隨 機訪問(或者說流的定位)。下面就開始分別進行闡述。



所謂的Filter Pin之間的連接,實際上是Pin之間Media Type(媒體類型)的一個協商程序。連接總是從輸出Pin指向輸入Pin的。要想深入瞭解直接的連接程序,就必須認真研讀SDK的基類來源碼(位於 DXSDK\samples\Multimedia\DirectShow\BaseClasses\amfilter.cpp,類CBasePin的 Connect方法)。連接的大致程序為,枚舉欲連接的輸入Pin上所有的媒體類型,逐一用這些媒體類型與輸出Pin進行連接,如果輸出Pin也接受這種 媒體類型,則Pin之間的連接宣告成功;如果所有輸入Pin上枚舉的媒體類型輸出Pin都不支持,則枚舉輸出Pin上的所有媒體類型,並逐一用這些媒體類 型與輸入Pin進行連接。如果輸入Pin接受其中的一種媒體類型,則Pin之間的連線到此也宣告成功;如果輸出Pin上的所有媒體類型,輸入Pin都不支 持,則這兩個Pin之間的連接程序宣告失敗。


有一點需要注意的是,上述的輸入Pin與輸出Pin一般不屬於同一個Filter,典型的是上一級Filter(也叫Upstream Filter)的輸出Pin連向下一級Filter(也叫Downstream Filter)的輸入Pin。如下圖所顯示:

當Filter的Pin之間連接完成,也就是說,連接雙方通過協商取得了一種大家都支持的媒體類型之後,即開始為資料傳輸做準備。這些準備工作中,最重要 的是Pin上的記憶體分配器的協商,一般也是由輸出Pin發起。在DirectShow Filter之間,資料是通過一個一個資料包傳送的,這個資料包叫做Sample。


Sample本身是一個COM對象,擁有一段記憶體用以裝載資料,Sample就由記憶體分配器(Allocator)來統一管理。


已成功連接的一對輸出、輸入Pin使用同一個記憶體分配器,所以資料從輸出Pin傳送到輸入Pin上是無需記憶體拷貝的。而典型的資料拷貝,一般發生在 Filter內部,從Filter的輸入Pin上讀取資料後,進行一定意圖的處理,然後在Filter的輸出Pin上填充資料,然後繼續往下傳輸。下面, 我們就直接闡述一下Filter之間的資料傳送。


首先,大家要區分一下Filter的兩種主要的資料傳輸模式:推模式(Push Model)和拉模式(Pull Model)。參考圖如下:



所謂推模式,即源Filter(Source Filter)自己能夠產生資料,並且一般在它的輸出Pin上有獨立的子線程負責將資料傳送出去,一般的情況如代表WDM模型的採集卡的Live Source Filter;而所謂拉模式,即源Filter不具有把自己的資料送出去的能力,這種情況下,一般源Filter後緊跟著接一個Parser Filter或Splitter Filter,這種Filter一般在輸入Pin上有個獨立的子線程,負責不斷地從源Filter索取資料,然後經過處理後將資料傳送下去,一般的情況如 文件源。推模式下,源Filter是主動的;拉模式下,源Filter是被動的。而事實上,如果將上圖拉模式中的源Filter和Splitter Filter看成另一個虛擬的源Filter,則後面的Filter之間的資料傳輸也與推模式完全相同。


那麼,資料到底是怎麼通過連接著的Pin傳輸的呢?首先來看推模式。


在源Filter後面的Filter輸入Pin上,一定實現了一個IMemInputPin接頭,資料正是通過上一級Filter使用這個接頭的 Receive方法進行傳輸的。值得注意的是(上面已經提到過),資料從輸出Pin通過Receive方法使用傳輸到輸入Pin上,並沒有進行記憶體拷 貝,它只是一個相當於資料到達的「通知」。


再看一下拉模式。拉模式下的源Filter的輸出Pin上,一定實現了一個IAsyncReader接頭;其後面的Splitter Filter,就是通過使用這個接頭的Request方法或者SyncRead方法來獲得資料。


Splitter Filter然後像推模式一樣,使用下一級Filter輸入Pin上的IMemInputPin接頭Receive方法實現資料的往下傳送。深入瞭解這部分內容,請認真研讀SDK的基類來源碼(位於
DXSDK\samples\Multimedia\DirectShow\BaseClasses\source.cpp和pullpin.cpp)。


下面,我們來講一下流的定位(Media Seeking)。在GraphEdit中,當我們成功構建了一個Filter Graph之後,我們就可以播放它。在播放中,我們可以看到進度條也在相應地前進。當然,我們也可以通過移到進度條,實現隨機訪問。


要做到這一點,在應用程式級別應該可以知道Filter Graph總共要播放多長時間,當前播放到什麼位置等等。那麼,在Filter級別,這一點是怎麼實現的呢?

我們知道,若干個Filter通過Pin的相互連接組成了Filter Graph。


而這個Filter Graph是由另一個COM對像Filter Graph Manager來管理的。通過Filter Graph Manager,我們就可以得到一個IMediaSeeking的接頭來實現對流媒體的定位。



在Filter級別,我們可以看到,Filter Graph Manager首先從最後一個Filter(Renderer Filter)開始,詢問上一級Filter的輸出Pin是否支持IMediaSeeking接頭。如果支持,則返回這個接頭;如果不支持,則繼續往上一 級Filter詢問,直到源Filter。


一般在源Filter的輸出Pin上實現IMediaSeeking接頭,它告訴使用者總共有多長時間的媒體內容,當前播放位置等信息。



(如果是文件源,一般在Parser Filter或Splitter Filter實現這個接頭。)對於Filter開發者來說,如果我們寫的是源Filter,我們就要在Filter的輸出Pin上實現 IMediaSeeking這個接頭;如果寫的是中間的傳輸Filter,只需要在輸出Pin上將用戶的獲得接頭請求往上傳遞給上一級Filter的輸出 Pin;如果寫的是Renderer Filter,需要在Filter上將用戶的獲得接頭請求往上傳遞給上一級Filter的輸出Pin。


進一步的瞭解,請認真研讀SDK的基類來源碼(位於DXSDK\samples\Multimedia\DirectShow\BaseClasses \transfrm.cpp的類方法CTransformOutputPin::NonDelegatingQueryInterface實現和 ctlutil.cpp中類CPosPassThru的實現)。



以上我們介紹了一下如何學習DirectShow Filter開發,以及一些開始寫自己的Filter之前的預備知識。下一講,筆者將根據自己開發Filter的經驗,一步一步教你如何寫自己的Filter。


在上兩講中,筆者介紹了DirectShow的套用原理以及開發Filter之前的一些預備知識。


這一講,筆者就要一步一步教你如何寫自己的Filter啦。
首先,從VC++的專案開始(請驗證你已經給VC++配置好了DirectX的開發環境)。寫自己的Filter,第一步是使用VC++建立一個Filter的專案。



由於DirectX SDK提供了很多Filter的例子專案(位於DXSDK\samples\Multimedia\DirectShow\ Filters目錄下),最簡單的方法就是拷貝一個,然後再在此基礎上修改。但如果你是Filter開發的初學者,筆者並不贊成這麼做。


自己新增一個Filter專案也很簡單。使用VC++的嚮導,建立一個空的」Win32 Dynamic-link Library」專案。




注意,幾個文件是必須有的:.def文件,定義四個匯出函數;定義Filter類的.cpp文件和.h文件,並在.cpp文件中定義Filter的註冊信息以及兩個Filter的註冊函數:DllRegisterServer和DllUnregisterServer。(


註:Filter的註冊信息是Filter在註冊時寫到註冊表裡的內容,格式可以參考SDK的示例程式碼,Filter相關的GUID務必使用 GuidGen.exe產生。)接下去進行專案的設定(Project->Settings…)。此時,你可以開啟一個SDK的例子專案進行對比, 有些巨集定義完全可以照抄,最後注意將輸出文件的副檔名改為.ax。

上一講曾經提到過,在寫Filter之前,選項一個合適的Filter基類是至關重要的。為此,你必須對幾個Filter的基類有相當的瞭解。


在實際套用中,Filter的基類並不總是選項CBaseFilter的。相反,因為我們絕大部分寫的都是中間的傳輸Filter(Transform Filter),所以基類選項CTransformFilter和CTransInPlaceFilter的居多。



如果我們寫的是源Filter,我們可以選項CSource作為基類;如果是Renderer Filter,可以選項CBaseRenderer或CBaseVideoRenderer等。


總之,選項好Filter的基類是很重要的。當然,選項Filter的基類也是很靈活的,沒有絕對的標準。



能夠通過CTransformFilter實現的Filter當然也能從CBaseFilter一步一步實現。下面,筆者就從本人的實際經驗出發,對Filter基類的選項提出幾點建議供大家參考。


首先,你必須明確這個Filter要完成什麼樣的功能,即要對Filter專案進行需求分析。


請儘量保持Filter實現的功能的單一性。如果必要的話,你可以將需求分解,由兩個(或者更多的)功能單一的Filter去實現總的功能需求。


其次,你應該明確這個Filter大致在整個Filter Graph的位置,這個Filter的輸入是什麼資料,輸出是什麼資料,有幾個輸入Pin、幾個輸出Pin等等。你可以畫出這個Filter的草圖。


弄清這一點十分重要,這將直接決定你使用哪種「模型」的Filter。譬如,如果Filter僅有一個輸入Pin和一個輸出Pin,而且一進一處的媒體類 型相同,則一般採用CTransInPlaceFilter作為Filter的基類;如果媒體類型不一樣,則一般選項CTransformFilter作 為基類。

再者,考慮一些資料傳輸、處理的特殊性要求。譬如Filter的輸入和輸出的Sample並不是一一對應的,這就一般要在輸入Pin上進行資料的緩衝,而 在輸出Pin上使用專門的線程進行資料處理。這種情況下,Filter的基類選項CSource為宜(雖然這個Filter並不是源Filter)。


當Filter的基類選定了之後,Pin的基類也就相應選定了。接下去,就是Filter和Pin上的程式碼實現了。有一點需要注意的是,從軟體設計的角 度上來說,應該將你的邏輯類程式碼同Filter的程式碼分開。下面,我們一起來看一下輸入Pin的實現。你需要實現基類所有的純虛函數,譬如 CheckMediaType等。在CheckMediaType內,你可以對媒體類型進行檢驗,看是否是你期望的那種。因為大部分Filter採用的是 推模式傳輸資料,所以在輸入Pin上一般都實現了Receive方法。有的基類裡面已經實現了Receive,而在Filter類上留一個純虛函數供用戶 重載進行資料處理。這種情況下一般是無需重載Receive方法的,除非基類的實現不符合你的實際要求。而如果你重載了Receive方法,一般會同時重 載以下三個函數EndOfStream、BeginFlush和EndFlush。我們再來看一下輸出Pin的實現。


一般情況下,你要實現基類所有的純虛函數,除了CheckMediaType進行媒體類型檢查外,一般還有DecideBufferSize以決定Sample使用記憶體的大小,GetMediaType提供支持的媒體類型。


最後,我們看一下Filter類的實現。首先當然也要實現基類的所有純虛函數。除此之外,Filter還要實現CreateInstance以提供COM的入口,實現NonDelegatingQueryInterface以暴露支持的接頭。


如果我們新增了自訂的輸入、輸出Pin,一般我們還要重載GetPinCount和GetPin兩個函數。
Filter框架的實現大致就是這樣。


你或許還想知道怎樣在Filter上實現一個自訂的接頭,以及怎麼實現Filter的內容頁等等。限於篇幅,筆者就不展開闡述了。其實,這些問題都能在SDK的示例專案中找到答案。其他的,關於在實際編程中應該注意的一些問題,筆者整理了一下,供大家參考。


1. 鎖(Lock)問題
DirectShow應用程式至少包含有兩條線程:一條主線程和一條資料傳輸線程。既然是多線程,肯定會碰到線程同步的問題。


Filter有兩種鎖:Filter對像鎖和資料流鎖。Filter對像鎖用於Filter級別的如Filter狀態轉換、BeginFlush、 EndFlush等;資料流鎖用於資料處理線程內,譬如Receive、EndOfStream等。如果這兩種鎖沒有搞清楚,很容易產生程序的死鎖,這一 點特別需要提醒



2. EndOfStream問題

當Filter接收到這個「消息」,意味著上一級Filter的資料都已經傳送完畢。在這之後,如果Receive再有資料接收,也不應該去理睬它。如果 Filter對輸入Pin上的資料進行了緩衝,在接收到EndOfStream後應確保所有緩衝的資料都已經處理過了才能返回。

3. Media Seeking問題


一般情況下,你只需要在Filter的輸出Pin上實現NonDelegatingQueryInterface方法,當用戶申請得到 IID_ImediaPosition接頭或IID_IMediaSeeking接頭時將請求往上一級Filter的輸出Pin上傳遞。當Filter Graph進行Mediaseeking的時候,一般會使用Filter上的BeginFlush、EndFlush和NewSegment。如果你的 Filter對資料進行了緩衝,你就要重載它們,並做出相應的處理。如果你的Filter負責給傳送出去的Sample打時間戳,那麼,在 Mediaseeking之後應該重新從零開始打起。

4. 關於使用專門的線程

如果你使用了專門的線程進行資料的處理和傳送,你需要特別小心,不要讓線程進行死循環,並且要讓線程處理函數能夠去時時檢查線程指令。應該確保在 Filter結束工作的時候,線程也能正常地結束。有時候,你把GraphEdit程序關掉,但GraphEdit工作仍在記憶體中,往往就是因為資料線 程沒有安全關閉這個原因。


5. 如何從媒體類型中獲取信息

譬如,你想在輸入Pin連接的媒體類型中,獲取視瀕圖像的寬、高等信息,你應該在輸入Pin的CompleteConnect方法中實現,而不要在SetMediaType中。


DirectX媒體對像(DirectX Media Objects,簡稱DMOs),是微軟提供的另一種流資料處理COM元件。與DirectShow filter相比,DMO有很多相似之處。對filter原理的熟悉,將會大大說明 你對DMO的學習。另外,DMO也因其結構簡單、易於新增和使用而倍受微軟推崇。

下面,我們來看一看DMO與filter的對比。

1. DMO比filter實現的功能要少很多,這使得DMO「體積」很小;

2. DMO使用起來比filter更有靈活性。DMO的使用不需要filter graph,應用程式可以直接與DMO交互。而DMO也可以通過一個DMO wrapper filter工作於DirectShow環境;

3. DMO總是同步處理資料,不像filter有獨立的資料傳送線程,需要考慮多線程編程問題;

4. 與傳統的編解碼管理器ACM、VCM相比,用DMO開發的編解碼器是關於COM的,更易於擴展。並且DMO支持多個輸入和多個輸出;

5. DMO不需要像filter一樣分配資料傳送的記憶體,而有DMO的使用者負責;


6. DMO是一個獨立功能模組,不需要像filter一樣連接成一條鏈路;


7. DMO不需要像filter一樣將資料「推」下去,資料的輸入輸出都是由DMO的使用者完成的;

所有這些優點,使得DMO成為微軟對於Encoder和Decoder開發的重點推薦模式。


DirectX 9.0 SDK中,微軟更是把DMO從DirectShow中分離出來,而對於一些transform filter,微軟也推薦用DMO的方式來替換。


關於DMO的使用方式,目前大概有兩種:

一種是應用程式直接使用DMO,另一種就是在DirectShow filter中的套用。


後者比較簡單,只是使用了一個DMO wrapper filter。在DirectShow應用程式中,DMO是對用戶透明的,所有使用DMO的工作均由DMO wrapper filter來完成。參見下面的程式碼。


// Create the DMO Wrapper filter.
IBaseFilter *pFilter;
HRESULT hr = CoCreateInstance(CLSID_DMOWrapperFilter, NULL,
CLSCTX_INPROC_SERVER, IID_IBaseFilter,
reinterpret_cast(&pFilter));

if (SUCCEEDED(hr))
{
// Query for IDMOWrapperFilter.
IDMOWrapperFilter *pDmoWrapper;
hr = pFilter->QueryInterface(IID_IDMOWrapperFilter,
reinterpret_cast(&pDmoWrapper));

if (SUCCEEDED(hr))
{
// Initialize the filter.
hr = pDmoWrapper->Init(CLSID_MyDMO, DMOCATEGORY_VIDEO_EFFECT);
pDmoWrapper->Release();

if (SUCCEEDED(hr))
{
// Add the filter to the graph.
hr = pGraph->AddFilter(pFilter, L"My DMO");
}
}
pFilter->Release();
}
而對於DMO的直接使用,以下幾點是要特別注意的。

1. 在處理資料之前,必須為每條輸入輸出stream設定media type(Optional stream除外);

2. 從DMO從獲取的media type未必包含format塊,但是在給DMO設定media type時,務必帶上這部分信息(MIDI除外);

3. 應用程式必須自己負責分配資料緩衝。


緩衝的大小可以通過使用DMO的IMediaObject::GetInputSizeInfo或 IMediaObject::GetOutputSizeInfo得到。DMO使用的資料緩衝也是一個COM對象,支持ImediaBuffer接頭,與 DirectShow filter的Media Sample類似。


4. 一般的DMO依次使用IMediaObject::ProcessInput和IMediaObject::ProcessOutput處理資料,In-Place的DMO使用IMediaObjectInPlace::Process處理資料。


兩套方法不能混用。

5. 在使用ProcessOutput時,如果返回的標記是DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE,說明資料的資料還沒有完全取出,需要再次使用ProcessOutput。


6. 所有輸入資料都已輸入完成,應該使用DMO的IMediaObject:Discontinuity方法。


7. 如果你想中斷資料處理流程,使用DMO的IMediaObject::Flush。
區別兩種不同的可丟棄stream,標記分別為DMO_OUTPUT_STREAMF_OPTIONAL和DMO_OUTPUT_STREAMF_DISCARDABLE。注意,後者是要設定media type的。

2009年9月3日

.NET Compact Framework and the .NET Framework, What Are the Difference?

few features that differentiate between .NET Compact Framework and the .NET Framework:

1 – Common Language Runtime

The common language runtimes in both Frameworks benefit from managed code execution, just-in-time (JIT) code compilation, and garbage collection. They support the Common Language Specification (CLS).
Both Frameworks have built-in primitive types as well as other types that you can use and derive from when you build your application.

2 – Classes and Types

The .NET Compact Framework supports a subset of the .NET Framework class library. This subset is appropriate for applications that are designed to run on resource-constrained devices and is semantically compatible with same-named classes in the .NET Framework.

3 – Assemblies and Global Assembly Cache

The .NET Compact Framework does not currently support multi-module assemblies, but does support satellite assemblies.

4 – How about Deploying Applications ?

To deploy an application, you can easily copy the assembly to the target device by using a cable from the desktop computer, its infrared port, or a wireless Internet or intranet connection. In Microsoft Visual Studio 2005, you can deploy directly to the device while debugging.

5 – ASP.NET Features

The .NET Compact Framework is primarily a rich client platform and does not provide ASP.NET support. To develop Web pages for mobile devices, you can use ASP.NET mobile Web controls. To develop Web pages for personal computers or Web service providers, see your ASP.NET documentation.

To get the latest .NET 3.5 Framework hosting enabled hosting plan and support, log on to SeekDotNet.com to view more information realize how you can deploy your .net Framework application right away.

2009年8月20日

What is the Intent

在一個Android應用中,主要是由四種組件組成的,這四種組件是 Activitty, service, boardcase receiver,content provider 。 而這四種組件是獨立的,它們之間可以互相調用,協調工作,最終組成一個真正的Android應用。在這些組件之間的通訊中,主要是由Intent協助完成 的。Intent負責對應用中一次操作的動作、動作涉及數據、附加數據進行描述,Android則根據此Intent的描述,負責找到對應的組件,將 Intent傳遞給調用的組件,並完成組件的調用。因此,Intent在這裡起著一個媒體中介的作用,專門提供組件互相調用的相關信息,實現調用者與被調 用者之間的解耦。
例如,在一個聯繫人維護的應用中,當我們在一個聯繫人列表屏幕(假設對應的Activity為listActivity)上,點擊某個聯繫人後,希望能夠 跳出此聯繫人的詳細信息屏幕(假設對應的Activity為detailActivity),為了實現這個目的,listActivity需要構造一個 Intent,這個Intent用於告訴系統,我們要做「查看」動作,此動作對應的查看對象是「某聯繫人」,然後調用startActivity (Intent intent),將構造的Intent傳入,系統會根據此Intent中的描述,到ManiFest中找到滿足此Intent要求的Activity,系 統會調用找到的Activity,即為detailActivity,最終傳入Intent,detailActivity則會根據此Intent中的描 述,執行相應的操作。

一、抽象描述要描述什麼
在Android參考文檔中,對Intent的定義是執行某操作的一個抽象描述(確實很抽象)。我們先來看看這裡的抽象描述,到底描述了什麼。
首先,是要執行的動作(action)的一個簡要描述,如VIEW_ACTION(查看)、EDIT_ACTION(修改)等,Android為我們定義了一套標準動作:
  • MAIN_ACTION
  • VIEW_ACTION
  • EDIT_ACTION
  • PICK_ACTION
  • GET_CONTENT_ACTION
  • DIAL_ACTION
  • CALL_ACTION
  • SENDTO_ACTION
  • ANSWER_ACTION
  • INSERT_ACTION
  • DELETE_ACTION
  • RUN_ACTION
  • LOGIN_ACTION
  • CLEAR_CREDENTIALS_ACTION
  • SYNC_ACTION
  • PICK_ACTIVITY_ACTION
  • WEB_SEARCH_ACTION
此外,我們還可以根據應用的需要,定義我們自己的動作,並可定義相應的Activity來處理我們的自定義動作。

其次,是執行動作要操作的數據 (data),Android中 採用指向數據的一個URI來表示,如在聯繫人應用中,一個指向某聯繫人的URI可能為:content://contacts/1。這種URI表示,通過 ContentURI這個類來描述,具體可以參考android.net.ContentURI類的文檔。
以聯繫人應用為例,以下是一些action / data對,及其它們要表達的意圖:
  • VIEW_ACTION content://contacts/1 -- 顯示標識符為"1"的聯繫人的詳細信息
  • EDIT_ACTION content://contacts/1 -- 編輯標識符為"1"的聯繫人的詳細信息
  • VIEW_ACTION content://contacts/ -- 顯示所有聯繫人的列表
  • PICK_ACTION content://contacts/ -- 顯示所有聯繫人的列表,並且允許用戶在列表中選擇一個聯繫人,然後把這個聯繫人返回給父activity。例如:電子郵件客戶端可以使用這個Intent,要求用戶在聯繫人列表中選擇一個聯繫人

另外,除了action和data這兩個重要屬性外,還有一些附加屬性:
  • category(類別),被執行動作的附加信息。例如 LAUNCHER_CATEGORY 表示Intent 的接受者應該在Launcher中作為頂級應用出現;而ALTERNATIVE_CATEGORY表示當前的Intent是一系列的可選動作中的一個,這 些動作可以在同一塊數據上執行。
  • type(數據類型),顯式指定Intent的數據類型(MIME)。一般Intent的數據類型能夠根據數據本身進行判定,但是通過設置這個屬性,可以強制採用顯式指定的類型而不再進行推導。
  • component(組件),指定Intent的的目標組件的類名稱。通常 Android會根據Intent 中包含的其它屬性的信息,比如action、data/type、category進行查找,最終找到一個與之匹配的目標組件。但是,如果 component這個屬性有指定的話,將直接使用它指定的組件,而不再執行上述查找過程。指定了這個屬性以後,Intent的其它所有屬性都是可選的。
  • extras(附加信息),是其它所有附加信息的集合。使用extras可以為組件提供擴展信息,比如,如果要執行「發送電子郵件」這個動作,可以將電子郵件的標題、正文等保存在extras裡,傳給電子郵件發送組件。

總之,action、 data/type、category和extras 一起形成了一種語言。這種語言使系統能夠理解諸如「查看某聯繫人的詳細信息」之類的短語。隨著應用不斷的加入到系統中,它們可以添加新的action、 data/type、category來擴展這種語言。應用也可以提供自己的Activity來處理已經存在的這樣的「短語」,從而改變這些「短語」的行 為。

二、Android如何解析Intent
在應用中,我們可以以兩種形式來使用Intent:
  • 直接Intent:指定了component屬性的Intent(調用setComponent(ComponentName)或者setClass(Context, Class)來指定)。通過指定具體的組件類,通知應用啟動對應的組件。
  • 間接Intent:沒有指定comonent屬性的Intent。這些Intent需要包含足夠的信息,這樣系統才能根據這些信息,在在所有的可用組件中,確定滿足此Intent的組件。
對於直接Intent,Android不需要去做解析,因為目標組件已經很明確,Android需要解析的是那些間接Intent,通過解析,將 Intent映射給可以處理此Intent的Activity、IntentReceiver或Service。
Intent解析機制主要是通過查找已註冊在AndroidManifest.xml中的所有IntentFilter及其中定義的Intent,最終找 到匹配的Intent。在這個解析過程中,Android是通過Intent的action、type、category這三個屬性來進行判斷的,判斷方 法如下:
  • 如果Intent指明定了action,則目標組件的IntentFilter的action列表中就必須包含有這個action,否則不能匹配;
  • 如果Intent沒有提供type,系統將從data中得到數據類型。和action一樣,目標組件的數據類型列表中必須包含Intent的數據類型,否則不能匹配。
  • 如果Intent中的數據不是content: 類型的URI,而且Intent也沒有明確指定它的type,將根據Intent中數據的scheme (比如 http: 或者 mailto: ) 進行匹配。同上,Intent 的scheme必須出現在目標組件的scheme列表中。
  • 如果Intent指定了一個或多個category,這些類別必須全部出現在組建的類別列表中。比如Intent中包含了兩個類別:LAUNCHER_CATEGORY 和 ALTERNATIVE_CATEGORY,解析得到的目標組件必須至少包含這兩個類別。

2009年8月13日

Convert int to char

大部分的方式是使用itoa()這個function, 用法如下:
char * itoa ( int value, char * str, int base );
ex:
int
i=3;
char
buffer [10];
itoa (i,buffer,10);
其中 i 是要轉成char的integer, buffer是要存的char, 而10表示轉成十進位.

但是itoa()在某些Linux底下就是無法被使用, 簡單說就是無法被系統reference到. 此時可以使用另外一種方式sprintf(), 用法如下:
int sprintf ( char * str, const char * format, ... );
ex:
char
buffer [50];
int n, a=5, b=3;
n=sprintf (buffer, "%d plus %d is %d", a, b, a+b);
其中 buffer 裡就是"引號"中的字元(char), 而 n 則是"引號"的位元大小(int).

2009年8月10日

Android Process Lifecycle

Android Process Lifecycle

Android系統記憶體不足時, 就需要把舊的或不需要用的應用程式移除. 如同之前的Activity生命週期所介紹, 這個移除的決定是由應用程式所處的狀態來判斷. 一般來說,當需要移除應用程式時, 系統將會做排序, 然後從最不重要的開始移除, 以下是移除時的考量順序:



1. 最早被移除的是 Empty Process(空行程):
Empty process 是指那些沒有跟Activity綁定, 也沒有跟任何的應用程式元件(比如Service或IntentReceiver)綁定在一起的process, 這些空行程一定是最早被系統考慮移除的.


2. 第2順位考慮被移除的是 Background Activity.
Background Activity指這個activity是無法被使用者看到的的情況, 表示Activity已處於stop的狀態, 系統移除這些 Activity 是安全的. 通常有多個 Background Activity同時運行, 這些Activity被存放在一個 LRU (least recent used) list中, 系統可以根據 LRU list 判斷哪些 Activity可以被移除, 哪一個應該是最先被移除的.

3. 第3順位被移除的是 Service Process.
在 Android 應用程式裡, 有一種沒有 UI 的類別(android.app.Service), 稱之為 Service. Service Process 通常是由startService()方式啟動. 簡單來說,Service 屬於 background(背景)程序, 透過背景程序, 我們可以製作一些不需要 UI 的功能, 例如: 在背景撥放音樂, 上傳或下載文件等. 系統通常會保護它, 除非真的沒有記憶體可用.

4. 接著輪到 Visible Activity / Visible Process:
Visible Process是一個可被Visible的, 但是沒有顯示在最上端 (onPause被使用時). 舉例來說, 當一個新的對話框Activity出現時, 原來的Activity仍然visible, 仍然被系統認為是重要的, 通常不會被移除. 但若不得不移除時, 由於屬於 Paused狀態, 相對來說, 它已經處於一個比較安全的位置.

5. 最後被移除的是 Foreground Activity:
Foreground是一個在螢幕最上端與使用者做互動的Activity, 它的優先權最高. 原則上會是最後一個被移除的程式, 除非這個Activity所需要的記憶體大小已經超出系統所能給的. 系統之所以會移除這些程序, 是為了不讓使用者介面停止回應.

由於Android應用程式的lifecycle並不是由程式本身直接控制的, 而是由系統平台進行管理. 所以對於開發者而言, 許要瞭解不同元件 Activity, Serivce 和 IntentReceiveer的Lifecycle. 要切記: 如果元件使用不當, 雖然正在進行重要的Process, 仍有可能被系統主動移除.

Android Activity生命週期簡介

Android Activity生命週期簡介

前面有提到何謂Activity: 最簡單的就是把Activity看成一個User Interface Program. 它會提供使用者一個互動式的介面功能. 當然一個activity通常不只一個UI, 所有的Activity在系統裏由Activity stack 所管理, 當一個新的Activity被執行後,它將會被放置到stack的最頂端,並且變成"running activity", 而之前的Activity原則上還是存在stack中,但不會是在foreground(前景)的情況.

一個Activity基本上有四個狀態 Active, Paused, Stopped, Dead:
Activity 處在Paused狀態時, 使用者無法與原來的 Activity 互動.


Dead/Inactive (已回收或未啟動)
Dead狀態是 Activity 尚未被啟動, 已經被手動終止, 或已經被系統回收的狀態.
要手動終止 Activity, 可以在程式中呼叫 finish 函式.
如果是被系統回收, 可能是因為記憶體不足, 系統根據記憶體不足時的回收規則, 將處於Stopped狀態的 Activity 所佔用的記憶體回收.


下面的流程圖說明一個Activity運行的情況, 長方形代表callback methods(回呼函式), 可以做出想要處理的事情, 有顏色的部份就是實際Activity會處於的狀態.






上圖有三個主要 lifetime :

1. Entire lifetime: 一個Activity的Entire lifetime是由onCreate()開始, 一直到onDestroy()結束.
一個Activity可以把所有的資源設定寫在onCreate中, 一直到onDestroy()時再釋放出來.

2. Visible lifetime: 一個Activity的Visible lifetime是指在onStart()到onStop()之間.
在這段時間內,使用者可以在螢幕上看見Activity, 要注意這個"Visible"是個形容, Activity不見得一定在foreground(前景)跟使用者直接互動.

3. Foreground lifetime: 一個Foreground lifetime 指 onResume() 到 onPause() 之間. 這個時期的Activity是在其他的Activity的前面, 且可以直接跟使用者進行互動. 所以這段時期指的就是圖中的Activity is running.


簡單的總結幾個動作:
onCreate()用來做程式的初使化動作;
onDestory()通常都拿來把onCreate()時的資料做釋放的動作;
onPause()時把需要保存的資料保存;
onResume()把保存的資料拿回來使用.


onCreate -> onStart -> onResume
啟動一個 Activity 的基本流程是: 分配資源給這個 Activity(onCreate), 然後將 Activity 內容顯示到螢幕上(onStart), 在一切就緒後, 取得螢幕的控制權(onResume), 使用者可以開始使用這個程式。

onPause(1) -> onCreate(2) -> onStart(2) - onResume(2) -> onStop(1)
先凍結原本的 Activity, 再交出直接存取螢幕能力(onPause )的過程. 直到 Activity 2 完成一般啟動流程後, Activity 1 才會被停止.

onPause(2) -> onRestart(1) -> onStart(1) -> onResume(1) -> onStop(2) -> onDestroy(2)
按 Back鍵可以回到原本的 Activity。

onPause -> onStop -> onDestroy
如果程式中有直接呼叫 finish 函式來關閉 Activity的話, 系統會暫停(Pause), 停止(Stop)然後銷毀(Destroy)。

onCreate -> onStart -> onResume
被回收掉的 Activity 一旦又重新被呼叫時,會像一般啟動一樣再次呼叫 Activity 的 onCreate 函式.

Android 的名詞觀念

Android 的名詞觀念

在真正進入 Android 程式設計前,必須先了解以下幾個名詞觀念。

1. Android Package (.apk):
包含應用程式本身,以及相關的資源檔案。將 apk 套件下載到 Android 手機後,即可安裝至手機上。Android Development Kit 可自動將 apk 套件下載至模擬器或實體手機。
記得前面我們在 Eclipse 中若要檢視HelloWorld程式時, 是利用 Package Explorer 來查看整個程式的結構的.

2. Task
Task 為"工作"的意思, 可以看成 Application (應用程式)本身. 也就是手機上的應用程式的圖示,使用者可點擊圖示啟動 task。從開發者的角度來看

3. Process
Process 的定義上,指的是"執行中的程式",在 Android 的Application 環境中,代表低階的執行程式,屬於 Kernel 部份。一個 package 的所有程式,都是在一個 process 中執行。
在一般情況下, Android 應用程式都有一個自已的 Process. 在 Android 系統裡, Process 的生命週期(life cycle) 並不是直接由 Android 應用程式本身來決定, 而是由系統來決定.
Android 的 process 有五種類型:foreground process、visible process、service process、background process 與 empty process。

4. Activity
Activity是Android開發中非常重要的一個基礎類。就字面上來說, Activity 就是"活動"的意思. 可以簡單的解釋為, Activity 是一個與使用者互動的物件(object)。
舉例來說:一個EMail程式,就可能包含三個activity: 有列出郵件的 Activity 1, 顯示郵件內容的activity 2, 及 撰寫郵件 activity3.
Activity 大概可以分成四種生命狀態:
一個Activity在螢幕的最上層時(堆疊的最頂端),它就是屬於activerunning的狀態
如果一個Activity失去focus(焦點)但還看得到它的畫面(例如:一個Activity畫面是被蓋掉部份畫面或一個半透明的情況), 這個Activity則處在 paused的狀態。這個失去焦點的Activity它還是完全活著的, 並沒有消失。(活著的意思是指,Activity本身所有的狀態及資料都還是存在,與管理程式保持連繫). 但這種paused的activity, 會在某些情況下消失, 例如當系統的記憶體不夠用時, 系統會自動判斷, 把不重要的activity移除.

如果一個Activity被其它的Activity完全的遮住時, 這個被遮的Activity處於stop的狀態, 但它仍然保有全部的狀態及資料. 由於它已不被使用者看見,所以它的畫面是被隱藏起來的, 當系統記憶體不足時,這種stop狀態的activity是最先被系統考慮移除以釋放記憶體.

當一個Activity處於pause或stop的狀態時, 系統可以要求Activity結束(finish)或移除(kill)它. 當它需要再度呈現在使用者面前時, 它必需要能完整的重新啟動(restart)及回復(resume)先前的狀態。

5. View
簡單來說,android.app.View 類別就是手機的 User Interface. View 負責繪製UI與 event (處理事件). Android 利用 View 建立所謂的 Widgets(元件), 利用 Widget 就可以製作互動式的使用者介面(interactive GUI).

2009年6月25日

清晨, 發現自己生活在緩慢的空間中

這幾個月來老是忙忙碌碌的生活著、走著、呼吸著、手舞足導著。
或許這是台北人生活的本質能力,能力歸屬在於城市所賦予的那種能力與步調。
是阿! 我是一個台北人。
我本能性的想要拒絕,也因此我在腦袋裡思索著一點點可以抗拒這個名詞的一丁點的文字..... Running
可惜的是除了[貢寮]、[獅頭]、[爸媽]這樣的文字之外我沒有能力跟立場去否認這個不屬於我的名詞。
感覺像是被冠上了甚麼而且無可奈何那樣的歸順。

耳朵迴盪著蘇打綠的歌詞[我們都是一個人加上一個人的長像...],
音符跳耀著在我的腦袋裡!
出現的畫面是今天早晨那緩慢的空間,我想我走進了一個緩慢的空間,而且就那短短的半小時。
像是捷運開進了一面薄膜般的邊線。
而我到了一個不屬於台北的台北!

仔細看看路上吸壤來往的人們,大部分的老人是今天的景象,這是一種徵兆嗎?
或許是一種徵兆。
因為緩慢的感覺需要有點緩慢的佈景,也需要緩慢的氣氛與感觸。
感觸是最直接的!
因為像是肌膚受電般的快速傳達。

該上班了,好像需要寫上上萬個字形容一個有感覺的早上。
好像需要再延續一點或是更多的秒、分、時去闡述今天雙手半徑世界的初發感。

像音樂那樣[拉...啦啦啦啦啦啦....啦啦啦啦]:蘇打綠這樣的延續著...
我也延續著,延續著我的生命,延續著我的感覺,延續著無可厚非的不太在意。

2009年6月5日

以手為半徑是我的世界範圍!

以手為半徑是我的世界範圍!

在這個世界裡頭沒有爭吵,沒有公司派的勾心鬥角,當然也沒有那種跟你搏軟然後在背後捅你一百多刀的smile!

所以這是屬於我的半徑! 閒人勿進,因為結界的關西。
半徑內的咖啡、半徑內的電波、半徑變成了一個獨立的單位在這個獨立的世界與國家中。

而我的半徑中我是完全自由的非固體!
因為我是法系人物!

這世界上第一套的浮潛裝備

可能是夏天天氣的氣氛感招吧!
今天一整個下午處在海洋的空間感中,當然會這樣說的意思就是我依然在這惱人的都市裡頭並且活在社會裡。

突然而來的一股腦的衝動促使我的著去收集我一直想要的浮潛裝備!
打從哪時候我一直囔囔著自己要去浮潛的? 三年、五年? 管他的!但是就是很久。
印象中應該是第一次到墾丁去浮潛之後就一直想做的事情。
拖了久了! 真得很久~

既然有FU了! 有什麼比這還重要? Answer是NO! 這他媽的世界上沒什麼東西是比FU更重要的了。
所以晚上就去COSCO買下這世界上獨一無二的第一套浮潛裝備!

在靈魂的感招下在這幾百年沒寫的BLOG加上一筆記錄!

Maybe有一天這會是一篇感招自己心靈,遙遠的傳送電波到台灣海峽、太平洋還是巴士海峽的一篇電文。
先記錄這歷史性的一刻!
The moment in history~

當在海裡遇到鯊魚的武器 : 蛙鞋


躲在水裡的利器: 呼吸管與蛙鏡


大團圓!


突然間讓我想起了碧海藍天這部電影裡頭的尚雷諾(天阿!他變得好老)。

2009年4月30日

就是自己的命運吧!我想~

努力的經營,努力的去做自己想做的,努力的去給出自己想給的。
原來到頭來感覺自己可有可無的。

或許自己太過努力的想要付出所以產生反效果吧。
有很多事情不管自己怎樣努力,怎樣的經營都無效。
maybe這就是命運吧!

2009年4月21日

[C++] Virtual inheritance

基本類別重複的問題會發生在多重繼承的結構下.
因此為了解決這個問題就需要用到 virtual inheritance,
假設P1 and P2 都繼承了 A class,而P3繼承了P1 and P2.
這種inheritance的關西下在P3裡就會有兩份的A, 存取的話就需要使用P1::x and P2::x去存取你想要的成員。
但是問題是,我們不需要兩份A!
virtual inheritance 可以解決這個問題讓多重繼承不會有重複的類別重複。

2009年3月13日

2009-03-13 的我

天曉得我來自哪個象限的,我想我媽也不知道。
因為沒人認為我試地球人阿~

被問過我的翅膀是什麼顏色的,我直覺就認為一定是黑色的,
為甚麼?
我哪知道為甚麼!

咖啡跟我有緣份嗎?
當然有阿!因為我每天都會親吻她不下數十次,那是種超溫柔的香醇!
有什麼比她更讓我幸福的呢?

如果有天我可以選擇跳躍,我要跳躍到那天空與世界的結界去,
因為我喜歡消失在那結界的感覺,可能我可以享受那一秒剎那的消失感!
也maybe我就這樣失去所有的感覺~

不過沒辦法阿! 我是法系的人物,儘管我只是個吟遊詩人~

於是乎! 這是我的最新介紹~

2009年3月7日

超自然品牌 ...


有些字句總是充滿回憶的!
有些東西總是充滿記憶的!

記憶跟回憶佔據腦袋很大的一部分,於是乎我很好奇動物是否也有記憶與回憶?

有時候看到熟悉的文字、看到熟悉的物件、從抽屜中無意看到塵封很久的物品。
然後你會記起很多東西。
像是深深沉沉的壓在櫃子底下的那見露出衣角的衣服!

那是精華吧! 感覺是無可取代的!
因為不會在有第二件了,無可取代般的就是獨特無比。

想著、回憶著,然後為他上色~
於是乎腦袋中那個記憶拼圖突然間出現了一塊!

神奇吧!
像是坐在山與湖中那懸空咖啡座喝著一杯香醇的咖啡,
又或是拿著杯有著三塊冰的威士忌一樣的感覺。

平靜、微笑著、享受~這是我的某一天下午間的那一個被我標籤的1.5個小時~
不會在有第二個這樣的1.5個小時~

2009年3月5日

喃喃的自語著

我是一個很快樂的人~但原來要讓一個心鎖上的人快樂是很難的~不知不覺得感覺到有點sad~鎖起來比打開好嗎? i don't think so~

我想~ 我真的活在我的世界吧 ~ right~

um.... 果然是挺挫折的~

而且原來我的腦袋有太多地方沒有開發到....

突然想到~ 會不會我會向村上的書一樣,總是有種會死在自己的世界那樣的感受

村上很多書看了之後就會感覺到像是死在一個自己的空間裡頭,但是想像力還是持續的在飛揚著~~

所以黑暗跟奔騰著的幻想思考力以同樣的速度馳乘著~~~

於是! 每次看完一次村上的書,會有段時間進入那樣的結界一樣的久久走不出來。

我想我真的不是法系的人物!

我真的很希望自己是法系的人物! 因為我喜歡法師~

但是我想我應該是屬於吟遊詩人或是遊俠般的人物

但我想我永遠走不出結界也就沒法換個role再進去了吧! 就這樣的遊在結界的空間裡頭那樣的生活著~

2009年2月28日

2009/02/28 編寫自己

天使與魔鬼間有著很棒的協定,上帝與撒旦像是玩著人遊戲一樣的把人類當賭注!
條件是不能干涉而只能從旁影響。

不免強的情況下去玩弄人,是玩弄?是意志? 個人覺得是好笑的一場遊戲,
不幸的我是那眾多棋子中的一顆!

左右我的是什麼? 撒旦還是上帝? 我接近天使還是魔鬼?
我看過兩本聞名於世的聖經! 我求知於兩種對立信仰!
最終,我決定我還是我。
他們不過是他媽的玩家,而我不過是簡單的人類。

長不大的眼神? 邪氣的眼神?
可愛? 玩家?

長時間天秤的傾倒於一邊,短時間頃倒於一邊。
其實我渴望一股力量?上帝?撒旦?

結果我還是我,依舊不變~

2009年2月20日

鞋貓夫人,Madame!!!



能夠給的我都給了 你要挺起胸膛更坦蕩蕩的活
能夠說的我都說了 只是緊要關頭不一定行得通

這個世界轉眼就要崩毀
為了你我都開心so be my baby
何必擔心明天的天氣
也許人生不必要so complicated
寂寞 又算什麼 並不是每個夢想都能有著落
再說 我還能夠 邀請你與我一起活在夢裡頭

我的強壯勇敢都只為你
你是我眼前的唯一so be my baby
何必擔心未來會有風雨
也許人生不必要so complicated
失落 又算什麼 並不是每個夢想都會有感動
再說 我還能夠 邀請你與我一起活在夢裡頭
你可以 笑我做夢天生好手 偏偏 我理直氣壯四處大步走 ya~你是忌妒我單純你沒有 比起你我卻輕輕鬆鬆更舒暢又更自由
再說 我還能夠 邀請你與我一起活在夢裡頭
你可以 笑我做夢天生好手 偏偏 我理直氣壯四處大步走 ya~
你是忌妒我單純你沒有 比起你我卻輕輕鬆鬆更舒暢又更自由

能夠給的我都給了 你要挺起胸膛堅強的活
能夠說的我都說了 不過不巧遇上another rainy day
擦乾眼淚 不要哭了 你要像我一樣驕傲才對
我是英雄 你就跟著我 我會帶你看遍世界的美

能夠給的我都給了 你要挺起胸膛坦蕩蕩的活
能夠說的我都說了 但若運氣不好失敗我不賠
擦乾眼淚 不要哭了 你要像我一樣驕傲才對 這才對
我是英雄 你就跟著我 我會帶你看遍世界的美

Yeah dude, you gonna be in my group,
and that's why I wanted you, see all the thing I will do for you
Yeah it's true 眼睛要看著遠方 意志鋼鐵般堅強 跌倒也不要害怕
我最強 我最強 世界上只我最強 我可以為你抵擋黑暗的力量
好快活 好快活 跟著我你會快活 因為我是獨一無二的我 Yeah~

吳聽徹【她】MV


作詞/曲:吳聽徹

琴弓面對著我的手掌
它們的誤會大過於緊張
氧氣稀薄的很平常
怎麼是這樣

琴弓拉扯著絃的去向
顫動的旋律像在說著謊
寂寞理所當然的住進左心房
血液也不抵抗 靜靜的在缺氧 不說話

唉 就這樣吧
(愛 就是這樣吧)

習慣上了一個人是怎樣
一不聽不想就開始慌張
用遺忘收場 我還要堅強

開始習慣一個人獨自對話
學著一個人的天份我有吧
而我也開始忘了妳是她

我試著慢慢忘了 她

【希臘神話】邱比特與賽姬〈Cupid and Psyche〉

轉載: http://mizuya.pixnet.net/blog/post/13412420

希臘神話中,邱比特與賽姬的故事是少數以喜劇結尾的故事。最近又開始看了希臘神話,就覺得乾脆自己來整理一下吧!

賽姬(Psyche, 意為「心靈」或「蝴蝶」)是國王的第三個女兒,她及兩個姊姊都貌美如花,小公主賽姬尤其美得不可方物。她的美貌使得大家將她視為女神般地愛慕與尊敬者,甚 至認為她就是美的女神的化身。因此,前去祭拜愛芙洛黛蒂(Aphrodite,真正的美神【羅馬名Venus,維納斯】)的人就變得少之又少,祂的聖壇變 得破舊不堪,積滿許多灰塵。這下子,便惹惱了尊貴的愛芙洛黛第女神。



畫家筆下的Psyche。

Jean-Baptiste Greuze, 1725-1805 (1786)




The Awakening of Psyche, by Guillaume Seignac, 1870-1924
女神不僅阻止任何向賽姬求婚的人,祂還命令祂的兒子─愛洛斯(Eros,愛神【羅馬名Cupid,邱比特】)用箭去射她,要讓賽姬愛上世界上最醜陋的男子。


人民對賽姬的崇拜,終於招來女神的忌妒,命令祂的兒子去陷害她。

Psyche Honored by the People,
Luca Giordano (1634-1705)


當邱比特飛到熟睡的賽姬床前,準備用金箭射向賽姬時,祂也被賽姬的美貌所吸引了,著迷地望著賽姬那絕俗的容貌,因而手中的金箭一不小心刺破了自己的手指,從此邱比特就愛上了賽姬,可是如此一來,祂就違反母親的命令了。

苦惱的邱比特就去請求阿波羅(Apollo,太陽神)的幫助,此時賽姬的父母也因賽姬的無人求婚而苦惱,正來到阿波羅的神殿請求神諭,阿波羅於是向賽姬的父親降下一道神諭:「這個女孩的丈夫是神與人都鬥不過的惡魔,正在山巔等待著她!」

同時,邱比特也化為一條大蛇,來到國王的夢中,要國王將賽姬帶到西方的山崖邊,他自會去迎娶她。否則,他將施法讓王國變得窮困,災禍連連。


賽姬前往嫁給妖怪的路上,完全見不到任何喜悅。


The Wedding of Psyche, by Sir Edward Burne-Jones (1895)

於是,傷心無助的賽姬決定嫁給大蛇以換取大家的幸福。一行人將賽姬送到山崖邊後就離去 了。恐懼的賽姬一直等到夜晚,才出現一陣柔和的西風,將賽姬送到一座森林,森林深處矗立著一座金碧輝煌的城堡。賽姬進入城堡後,站在明亮光潔的大廳,四處 無人,但此時她的耳邊卻出現人聲:「我們是你的僕人,遵照你的旨意做事。」

於是賽姬就這樣在城堡住了下來,城堡裡除了她不見半個人,平常她就享用美酒佳餚,有事 就使喚看不見的僕人,過著十分休閒的日子。時間過得很快,一個月過去了,賽姬幾乎也接受了這樣的事實。大蛇告訴她,因為他長得很怪異,所以白天他不會出 現,可是夜晚他會來陪伴賽姬,但是有一個條件,就是一到晚上,鍾響十二下之後,賽姬必須把所有的燭火弄熄,這樣他才敢出現在這個扆間裡。如果賽姬違反了這 個誓言,賽姬將永遠的失去他。

賽姬答應了這樣的請求,雖然好奇丈夫的長相,但她也不曾違反過她答應過的誓言。半年很快的過去了,對於賽姬而言,晚上能和大蛇共渡是最快樂不過的了,因為大蛇不像兇猛的怪物,反而既溫柔又體貼,帶給她不少歡樂,也讓她不再懼怕自己的丈夫。

一年過去了,賽姬開始想念起家人來了,她將思念家人的心情告訴自己的丈夫,邱比特知道賽姬的姐姐們將會毀了自己的婚姻,所以阻止賽姬的姐姐們來探望她。然而,賽姬卻愈發想念自己的親人。於是邱比特最終還是允許了賽姬的姐妹們來到他們所居住的地方。

賽姬的姊姊們來到了賽姬居住的城堡,三姊妹相擁而泣,到了晚上姊姊們才離開。姊姊們一連好幾個月都來看她,漸漸的,她們覺得賽姬嫁給大蛇並不是什麼壞事,而且看賽姬吃、穿都比她倆好時,一股妒火便油然而生。


賽姬向姊姊們獻寶,導致了她和邱比特的分離。


Psyche Showing Her Sisters Her Gifts, by Jean-Honore Fragonard (1732-1806) (1753)

姊姊們給了賽姬一把刀子,要賽姬殺了大蛇,這樣她們一家人就可以團聚了。賽姬不肯,於是姊姊又給了賽姬一個燭火,要她小心藏在身邊,她們深信,只要賽姬看見大蛇的醜陋的真實容貌,一定就會下得了手。

姊姊們不斷地造謠,挑撥賽姬對自己丈夫的信任,最後賽姬還是敵不過自己的好奇心,終於決定趁晚上丈夫熟睡的時候,要好好地瞧一瞧他的面貌。

當天晚上,等到枕邊人真正的熟睡之後,賽姬便悄悄的起床來,拿著刀子和點燃燭火小心的靠近自己的丈夫,想一睹他的盧山真面目。

賽 姬拿著燭火慢慢靠近,結果卻讓賽姬嚇一跳,只見一個俊俏少年躺臥在床上。金色的鬈髮、象牙白的皮膚,自己的丈夫哪裡是一條大蛇呢?賽姬傻傻地看著他,而忘 了她與大蛇之間的約定。結果,蠟油一不小心滴到了邱比特的肩上,邱比特驚醒過來。邱比特發現賽姬竟毀了他倆的誓言,傷心的坐了起來,他說:「噢!愚蠢的賽 姬啊,你就是這樣回報我的愛嗎?在違背我母親的命令與讓你成為我的妻子之後,你還認為我是妖怪想砍下我的頭嗎?回去找建議你這樣做的姊姊們吧!去找建議你 這樣做的人吧!我給你最大的懲罰就是妳再也見不到我了。愛情是不能存在懷疑之中的。」(註1)說完,邱比特就像一陣風似的不見了,就連城堡也消失了。


賽姬點蠟燭窺見了邱比特的那一剎那,也是他們分離的開始。

Love and Psyche, Jacopo Zucchi ,1540-1596, (1589)




Cupid and Psyche, by Giuseppe Cammarano


Simon Vouet, 1590-1649


沒有了信任,愛情( Cupid )也無法留在心靈( Psyche )之中。也因此,邱比特只能選擇離開賽姬了。

Cupid and Psyche, by Jacques-Louis David, 1748-1825 (1817)


Cupid fleeing the sleeping Psyche, by Francois-Edouard Picot (1786-1868) (1817)




Psyche Abandoned by Cupid, by Charles-Antoine Coypel, 1694-1752 (1730)

賽姬坐在消失的地板上,傷心後悔,最後她告訴自己,無論如何,她一定要挽回邱比特的心。於是她開始不眠不休地尋訪她的丈夫。有一天她來到迪蜜特(Demeter,穀物女神【羅馬名,Ceres】)的神廟,賽姬誠心地祈求女神指引他的方向。穀物女神被她所感動,於是告訴賽姬,讓她去找美神愛芙洛黛蒂。


來到愛芙洛黛蒂王座之前的賽姬。
Psyche at the Throne of Venus, by Edward Matthew Hale, 1852-1924 (1883)


邱比特受傷回到母親愛芙洛黛蒂的神殿,結果他和賽姬的事情就被女神知道了。愛芙洛黛蒂 很生氣,竟然連兒子都愛上了賽姬。於是當賽姬來找愛芙洛黛蒂的時候,這位美神就派給賽姬許多工作,將賽姬當成僕人使喚。不過賽姬並不怨恨愛芙洛黛蒂,因為 祂希望經由幫祂作這些事情,求得愛芙洛黛蒂及邱比特的原諒。

愛芙洛黛蒂給賽姬的第一道難題是帶她到穀倉,要她在一天內將混雜的各種穀物分門別類地 挑揀出來。賽姬看到成山的穀物都傻了,單靠她一人之力根本不可能在一天之內完成這個任務。而邱比特雖然被賽姬所傷,但仍忘情不了他這個妻子。於是他就請螞 蟻們來幫賽姬的忙。有了大群螞蟻的幫助,在天黑之前,就幫賽姬把小麥、大麥、高粱等等的穀物分類好了。愛芙洛黛蒂看到賽姬完成了任務,但祂還是不高興,於 是又給賽姬出了另一道難題。

愛芙羅黛蒂的第二個難題是要她去拔金色的羊毛回來。問題是這群沒有主人的羊為數眾多, 賽姬一個人實在沒辦法在每頭羊身上都拔下一撮毛帶回給女神。正當她為此煩惱的時候,牧神悄悄地告訴她,這群羊都會到荊棘附近休息吃草,賽姬只要到荊棘那邊 拿下鈎在枝幹上的羊毛就可以了。有了牧神的指示,賽姬再一次的完成女神交給她的任務。


有了牧神Pan的幫助,賽姬才能完成使命。

Pan and Psyche, by Sir Edward Burne-Jones

關於第二個任務,也有一說是女神要求賽姬編織一條結實的繩索來提取神殿後的山泉水,因 為那是可以讓眾神養顏美容的水源之一。賽姬照著女神的命令來到了山谷邊,正愁著用什麼東西來編織繩索時,忽然從天上飛來許多小鳥,嘴上各啣著一條堅韌的麻 草,牠們很快地就幫賽姬結好了一條紮實的繩索。於是賽姬就完成了她的任務。

儘管賽姬接連完成了女神交付的任務,愛芙洛黛蒂還是不滿意。於是祂又交給賽姬第三項任務。祂要求賽姬到冥府去,用盒子把冥後---波塞芬妮(Persephone)的青春靈藥裝一點回來。賽姬接受了這個任務,當她到達冥府,向波塞芬妮轉達愛芙洛黛蒂的要求時,波塞芬妮給了她一個盒子,並叮囑她不能自己打開。


正要渡過冥河的賽姬。
Psyche and Charon, by John Roddam Spencer Stanhope (ca. 1873)

向波塞芬尼拿取青春靈藥。
Psyche obtained elixir of beauty of the box, Charles-Joseph Natoire

賽姬帶著波塞芬妮給她的盒子上路了,然而,再一次的,賽姬敵不過自己的好奇心,在半路打開了盒子,放在盒子裡的睡魔一下子就籠罩在賽姬身上,於是賽姬就倒在地上,沉沉睡去。


偷開盒子的賽姬。

Psyche Opening the Golden Box, oil on canvas by John William Waterhouse, c.1903

待在愛芙洛黛蒂神殿的邱比特正心急地等待賽姬的歸來,但已過了好幾天卻杳無音訊,於是祂親自出來尋找賽姬,終於在通往冥府的道路上發現了沉睡的賽姬。邱比特輕易地看出她是被睡魔所困住,於是祂將睡魔又收回盒子裡,賽姬這時才徐徐醒來。


受到睡魔襲擊,沉沉睡去的賽姬還是等到了邱比特的搭救。
Cupid and Psyche, by Anthony van Dyck (1599-1641) (1638)


經過這件事之後,邱比特就飛到了奧林帕斯山,祈求眾神之王—宙斯(Zeus,【羅馬名Jupiter,朱彼特(也是木星的名字)】)讓祂跟賽姬可以結為連理,不再受到神與凡人的限制。宙斯被賽姬的真情所感動,決定破例一次,將賽姬提升為神祇之一,讓邱比特與賽姬這對夫妻從此都能過著幸福快樂的日子。


幸福的婚禮。
The Marriage of Cupid and Psyche, By Pompeo Girolamo Batoni, 1706-1787, (1756)


其他有關邱比特與賽姬的畫作。舉凡頭上、背上有蝴蝶翅膀的,大概都可以視為賽姬;而背上有天使翅膀的,當然就是邱比特囉!


Adolphe William Bougereau (1825-1905)


Adolphe William Bougereau (1825-1905)(1889)


Cupid and Psyche, by Jean Baptiste Regnault, 1754-1829 (1828)


Psyche Receiving The First Kiss From Cupid, by Francois-Pascal-Simon Gerard, 1770-1837 (1798)


The Ravishment of Psyche, Adolphe William Bougereau (1825-1905) (1895)

註1:O foolish Psyche, is it thus you repay my love? After having disobeyed my mother's commands and made you my wife, will you think me a monster and cut off my head? But go; return to your sisters, whose advice you seem to think preferable to mine. I inflict no other punishment on you than to leave you forever. Love cannot dwell with suspicion.
(我很喜歡最後一句話:Love cannot dwell with suspicion.意即「當賽姬(心靈)存有懷疑時,愛情(邱比特)根本無法安棲。」)