發表文章

目前顯示的是有「系統研發手札」標籤的文章

救火奇兵之 Android USB Host API 反應遲緩

圖片
話說,Android 在某個版本後,開始提供了 USB Host API,這代表開發者可以不必再用 NDK 和硬梆梆的 C 語言去開發 USB 裝置的驅動程式,而可以完全用 Java 來開發。但是,現實往往沒有這麼美好。 日前,就協助了一個案子,解決了一個 USB 裝置驅動程式的問題,起因就是客戶用了 Android USB Host API 去控制 USB 裝置,但發現 USB 裝置的回應一直不如預期,有時像是掉資料,有時像是沒反應。而同樣的控制邏輯,用純 C 開發的驅動程式配上 libusb 就完全正常,所以我們相信肯定不是控制邏輯上的問題。 剛開始,大家都懷疑是 Java 本身的問題,懷疑是不是 JVM 執行驅動程式太慢,而造成接收 USB 裝置的資料時來不及。但我一直保持著懷疑,因為 USB 裝置回傳的資料並不多,如果 JVM 本身的效能連處理這幾 KB 的資料量都如此差,就實在是太可笑了,我無論如何不相信。 還好最後還是解決了,雖然過程曲折。 USB Request Block 的 16KB 限制 事實上,每次最多傳送 16KB 資料,是一個 bulk transfer 的 URB 限制,使用 Android USB Host API 就會直接遭遇到這個問題,所以不管用什麼方法,怎麼收資料,只要資料太大,你最多一次就只能收到 16KB。 多次收資料所發現的延遲問題 當然,既然一次最多只能收 16KB,我們可以分多次向 USB 裝置要求收資料,但就會發現會莫名掉資料。從 USB 的分析器上來看,該有的命令都有,但就是有掉,後續的資料不管怎麼取都是 0。 後續資料為 0,在這個案子的 USB 裝置設計上是可以理解的狀況,因為該 USB 裝置只會保留資料一小段時間,然後就會清空,所以若之後跟它要任何資訊,他都會回傳空的東西回來。這很明顯,就是我們要資料的過程時間,已經超過了該 USB 裝置正常的情況。 而從收到的資料來看,有收到的資料,經驗證過後發現是斷斷續續的,中間有漏資料。經過測試,發現是每個命令之間的間距時間太長,因為該 USB 裝置會不斷復寫一段緩衝區,如果我們太慢去要資料,那段緩衝區就會被新的資料蓋掉,理所當然的,我們就會漏掉一些資料。 經過各種測試紀錄,很明顯的,Android USB Host API 並沒...

產品開發玩技術很過癮!實作 QML 動畫背景!

圖片
由於最近在開發自己的產品,又開始重操舊業,開發起 Linux 系統的相關應用和嵌入式技術。為了這個產品,精心開發了一個使用者介面,除了動手把驅動程式搞定、圖形化介面搞定,也調教效能、改善系統架構。 開發自己的產品很過癮,愛怎麼搞就怎麼搞!於是,看到死板的背景覺得很不舒服,就在思考是否可以跑個動畫背景呢? 因為使用的是 QML 技術來開發 UI,最直接的想法,就是用 QtMultimedia 的 MediaPlayer 無限循環播放一個影片,當作動畫背景: MediaPlayer { id: bg; source: 'bg.mov'; loops: MediaPlayer.Infinite; autoPlay: true; } VideoOutput { anchors.fill: parent; source: bg; } 當然,我們選擇的背景影片,是一個開頭跟結尾一樣的影片,如果正確循環播放,會無縫接軌的變成一個順暢的動畫背景。 然而,結果不如預期,碰到了一個問題,那就是每當背景影片播放到最後時,會畫面變成全黑,然後才再一次重新開始播放,沒辦法「無縫接軌」。仔細暸解以後,發現 MediaPlayer 元件是 QMediaPlayer 的 QML Type 實作,所有秘密都藏在 QMediaPlayer 之中。因為 QMediaPlayer 預設所有的通知事件,都是固定以 1000ms(1秒)的頻率來觸發,這代表,當 QML 元件發現影片播完時,通常已經是播完以後的事了,所以畫面一定會因為影片結束而變黑,然後 QML 元件才發現影片結束,重新進行播放。 知道緣由後,我們可以從事件更新頻率下手,讓 QML 元件發現影片播完的時間更接近實際影片結束的時間,但這必須動用到 C/C++ 的實作,因為 QMediaPlayer 的事件更新頻率無法以純 QML 的方法修改。 C/C++ 完整應用程式的實作如下,我們把更新頻率調高為每 100ms 一次: #include <QGuiApplication> #include <QQmlApplicationEngine> #include <QQuickWindow> #include ...

OwaNEXT 計畫來吧!不懂技術也可以設計可用的 Android Launcher!

圖片
自 HanGee 國民機運動發起之後,我們開始從一個全新的思維和高度,來看待這個手機科技產業到底需要些什麼東西,甚至是創新發明這件事的困難是什麼。尤其是排除掉鬼遮眼的短線商業問題後,專注考慮怎麼讓產業提升、科技產業改變,更可以發現有很多東西是我們可以去嘗試的 其中,使用者介面(User Interface、UI/UX)是一個相當重要的課題,當人類和使用者越來越依賴顯示和觸控技術與機器溝通,虛擬形式的使用者介面就更是不可缺少。因此近年來,這也是許多科技人之間的熱門話題。只是,在我們深入了解後發現,仍然存在許多可以改進的空間。 講可以改進,並非是我們真有一個絕佳的 UI/UX 想法,或是覺得現今已經存在的使用者介面和模式有什麼不好,而是我們認為,設計方法和工具有很大的進步空間。我們都相信,在手機產業蓬勃的今天,設計師們早就已經花了大量時間去研究使用者的行為,並為其設計出不少使用者界面,甚至直到此時此刻,世界上隨時都有新的 UI 被設計出來。不過最大的問題是,設計師腦中的想法很容易被創造,但可以用的原型(Prototype)卻難以被製造出來,需要很大的成本和很高的門檻。 的確,打造實際可以用的原型(Prototype)無論在哪裡,一直都是人類進步的過程中最大的問題之一,更可怕的是,往往人類的思考和創意,都因為牽就工具和打造原型的方法,而受到挶限。還記得在你設計出一個自認傑作的使用者界面當下,工程師告訴你做不到時,你有多麼難過嗎?還記得你告訴自己開始要有 Sense 的去設計 UI 後,你總是設計出平淡無奇的作品嗎?這些都是我們被制約的實際例子,當我們無法很容易取得或使用多彩多姿的顏料,我們就只能畫沒有色彩的素描,你我的創意和創作就沒有了明天。 那麼,我們可以做些什麼呢? 事實上,就在 HanGee 國民機運動開始不久後,筆者就建立了一個『 UI 先進技術研究組』,並創造了一個名為 OwaNEXT 的專案,試圖找尋協助設計師更快打造出 UI 的方法,然後進一步設計出可用的工具。如果可以讓不懂程式技術的人和設計師,都能輕易打造出可用的 UI 原型,而且能夠真正直接被使用在實際環境上,勢必能讓 UI/UX 領域的工作和視野往前跨更大一步。 OwaNEXT 的名字由來,一方面是以 Owa (芋頭的台語念法)來呼應蕃薯(HanGee),另一方面是向已逝的 S...

【玩轉 Cubieboard】安裝 u-boot 到 SD Card

當你得到 Cubieboard 後,首先要做的就是學習如何啟動你的作業系統。Cubieboard 可以支援 SD Card 啟動,這通常也是最容易入門的 Embedded System 開機方法。你只要準備一張 Micro SD Card,切割出分割區,然後把 Bootloader 和作業系統放進去即可。 安裝 u-boot Cubieboard 使用的是 u-boot 做為 bootloader,你必須先下載 Source Code 然後 Compile: git clone https://github.com/linux-sunxi/u-boot-sunxi.git make Cubieboard CROSS_COMPILE=arm-linux-gnueabihf- 如果你的系統 toolchain 都已經安裝完成,且都沒有問題,u-boot 會順利編譯通過,然後你會在 u-boot 的目錄裡得到兩個檔案: spl/sunxi-spl.bin u-boot.bin 有了這兩個檔案,安裝 u-boot 就不是什麼問題,只需要使用 dd 指令來把 bootloader 寫入 SD Card: dd if=spl/sunxi-spl.bin of=/dev/sdb bs=1024 seek=8 dd if=u-boot.bin of=/dev/sdb bs=1024 seek=32 註:/dev/sdb 是 SD Card 的裝置路徑,請依照你系統上的實際狀況,填入正確路徑。 後記 有趣的是,很久沒有玩開發板了,自從兩年多前開始陸續投入各廠商的專案後,所碰到的 ARM Board,清一色都是客製化的產品樣板。而幫大廠商做產品,和自己玩開發板相當不一樣,同樣是驅動程式有問題,要做的是打電話罵人;如果會當機,快打電話罵人;如果缺任何東西,打電話罵人就對了!所以,在客戶的專案中,做最多的總是證明硬體廠商的問題,讓硬體廠商們心甘情願幫你把問題排除。雖然還是要熟悉 Debug 的技巧,但多半不用自己煩惱『該怎麼辦』,只需要下指導棋。因此,自己也慢慢養成很多惰性,除非是真的沒人可以幫你,才肯親自出手跳下去從底層到應用程式,深入修正問題。 但是,玩開發板可不是這麼一回事。有問題?你自己搞定吧。搞不定?問 Google 大神吧。還是不行?慢慢看...

NPK 釋出!打包你的 Node.js 程式!

『 NPK 』是什麼?一個打包 Node.js 程式的工具,之所以會有這個工具,完全是拜下周(5/18 - 5/19)將在中研院舉行的 JSDC 所賜。因為這次筆者也有個 Talk,將探討如何去編譯我們寫出來的 Node.js 程式,所以過程中便寫出了這樣一個工具。 https://github.com/cfsghost/npk 事實上,目前的 NPK 不是個新東西。JavaScript 經過多年的發展,早已經有非常多合併 JavaScript 程式碼的工具可以使用,對於前端工程師來說,是再尋常不過。不過,針對 Node.js 的程式碼,這樣的工具卻相對來說非常少。 如果你想嘗試使用看看,可以立即用 NPM 來安裝 NPK: npm install npk -g 以 NPK 裡的範例專案為例,你可以直接下命令打包該專案: npk tests/purejs/ 接著你可以在輸出的資料夾中找到打包好的檔案: tests/purejs/out/app.js/ 甚至是直接執行他: node tests/purejs/out/app.js/app.js NPK 工具定義了一個新的屬性『npk_target』在 package.json,用來讓程式人員自行決定要合併和連結哪些 JavaScript 程式(像過去為 C/C++ 等語言寫 Makefile),可以參考 NPK 內附範例專案的 package.json。 最終目標『編譯』成二進位格式(Binary) 在與一些企業以及同業分享 Node.js 技術和其未來發展時,時不時會有人提出一個問題:『我們能不能編譯自己寫的 Node.js 程式?』這個問題,往往會開始引發一長串的討論,有一些人為終止這個問題,甚至會開始引用 JavaScript 語言的設計宗旨:『明碼保存,不需要預先編譯』,來強調我們不應該把 JavaScript 的優勢給閹掉。當然,這樣一廂情願的說法,是不可能消除許多人的疑慮,尤其是和 Open Source 路線不熟的企業,或是被『壞客戶欺負』的工程師。 有些眼睛比較亮的人,或許已經發現到,這其實是個複合性的命題,會提出這個問題的人,多半想要知道或得到的是: 能否透過預先編譯 JavaScript 程式,讓執行效能提升。 想要打包自己寫的程式,讓他合併成一個,以方便...

jsdx-power 讓你使用 JavaScript 實現電源管理

既然會成立新創公司,總有些自己的想法,也期望做一些和其他人不一樣的事,對我們而言,改變世界事小,留下長久價值更重要。也許你有點印象,筆者今年度到處給予的 Talk,多半都是與 Node.js 相關,也透漏許多正在執行的計劃。因此,有些人可能聽過筆者提及『JSDX Framework』,這是『 Mandice 』的長期計劃,也是將 JavaScript 落實在系統、桌面和嵌入式裝置的解決方案。 由於 JSDX 是一個開放原始碼專案,市場上也已經有一些產品和公司專案導入使用,但因為合約的關係,我不能透漏太多細節,所以本文將只討論些許技術細節。說到這,也許你還是沒什麼概念,但若是真要舉出最直接與其相關的專案,就是今年 8 月,於 COSCUP 一場議程中所公開的『JUICE Desktop Environment』(簡報檔詳見舊文: 【COSCUP 2012】閉幕!簡報釋出! ),完全以 JavaScript 實作的 3D 桌面環境。 JSDX 包括許多元件,從 GUI Toolkit(使用者界面)、Multimedia Player(多媒體播放)、Network Manager(網路管理)到 Sound Manager(聲音管理)等,提供了各類系統控制或硬體相關的 APIs。程式開發者只需要會 JavaScript 語言,就可以使用這些 JSDX 所提供的 APIs ,開發各類應用程式或系統程式。 回應本文標題,JSDX 如果可以拿來開發桌面環境,當然也有電源管理相關的 APIs 可使用。所以在這邊我們要討論的元件是『 jsdx-power 』,你可以在 github 上取的他的 Source Code,亦或是直接透過 npm 安裝。 npm install jsdx-power 這邊簡單示範如何使用 JavaScript 透過『jsdx-power』命令作業系統休眠: var Power = require('jsdx-power'); var power = new Power; power.init(function() { power.suspend(); }); 後記 JUICE DE 的 juice-panel 電量顯示,就是使用了 jsdx-power,有興趣的人可以參考其 Source Code。

【COSCUP 2012】閉幕!簡報釋出!

國內一年一度最大的開放原始碼(Open Source)活動,再次順利落幕。很多人都意猶未盡,所以每當活動結束,便會開始思考明年要分享什麼、反省今年哪裡做的不夠,有人會在活動過後的連續幾天,電腦上開著簡報編輯器,盯著空白的內容,靜靜的發著呆。可惜的是,最後總是因為沒有任何想法而放棄。一直到了明年 COSCUP 的徵稿消息發出,一堆人又開始拾起空白了近一年的想法,擠出一個題目,逼著自己在一兩個月內完成,上台發表。就如同學校的期中、期末考,沒有死到臨頭,大多數同學都沒有唸書的動力,也不會激發自己 120% 的腦力和體力。所以,Event-Driven 的開發模式,一直是在很多開發者促使自己進步的方式。 別以為兩個月時間很充足,大家都有外務、有工作、要過生活,所剩的時間實在有限,所以每當到了 COSCUP 的前夕,總是會聽到很多講者在拼命趕工的聲音。其實,這樣都還算是常態,頂多拼命一點,少睡一點,都還可以頂得過去。可是,今年一次給『兩個 Talk』,就真的讓人會虛脫到不行,還好,熱情能戰勝一切。:-) 由於今年活動承辦的分工方式比較特殊,所以被選上了兩個稿,分別是: JavaScript 全面逆襲!使用 Node.js 打造桌面環境! 用最潮的 JavaScript 盡情開發 KDE/Qt 程式 前者是總結這一年來,我們團隊所開發出來的各種 Node.js APIs,並整合成為一個如同 GNOME/KDE 的桌面環境(Desktop Environment),並展示使用 JavaScript 開發出來的 Juice Desktop Environment(果汁桌面環境)。後者則是為符合 KDE 的議程方向,探討使用 Node.js 開發 Qt 程式的可能。 後記 退學不是件好的事情,請勿模仿。:-S

實作 X11 底下的 Popup Menu

既然要投入 JavaScript 的發展,一個很重要的目標就是讓 JavaScript 能被用來開發 Native 桌面程式。而為了達成這樣的願景,我們的團隊沒日沒夜的發展各類基礎技術和擴充底階 API,甚至是將以 JavaScript 開發整個桌面環境(Desktop Environment, 如:GNOME、KDE、LXDE 或 XFCE 這類專案)為終極目標。 當然,跳出瀏覽器之後的 JavaScript,缺少了繪圖引擎,所以要拿來做圖型化使用者介面,會更為困難。慶幸的是,前些日子發展出的 jsdx-toolkit 已經解決了大多數的問題,除了有 3D、動畫等支援,也已經有了許多現代桌面有的UI元件(如:Entry、Label、Button... 等),以致我們完全可以放心的開發屬於自己的圖型化應用程式。 不過,目前的情況,在手機、平板或是特定用途的嵌入式系統中完全夠用,一旦回到更為複雜的桌面環境下,就會遭遇到許多的問題。像是回到 XWindow 底下後,會遇到許多 X11 的視窗管理機制,其視窗之間錯縱複雜的交互關係,就是我們要處理的。尤其是當我們在開發桌面環境時,就會發現在一般的桌面環境下,使用者可能會同時開無數個視窗和程式,並且隨機又大量的切換使用,這迫使我們必須去修改 jsdx-toolkit 以處理 X11 底下的更多狀況,符合並更完整支援一般桌面環境下的操作習慣。 你可能用過 GTK+/Qt 這類常見的 Toolkit,也知道在 X11 之下有很多種類型的視窗(Window),像是 Dialog、Splash、Menu、Popup Menu 等等,但你可能不知道這些現代 Toolkit 內部做了多少事。事實上,X11 本身雖定義了各類視窗的類型,但實際上 XWindow 和 Window Manager 並不會完全去照定義去處理你的視窗行為,更準確的說法是,X11 HWMH 中的基本定義和我們實際的認知是有出入的,該定義只是說希望達成的行為,但沒說是由誰(XWindow、Window Manage 還是 GUI Toolkit)去處理。 舉例來說,我們現在寫一支程式,該程式不使用任何現代的 GUI Toolkit,然後單純使用 X11 API 把一個 Window 的類型設成 Popup Menu。但是你會發現,它的實際行為並不像我...

【OSDC.TW 2012】Let's Enjoy Node.js - All Development in JavaScript 簡報檔釋出

感謝各位捧場,上周六在『 OSDC.TW 2012 』所分享的『Let's Enjoy Node.js - All Development in JavaScript』,旨在介紹『 Node.js 』這新興的 Open Source 專案,我們可以預見,其將帶來軟體業相當重大的變革,並為 JavaScript 程式語言開拓了嶄新的局面。目前簡報檔釋出,歡迎各界取用和參閱。 自 2009 年 Node.js 公開發表以來,備受世界各地開發者囑目,在 2011 年 12 月更一度榮登 github 關注排行的第一位,超越了 Ruby on rails。Node.js 的出現意味著 JavaScript 語言從前端應用走進了 Server 的殿堂,除了效能表現不俗之外,使用其開發應用也相當迅速,未來 Web 開發者可以完全使用 JavaScript 語言打造網站服務,甚至是可大規模括展的雲端應用,堪稱 Web 開發者的夢想。 值得一提的是,Node.js 其實不僅限於 Web 應用,甚至可以用於各類系統程式開發,比較廣為人知的例子是 HP WebOS(前身是 Palm WebOS)。其採用了 Node.js ,做為系統服務的開發途徑之一。此外,Node.js 有模組化的設計,讓開發人員可以輕易使用各類其他語言(C/C++/Python... etc)為其擴充功能,彈性相當高,更讓 Node.js 不受限於任何開發領域。

使用 Node.js 控制網路連線管理員

既然以『國內首屈一指的 JavaScript 專家』為目標,使用 JavaScript 打造作業系統就是我們的終極目標。未來會將先前的 Flat Project(可參考舊文 Flat Project Demo - An OS for Tablets 平板作業系統 和 Flat Project - 從山寨做起,親手打造炫麗的平板系統 )以 JavaScript 重新架構,實作並提供一個更易於開發應用的環境。當然,就像過去所說,無論有多困難,產品化一直是我們的目標。 既然要開發作業系統,就要讓 JavaScript 能直接觸碰到系統層級或硬體裝置的控制,我們必須實作各種 JavaScript API 去達成這個目的,而前些日子所提及的『 node-dbus 』和『 jsdx-toolkit 』就是在做這類的工作。現在,基於 node-dbus,我們進一步和『 connman(Connection Manager) 』連接,實作出『 jsdx-connman 』,提供 JavaScript 網路管理機制的 API。這代表我們可以藉由 jsdx-connman 去控制無線、有線網路,甚至是WiMax、藍芽等各種連線介面。 如果想要嘗試 jsdx-connman,可以直接使用 NPM 安裝: npm install jsdx-connman 這邊有個範例,讓我們可以使用 jsdx-connman API 去得知無線裝置的狀態和掃描當前環境的 Wifi 無線基地台: var ConnMan = require('jsdx-connman'); var connman = new ConnMan(); connman.init(function() { if (connman.Wifi.Powered) console.log('Wifi is powered'); else console.log('Wifi is not powered'); if (connman.Wifi.Connected) console.log('Wifi is connected'); else console.log('Wifi is not connected'); ...

我的 Kinect 應用程式開發記錄

圖片
話說微軟(Microsoft) 這兩年為了 XBox 360 所推出的 Kinect 造成一股風潮,一夕之間,把 Wii 打的七零八落,取代了其體感遊戲機王者的地位,相比之下,Sony PS3 同一時期所推出的體感裝置,就顯得乏人問津 。 最近,筆者雖然忙錄,被很多麻煩的公事私事綁住,但每當睡前,仍抽空研究一些新技術。一方面是為了促使腦筋轉得更快,一方面是想加強許多 Idea 和 Business Model 的強度和完整性。於是,撿起丟在一旁生灰塵的 Kinect,嘗試著在上面開發一些應用。 由於『 OpenKinect 』眾高手的努力,所以驅動程式不是太大的問題,很久以前就已經能分別在 Linux、Windows 和 Mac 上驅動 Kinect。不過雖說如此,最困難的還是在於辨識演算法的相關設計,對於開發人員來說,你不過就是能得到攝影機的影像,就如同從 Webcam 上得到一樣,其他的圖學運算以及應用還是要自己動手。 當然,如果 Kinect 只是和一般 Webcam 一樣,那新聞媒體和許多人對他的誇讚,就太言過其實。其最大的不同在於,Kinect 有深度感測,這意味著我們可以得到實際的空間資訊,以工程的方式,大大減少辨識演算法的難度,並提升了辨識系統的精準度。 這次的嘗試,焦點放在辨識演算法之上,便開始思索各種方案。過程中,試了數種別人已經開發好的函式庫,都不甚滿意。倒不是效果不好,而是系統需求太大,開發語言大多採用  Java ,所以需要安裝一卡車的模組程式以及 JVM。有些更是對 Linux 的開發者不夠友善,或開發支援不夠完整。無論哪一種現有方案,都不足以讓筆者可以安心建構在上面然後開發下去,因為產品化和易移植性,對筆者來說是相當重要的考量,不是只要學術驗證而已。所以最後決定,自己手動設計演算法,採用 C/C++。 經過這幾天睡前的努力,可以看到開發過程中的截圖,演算法初步已經能辨識手掌,並抓取指尖位置,精準度已達可用的程度。接著,只要多做些簡單的開發,撰寫 X11 輸入裝置驅動程式,就能實際以多點操控的型式,操作螢幕上的所有應用程式。 此外,這裡有一些心得,Kinect 深度感測的數值範圍是 0 ~ 2047,至於得到的深度數值如何換算成實體距離,這邊有個公式可以換算成公尺(Meter)的型式: Real Dist...

Android 問題百出之 2.3.x 的 JavaScript Interface

有鑑於 Android 問題太多,只好定了個系列標題『Android 問題百出』當開頭,並將碰到的問題和解決或避開的方法記錄在內。話說回來,筆者個人其實相當討厭 Android,自 Android 出現以來,從未真的投入其中並賺過什麼錢,會接觸,多半是興趣玩弄或是幫一些朋友的公司臨時打工救火。不過,既然是救火,任何千奇百怪的問題或狀況都會遭遇到,甚至還得『限時』解決別人解決不了的問題。 這次碰到的問題就是 Android 2.3.x Gingerbread 缺少 JavaScript Interface 的實作,如果你的應用程式有自己實作 API 供 JavaScript 程式使用,那這些 API 將會完全失效。而這樣的問題,對於使用 Web 技術(HTML5 + Javascript)的應用程式來說,相當嚴重。 話說 Android 在 2.3 版本之後,採用了 V8 做為 JavaScript Engine。在快速掃過 Android 關於 WebView 和 WebKit 的程式碼後,發現不幸的是『Google 再次未將程式寫完』。但這一次,不只是功能未完成,而是將原本可以用的功能(在 Android 2.2),變成不能用。這讓筆者非常不能理解,為何 Google 換了 V8 Engine 後,明知功能沒有改完,卻仍將這部份實作隨新版 Android 釋出?然後,絕大多數廠商,在毫無感覺之下,直接繼承了這樣的 Bug。 對硬體裝置的廠商來說,重新實作這個 JavaScript Interface 支援是最佳的解決辦法,髒一點的方法是將 V8 改回舊的 JSCore。但很可惜的是,就算有人改好了,也沒有廠商願意釋出這部份的程式碼。(所以才會不停有人願意花大錢找筆者這種臨時救火工呀。) 此外,如果你是一般的應用程式開發者,因為動不了底層,則完全無解。但是,這邊有個 Workaround,可以暫時代替 JavaScript Interface,讓應用程式開發者可以避開這問題(以下假設自定的 JavaScript Interface Class 為 myAPI)。 定義將會用到的變數: /* Define private variable */ private static WebView mWebView; private static myA...

千萬別相信 Android 上的應用程式

或許有些過於危言聳聽,但我們確實得正視這樣的議題:『資料無故從手機中消失』。這是可能發生的問題,原因在於 Android Dalvik VM 的 Garbage Collection 機制太過強大,如果程式開發者在寫程式的過程中都沒注意到這問題,那這肯定是個不定時炸彈。 話說,寫過 Android Application 的人應該都很習慣了變數宣告後,用完了就不管他,彷彿系統會有無限量供應的記憶體一般。不過,這對『慣C』一族來說,永遠是種荒繆的事,實在無法想像『憑什麼』不用釋放記憶體。其實說穿了,這一切是 VM 裡面的 Garbage Collection 在『Hold 住』場面,他會自動將目前 Reference 數量稀少的記憶體回收,以避免系統記憶體被用光後又不釋放。而所有回收的動作,有一系列『聰明』的演算法,有興趣的人可以自己去找相關資訊閱讀。 但是別以為 Garbage Collection 可以聰明到百分之百準確知道,什麼東西回收後是對系統或是應用程式無傷的,因為這就算要人腦來判斷,也一定會犯錯。更者,也別以為 Garbage Collection 不太會動當前正在跑的程式,因為只要符合條件,就算是你程式當前不斷在用的變數,也可能消失被回收,導致程式錯亂,就像被鬼憑空抓走一般。 如果你認為這只是理論,那你就錯了,這問題實際發生在最近筆者幫忙救火的案子裡。詳細情況是,這是瀏覽器相關的案子,當時我們宣告了幾個 Private 變數,然後使該變數在使用者操作觸發後,會被設定了一些數值供接下來的許多運算使用。一般情況下,我們實作的程式都很正常,不過,一旦開到比較複雜或充滿 JavaScript 的網站後,我們的程式彷彿就開始像中邪,完全不照我們的想像去執行,無論怎麼找問題,也找不出個所以然。 由於問題的發生是過程中變數無故被歸零,而我們又確定沒有任何一段自己的程式會讓他歸零,所以只剩下一種可能:『Garbage Collection 為了因應 WebView 過多的需求,而把變數給回收了。』 最後,我們將這變數設為 static 以解決這問題。不過,雖然用 static 暫時解決了問題,那也只是減少變數不被回收的機會,並不是最妥當的做法。 後記 別小看這問題,其實在 Android Framework 中也處處藏滿類似的問題,只是不知道...

發瘋的 Qt QWS TTY Keyboard Driver

最近接了一個小案子,主要是協助客戶將 Qt 應用程式從舊板子移植到新的硬體上,由於硬體是 Third-party 做,當然所有的驅動程式也是由別人處理,我們並不經手,只和硬體廠商密切合作。 這次我們碰到了一些 Keyboard 問題,一開始是 Linux driver 缺少 input_sync() 的操作,導致 Qt 無法正確辯別按鍵的『壓放』。接著碰到個更棘手的問題,就是 Qt 送到應用程式的 Key 事件(Event)有很大機率會送錯。這將導致按鍵所觸發的結果,有很大的機會我們無法預期。檢查過 evdev event 和其發出來的 keycode,我們確定了 Linux Driver 是正確的,判斷應該是 Qt 的問題。 經過一番檢查後,發現是因為 Qt-Embedded 預設使用 TTY Driver(qkbdtty) 去驅動鍵盤,大部份情況下不太會有問題,但因為我們走的是標準的 Linux evdev (/dev/input/eventX),如果很快速或多重觸發鍵盤按鍵,就有可能讓 Qt 的 TTY Driver 取樣和解析出錯誤的 Event 值。最快的解決辦法就是捨棄 Qt TTY Driver,然後改用 Qt  LinuxInput Driver(qkbdlinuxinput)。 不過 qkbdlinuxinput 並不處理 VT,這是與 qkbdtty 最大的差異,此舉會造成板子上的 Console 不正常切換,Debug 用的 Console 會因此壞掉。筆者在此對 QWS Keyboard Driver 做了些修改,讓 tty driver 使用 linuxinput driver 的方式去解析 keyboard event,並保留 VT 的處理機制。 Patch 檔案連結如下: http://fred-opensource.googlecode.com/git/patches/qkbdtty_qws.patch

針對『電子紙』優化 Qt 繪圖事件

最近手上有一些電子紙的案件,指定使用 Qt 做為圖型平台。與一般裝置不一樣的地方,電子紙的更新頻率很低,平均下來每秒頂多刷新一次,相對於 LCD Panel 的每秒 60 次起跳,實在低的可憐。對於專職繪圖的 Qt 而言,要如何壓低刷新的頻率和確認畫面繪圖完成,最後再推入 Display,便是一個重要的課題。 壓低更新頻率比較單純,透過修改 Framebuffer Driver 就可以達成。此外,也必須利用 setUpdatesEnabled() 和 update() 盡量避免 Qt 連續多次的繪圖需求,合併一次送入 framebuffer,這樣可以減少 Qt 來不及畫完和畫面閃爍的問題。 到此為止只是改善顯示效果和基礎的建置,真正要完成完美的一次推送,通常需要使用電子紙驅動程式所提供的額外 API,它同意我們自己決定何時將 Framebuffer 的資料推送到紙上。 至於何時該將畫面推送至電子紙上?這時需要針對 QApplication 做一些修改,監聽和自己創造一個新的事件: const QEvent::Type UpdateScreenEvent = (QEvent::Type)1234; bool Application::notify(QObject *obj, QEvent *e) {      if (e->type() == UpdateScreenEvent) {           if (updateScreen) {               EPaperDriverCtrl *ctrl = (EPaperDriverCtrl*)EPaperDriverCtrl;               ctrl->pushFBtoPanel();               updateScreen = false;  ...

MongoDB 快速筆記

這幾年 Web Service 的龐大需求,對資料庫的要求是快速且吞吐量大,因此業界開始流行 NoSQL,它省略最花時間的資料庫操作和複雜的結構,用最合人類使用需求的方式在儲存資料,其帶來的好處,當然就是能提供極速的反應和龐大的資料吞吐量。目前最廣為人知的就是 BigTable,Google 提供全世界快速搜尋和各種線上服務,靠的就是這 NoSQL Database。 其實 NoSQL Database 的選擇非常多,但本文只是記錄 MongoDB 的操作筆記,因此就不詳述。 有興趣的人,可以去查閱網路上更多的資料。 選擇 Database: use my_database 利用 root 新增 Database 的管理帳號: # 切換到 my_database use my_database # 先用 root 帳號認證 db.getSisterDB("admin").auth("root", "rootpassword"); # 新增 frankie 帳號 db.addUser("frankie", "fredpassword"); 新增 Database 的管理帳號: # 切換到 my_database use my_database # 先用 frankie 帳號認證 db.auth("frankie", "fredpassword"); # 新增 zombie 帳號 db.addUser("zombie", "zombiepassword"); 插入新增資料(Insert): # 插入一筆新資料到 users Collection (相當於傳統 SQL 裡的 Table 角色) db.users.insert({ username: "fred", password: "12345678" }) # 含當前時間 db.users.insert({ username: "fred", password: "12345678", created: new Timestamp() }) 查詢...

不想用傻逼 GNOME3 !好牛逼的雜牌軍替代方案 E17+GNOME/XFCE/Fluxbox Component!

圖片
好不容易,Linux 桌面經過十多年的演進,GTK+ 和 GNOME 總算進入了 3.0 的時代,向來最愛仗著『使用者之名』做盡任何事的 Ubuntu,也推出了他們的 Unity 介面,試圖重新打造桌面使用者的習慣。可惜的是,這些新的桌面設計雖然帶來了完全不一樣體驗,卻也造成不少使用者操作思維的混亂;更可怕的,這些標新立異的改變,將原本『好不容易』成熟穩定下來的桌面系統,在短時間內,又再次推向重新建立習慣和軟體崩潰的循環地獄。 網路上一篇討論文章『 Linux的桌面為什麼這麼傻逼 』(這篇文章是有心人翻譯的,內有原文連結),對 Linux 桌面環境有很獨道的見解和體驗,其批判性的強烈言詞,可以感覺到這些年作者的沉痛經歷。 就某方面來說,筆者相當讚同該文的論調,本身就長期使用 Linux 桌面,不時因為各種桌面系統的問題,親自動手去做程式開發或調整,可以說該文道盡筆者心聲。不過最近這一兩年, GNOME 已經可以算是很好用的桌面環境,程式也很穩定,周遭初入 Linux 的朋友們也都可以輕易上手。但高興沒辦法太早, GNOME 3.0 在此時投下了一顆超級炸彈,其更新除了讓許多元件壞東壞西,使用操作和程式開發上完全讓人覺得陌生。 喔不!我不要再經歷一次『桌面環境的黑暗時代』。我只想穩穩定定且不要有意外的使用著我的作業系統,所以我也拒絕 GNOME3 和 Unity。在一切混亂的情況下,Enlightenment(簡稱 E17) 帶來了一線曙光。 我對桌面環境的要求其實不高: 可用性高,穩定度和使用性最好不要與 GNOME 2.0 有太大的差異。 速度快 漂亮又炫麗(最好能夠有 3D 桌面的支援,這讓我覺得我的系統比 Windows 高級) 省系統資源 畫面易客製化(如果能讓我看起來更像個專業 宅男  Hacker更好) 經過一些拼裝和調整後,這是用 Enlightenment + GNOME Component + Thunar File Manager(XFCE) 組裝的桌面環境其最後樣貌: Debian 使用者,可以照下面步驟拼裝出同樣的桌面環境(當然畫面上的元件排版要依各自喜好自行調整): 去  http://packages.enlightenment.org/  尋找和系統相對應...

ibus-chewing 單純注音模式 - OSDC.tw 2011 Lightening Talk

為了注音輸入法,您是否長久以來也仍在使用 SCIM?每次裝好 Linux 的第一件事就是刪除系統預設輸入法,我們彷彿是外星人一般,一點都沒享有人權。感謝 OSDC.tw 2011(活動期間 3/26 ~ 3/27) 提供好吃的食物和舒適的場地,在活動這兩天筆者花了一點功夫做 patch,修掉了 ibus-chewing 單純注音模式的一些 Bugs,現在基本上 ibus-chewing 單純注音模式已經可用,我們可以丟掉 SCIM + 爛注音 Table 了! 以下是 Lightening Talk 的簡報檔: 後記 由於簡報格式的關係,暫時無法轉成 PDF 輸入,所以目前使用錄影的方式公開,待轉檔程式開發完成再來釋出 PDF 檔。

Linux Kernel Sendfile() 的提升 Server 效能之路

Apache 和 Samba 這類伺服器,主要以傳送檔案資料的工作為主,他們最常做的工作不外乎是開啟檔案(Open)、讀取(Read)、寫入網路連線(Write to Socket)。但是以 Kernel 的角度來說,這樣一個讀取檔案資料和傳送出去的流程相當繁複,並擁有最少兩次的 Kernel/User Space 資料搬移, 導致同一筆資料需要經過兩次多餘的複製。舉例來說,若一個檔案有 1MB,則 Linux Kernel 需要多做 2MB 的記憶體複製,使得效能經常消耗在這種地方,尤以 CPU 不夠快的平台上狀況特別明顯。 而過去曾有 khttpd 這樣的實作,讓 Linux Kernel 自成一個小型的 Web Server,提供一個極有效率方式的讀取靜態網站頁面和 Server 服務,其加速的方法,便是於 Kernel Space 讀取檔案並直接從網路連線送出資料,目的也在於減少 Kernel/User space 之間不必要的 context switch。 想瞭解 User Space 和 Kernel 的資料搬移狀況,我們可以來研究應用程式在讀取和傳送網路資料的流程,經簡化後大致上是(以下簡稱 User Space 為 US,Kernel Space 為 KS): [US] open() [KS] do_sys_open() [KS] do_filp_open() - 找到檔案,並從所在的檔案系統取得 struct file [KS] Return File Object [US] malloc() - 準備一塊記憶體當 buffer [US] read(file, buffer) [KS] vfs_read(file) - 標準 VFS 的檔案讀取 API [KS] file->f_op->read() - 使用資料所在的檔案系統(filesystem),其提供的低階 read 操作 [KS] copy_to_user(buffer) - 將檔案資料從硬碟讀出來後,複製一份到 user space 的 buffer 接著是將讀到的資料透過網路傳送出去: [US] write(Socket, buffer) - 將 buffer 內資料傳送出去 [KS] copy_from_user(buffer) -...

在 Linux Kernel 中取得目錄中的檔案清單

寫一支程式在 User Space 下列出目錄中的檔案清單相當容易,我們可以用 readdir() 去一項項取得檔案內容,事實上, readdir() 是由 Linux Kernel VFS(Virtual Filesystem) 所提供的 API,真正的實作在檔案系統的核心模組(Kernel Module)中。然而因為 Kernel readdir() 的相關實作牽涉到 Kernel/User space 的資料交換問題,所以如果我們是在撰寫 Kernel 的驅動程式,當然就不能使用這系列的 APIs。不過也因為所有的資訊都存在於 Kernel 的資料結構中,我們可以直接從記憶體中的 Linked list 中快速取得檔案清單。 在取得某個目錄下所有檔案資訊前,要先得到該目錄的敘述 struct dentry,VFS 用 struct dentry 的串聯組合來描述整個檔案系統的目錄結構,檔案清單則被保存在 dentry 中的 Double Linked List,因此其實只要知道如何使用 Kernel 提供的 Double Linked list,就能得到該目錄下所有的檔案資訊。而這邊要注意的是 Kernel 提供的一個通用 Linked list 結構(struct list_head),用來統一整個核心的 Linked list 操作機制,這檔案清單的 Double Linked List 就是使用這通用的結構。 這裡是 Linux Kernel 2.6.37 中 dentry 的資料結構(定義在 linux/dcache.h,其中較不重要的部份以『...』做省略,此外,某些定義與較早版本的 Kernel 有所差異,將在文章最後補上說明): struct dentry { ...省略 struct hlist_node d_hash; /* lookup hash list */ struct dentry *d_parent; /* parent directory */ struct qstr d_name; struct list_head d_lru; /* LRU list */ /* * d_child and d_rcu can share memory *...