第一個附帶 background service 的應用是由同事開發,但 GUI 程式與背景程式之間並沒有直接溝通的方式,即是說,沒有用上 Inter-Process Calls IPC。它們會共用一個數據庫來儲存設定,故此,背景程式只好每 50 秒讀取數據庫一次。換算出來,一天就讀取了 1728 次,但由於 GUI 設計簡陋,功能被限定只在 00:00 運作,多次讀取數據庫行為變成手機 CPU、FLASH 和電池的不必要負荷。
第二個附帶 background service 的應用是去年開發,由於對程式編寫的完美有份執著,所以我研究如何讓 GUI 程式與背景程式直接溝通,看了一些文檔、一些實例,終於開發出我的第一代 IPC。第一代的 IPC 只有一個 call:Reload()。沒錯,應用程式依舊存在過去的影子:GUI 程式讀寫數據庫,背景程式唯讀數據庫。但相比起第一個程式,已經針對性減少很多不必要的負荷。Reload() 只出現在用戶使用 GUI 程式更改了設定時,而 timer 由固定每幾秒運作延長至更接近需要運作的時刻。為了更清楚明白如何安排它工作,我把它比喻成家裡一個傭人。
沒錯,寫程式就像安排一個「人」為自己進行一堆指令的工作一樣。除非,寫的是 AI Engine。
其實我不太喜歡兩個應用共享一個數據庫、同步的唯一方法是 Reload,由為這樣會令管理上帶來混亂。背景程式不曉得更改的是什麼,它只能每次 Reload 前重置所有狀態,之後按新的數據行事。情況也變得被動,如果它不喜歡 GUI 的設定呢?應該說,如果它發現 GUI 的設定有誤,可以拒絕嗎?所以之後的產品針對這個作出了改良。
第三個附帶背景程式的應用是去年年尾、今年年頭時開發。 GUI 程式與背景程式之間共享的數據,全都是 Build-in 的 T class,要寫的 IPC 很簡單,只是數量比以前多。但這個背景程式又會啟動另一個 GUI 程式(三角關係),所以 IPC 變得複雜,有一個 Listener。建立一個 Listener 關係的 IPC 是最花時間的部份。另外,背景程式負責的事項太多,導致它存在著一些致命的漏洞(我卻在半年後才發現)。
有空的話,我真的想把以上三個程式重寫一次。不過,我該沒這個閒了。
最近在寫第四個附帶背景程式的應用,今次沒有三角關係的複雜,卻有數據的管理和傳遞問題。除了設定這類 build-in T class 外,我從來沒處理過 C class 和 R class。沒有過去的經驗支持,開發的進度一下子慢了下來。明明兩天搞定了 20% 的 UI 和基本的背景服務,但卻用了三天來建立 data class 和 database,還沒完成 IPC 的 C 類傳送。
今晚 google 出:Passing C classes over IPC asynchronously - both ways。其中這段,正是我今日做了一半的 IPC:
... split the entire call into a synchronous and an asynchronous step. The synchronous is for sending all the CBase derived stuff over the process boundary. The asynchronous is for initiating the work and waiting on completion.
Now it is very easy when you keep some things in mind. An instance of a custom class has to be serialized/deserialized. Sometimes this is also called marshaling/demarshaling.
原來我今日讀的 Example 裡用的 function 名:MarshalData 是有意思的。
Your CBase derived class instance fully writes its members into a dynamic growing CBufBase derived buffer. It is easy to put a stream over that buffer. The stream has several write functions to write all the build in types into that buffer (non build in member types have to provide write functions again - can be easy cascaded). After fully serialized you can create a descriptor from that buffer.
Sending that descriptor via IPC is easy.
On the other side you have to deserialize your instance from that descriptor. Again put a stream over the descriptor and read in all the members of your CBase derived class instance. (Keep in mind you do reading in the same order like writing before). Et voila you have a perfect clone of your CBase derived instance. ...
When requesting a CBase derived instance from a server you have to do this in 2 separate synchronous IPC calls.
1. Ask the server to provide a size: "How big is the requested CBase derived instance?" (Easy again: Serialize the requested instance and let the server provide the descriptor's length)
2. Let the server write the serialized instance into the client side provided descriptor.
OK. This is very costly because of the IPC calls but it works perfectly.
我已經把讀取一個 array 的 IPC 分拆兩部份:第一步,讀取 Array 的 Count,第二步,按每個 item 讀取它所需的 buffer 大小。第三步應該是開 buffer、讀出資料,但我還沒有想出確實的寫法。明天應該可以按這個 thread 來完成吧。還要 cache 起第二步建立的 buffer,留在第三步中使用,不浪費 CPU time。
昨日在 Facebook 上寫:兩日快、兩日慢,終於又陷入咗膠狀,又是 Data Type 的運用和 Database 的 design 最花功夫。Programmer 就係要諗埋 end users 估都估唔到的情況,如果咁咁咁,會點呢?
今日要改成:IPC 的設計、建立與維護也很花功夫。Programmer 就係要諗埋 bosses 唔會理的情況,無理由為了貪一時之快而限制 GUI 的功能。End users 會點諗呢?又一隻癈 APP?
No comments:
Post a Comment