7.17.2012

創業一年回顧


離開前公司開發app已經一年了,這篇文章可能有點亂,請慎入。

也許有人以為我在離職前就已經動手在開發了,
其實--大。錯。特。錯~!!
在那之前我只會C/C++跟Java(Matlab硬要算的話也會一點),
我是離職後才開始學Objective-C跟Cocoa Touch。

2011年整個夏天都在學這些東西跟寫sample code還有想idea。
9月才開始開發,12月才上架,而且第一次上架一波三折,
被Apple退貨了很多次(App Review Guide的條文太多了,很容易踩到),
1.0上架的東西跟當初第一版送review的真的差很大。

不過記得上架後最開心的事情有三樣:
1. 第一天就有人下載app
2. 下載app的人來自於想都沒想過的國家,能全球的人透過app連結的感覺很妙
3. 第一次有人付費購買app
但是這樣子的快樂並沒有延續多久,因為過了新年以後的下載量就迅速減少了。

因為1.0版只是最小可行性產品,當時覺得有人下載就表示有需求,
也許是個好的開始,卻沒想到也是誤判形勢的開始...

接下來的幾個月時間就是一直在改版和改進穩定性。
在1.0出廠之前就訂下一個月要出一次.1版的進度,因為Apple審app要一週,
所以又抓緊時間修送審後發現的bug或defect,並且在.1版之後馬上送..1版,
一方面是要督促自己要有紀律,另一方面是訓練自己估算時程的能力
(其他像version control等基本技能當然是必備)
我記得我在書桌旁邊貼了一張版號跟內容的清單,
上面列了洋洋灑灑的功能要做,如今大部份都有做進去了。
但...現在想起來那是app毀滅的開始,因為app應該是要把單一功能做到極致,
我自己卻犯了以往一直嘲笑台廠包山包海包豬肉的問題

但是做app不是只有開發,銷售也是重要的一環,
也是對工程出身的人來說最弱的一環,
所以這段時間的學習重點之一就是如何銷售app。
由於app產業先天上的限制,有些銷售的手段無法使用,
例如兩套app bundle在一起打折賣。
一般開發者會用的方法還有像價格策略、送promote code、
在論壇介紹、請app review site試用、cross promote等等,
但我學到最重要的就是let the app sells itself.
好的產品是可以讓人願意替你介紹的。

我也有想過用app海來增加廣告收入,
但是我只做了一款"電費估算機"就發現問題了:
我沒辦法maintain這麼多套產品,這樣只會拖累其他app。
還好做"電費估算機"的時間不長,就經驗來說倒是挺有收穫。

以銷售額來說,這個產品是失敗的。
以目的(學習iOS開發、上架、銷售與客服)來說,我已經達到了。
幸好,這個app並沒有借錢開發,也沒有合作夥伴,我只要對自己負責就好了。
創業的壓力真的不小,收入就是個大問題,這完全影響了社會對你的觀感,
我還記得離職第3天去辦信用卡就被洗臉了。
即使你再怎麼吶喊自己學了多少、你有多少實力,
對大多數人來說,沒錢,你就是失敗。
奉勸各位想跳出來做的朋友,仔細想想你夠不夠抗壓性來創業。

可是我完全不後悔創業這件事。
有了外在環境的挑戰,才會去學習以往不會碰觸的東西;
這跟以往只是照興趣去看東西又不一樣,因為自己知道這次是來真的了
以往只會說"某某sales不知道在搞什麼鬼,他應該怎麼樣怎麼樣...",
轉眼間已經變成我應該要這樣那樣...
我還是熱愛做一個工程師的感覺,但我也不甘只是一個工程師。

我也了解到我不是萬能的,像美工就不在行。
雖然大部份的圖都是我自己畫的,但是一看就知道水準在哪。
所以下次一定要找美術來合作(以下開放報名 XD)。

這一年來有開心的時候,也有難過的時候,
有沒日沒夜埋首苦幹的時候,也有覺得白做工的時候,
重點是你所相信的是什麼,那才是支持你走下去的動力。
就像我相信我能改善人們的生活,降低資源的浪費,
我相信我能透過app做這件事,所以弄了一個"購物記憶王"。
它不賣,但是實現了我所相信的事。

10.29.2011

What iOS Simulator does not simulate: free()

在開發iOS app時一定要用實機測試,因為simulator跟實機的行為很多地方並不相同。Simulator除了不支援某些硬體功能外,在根本的運作機制上也有不同,這篇要分享的是在memory上的運作機制。

不過我也不打算寫太長,只寫要注意的地方就好了(因為太晚了~)。
我們先來看一段將Pixel array轉成UIImage的程式碼:

// 產生要畫的圖
unsigned char *pixels = malloc(nImageSize);
/*
 * (省略繪圖步驟)
 */
// 用畫好的圖產生image用的context
CGContextRef context = CGBitmapContextCreate(pixels, width, height, ...);

// 用完的記得要還,要養成好習慣!
free(pixels);

// 產生UIImage
CGImageRef imageRef = CGBitmapContextCreateImage(context);
UIImage image = [UIImage imageWithCGImage:imageRef]; 

// 要release才不會leak
CGContextRelease(context);
CGImageRelease(imageRef);

這段code在simulator能將pixels正確地轉成UIImage,使用Intruments也沒有發現memory leak,看起來沒問題吧!

很不幸地,在iPhone上完全不是這麼回事,你只會看到一片空白。

你看出問題在哪了嗎?是的,問題就在於pixels太早free了!應該要產生完image之後再free pixels。在iPhone上執行時,一呼叫free就會將memory清掉了,但simulator是在Mac OS上面執行,呼叫free時模擬的行為可能跟在iOS上面不一樣(可能是因為Mac OS X有GC的機制而iOS沒有)。如果只在simulator上面執行,就永遠找不到這個bug了!

所以如果你有在程式碼中呼叫free的話,記得在實機上也要測過喔!

P.S. 以後有機會再來分享在Android emulator中開發的心得。話說用i7-2600K來跑Android emulator還是很慢啊...

10.28.2011

追求細節

寫程式或是做產品跟畫漫畫其實有很多相似的地方,除了都是追求藝術外,也都講究細節。
這邊我所謂的細節不是指筆觸多精密,而是指漫畫中沒有留白的地方。

許多人看漫畫其實是在看“有圖的小說”,意思是說除了主要人物跟對白以外其實並沒有注意太多畫面上的細節(例如貼網點),而這些是使得畫面更加圓滿的必要元素;諷刺的是,當這些細節不存在時才會被人注意到似乎少了什麼東西,或是更直接一點就罵作者太混(我絕對不是在說富X)。

產品也是這麼回事,許多的小細節能使平凡的產品變得與眾不同,而堅持做出這些小細節的你也就變得與眾不同。

題外話:過度設計跟沒有細節一樣糟糕。

9.19.2011

[雜談] 近況更新

距離上次在這邊發文已經過了一年多了,更新一下近況順便做點記錄。

首先是...我又失業了 XD (Yeah~繼續練功~)

其實已經離開公司一陣子了,這段期間雖然也有許多公司的邀約(例如HTC),但是都婉拒了。每次HTC打來都很想問他們"我可以事情做完6點就下班嗎?"
工作也好些年了,好不容易對某些事情有了熱情,於是想趁還年輕、還沒有家庭跟小孩的時候好好去闖。

之前Android也玩了幾年,最近則開始接觸Apple iOS的開發,之後也會陸續分享在iOS上的開發心得,敬請不要期待,haha。

最後就是要給自己的小提醒:

我是很討厭加班文化的人,尤其是常態加班,那簡直是慢性自殺!
台灣人總覺得衰事不會輪到自己,而我過去就因為每天熬夜寫code到最後搞壞身體,差點連命都沒了。
長期加班也會造成效率低落的惡性循環,變成晚上寫bug白天debug。
我一直期許自己能在架構以及實作上做到預防勝於治療,對我來說一個好的工程師可以解很多bug,但是優秀的工程師能少寫幾個bug。(能寫到沒bug的稱為神)
因此我要求自己要在上班時間內提高效率做完當天的工作,不管上面長官會不會因此黑掉我。

但是這幾天晚上都寫到2, 3點,今天警覺到惡性循環的開始:昨晚寫的東西有32 bytes的memory leak,結果今天又花了一個早上來抓漏,最後發現是某個該用assign的property寫成了retain。Oh,也許有人說32 byte leak算什麼,但是我相信最不起眼的問題總有一天會在最不希望的時候反咬自己一口。

雖然有點經濟上的壓力,但是要相信自己,就算不加班不熬夜也可以做到!

8.20.2010

[舊稿] [Q&A] Layout of blogger 版面配置

發現這篇從08年年底到現在一直在草稿狀態,雖然只有一點點,不過還是貼上來好了,因為接下來不知道什麼時候才有時間更新...

偶爾嘗試點不同的寫作風格,就先用這篇開刀好了。本來想用敘事的方式來說明一下這個blog的排版原因和某些小細節,不過寫一寫發現有點難閱讀,所以改用Q&A的方式看看會不會有所改善。以下是左腦跟右腦的訪談:

Q:感謝你接受我的訪問,能不能請你大概聊一下整個版面的設計理念?
A:我希望我的讀者能在第一眼就很清楚文章的位置在哪裡,尤其是使用手持式裝置(PDA、SmartPhone甚至PSP)的讀者,這樣的讀者已經愈來愈多了。當然我也想提供行動裝置版的版面,不過在Blogger現有的template下是蠻難實現的。除此之外,在右側我放了一些Gadget,主要是希望能多些人交流。

Q:那配色呢,有什麼特別的原因嗎?
A:我想藍色是很貼近科技本質的一種顏色,所以我就用它當標題的顏色了。我曾經在白底黑字跟深底淺字兩種方式做徘徊,深底淺字的好處包括對行動裝置來說比較省電、對眼睛來說比較溫和,但是白底黑字才是大多數習慣閱讀的方式,另一方面由於blog可能會出現大量的貼圖,我不太喜歡貼圖跟背景色的反差太大(我想很多人都有在深色投影片貼過圖的經驗),所以最後選擇的是白底黑字。

在Framework中加入自己開發的API

大家好久不見,今天要分享的是如何將自有API跟系統一起build並且能將build出來的SDK放在Eclipse裡面用,不過這招不適合要放到android market上的程式,如果是這種情況,你需要的是git並且做好管理(包括把Eclipse的sync選項打開,好像越扯越遠...)

我想很多人應該也跟我一樣,開發程式時會寫出很多共用的API(通常是utility API),或是很多程式都會去bind同一樣一個service,而這些code(包括AIDL檔)通常會有很多份copy在各個Eclipse的project內,造成sync上的問題。這還不是主要的問題,更主要的問題在於,如果你的程式用到了hidden API,那可是連在Eclipse中build都build不過,更別想直接跑在emulator或是透過新版adb install到target上(因為Eclipse有error就無法產生apk檔)。

為了解決這兩個問題,我想最好的方式就是把code放在frameworks中一起build,而build出來的SDK正好可以給Eclipse用。

假設今天com.blogspot.dejob這個package有a.java及b.aidl要放進framework,那麼請試著這麼做:
1. 把這兩個檔案放在frameworks/base/core/dejob/java/com/blogspot/dejob/底下
2. 更改build/core/pathmap.mk,在FRAMEWORKS_BASE_SUBDIRS最後面加上"dejob \"
3. 更改frameworks/base/Android.mk:
  • 在LOCAL_SRC_FILES這一段的最後加入dejob/java/com/blogspot/dejob/b.aidl,這樣就可以讓AIDL檔build到framework中,但是這樣還不夠,你還需要下一步才能將AIDL檔內的javadoc build到SDK的document內。
  • 在aidl_files這一段的最後加入frameworks/base/dejob/java/com/blogspot/dejob/b.aidl
  • 在packages_to_document這一段的最後加入com/blogspot,意思是說com.blogspot底下的所有package都build進document。
4. 照下列順序來build code:
  1. 先確認你不是用root權限來build code!
  2. 裝好所有toolchain及下載android source code(騙一點篇幅,雖然沒稿費...)
  3. 切換到android目錄下打". build/envsetup.sh",請注意最前面那個"點"
  4. lunch sdk-eng
  5. make sdk
5. 接下來就可以先去運動一陣子(除非你的電腦配有12 core CPU+SSD RAID+16GB以上的RAM)

如果你的code沒寫錯,應該就可以看到SDK已經build完並且壓成zip檔了,接下來只要將這個zip檔解開,然後在Eclipse中指定SDK的位置到這個目錄就可以了。另外也建議在Eclipse的project中選clean將project重新build一次才能知道有沒有成功。

講一些題外話,最近一直在想要不要去應徵聯發科的android工程師,不過應該會被打槍吧,不知道還有沒有其他android相關的工作...

6.15.2009

在Eclipse下看framework的source code

參考: View Android Source Code in Eclipse

簡單來說,trace ADT的code會發現ADT會去android.jar同個目錄下的sources這個目錄找source code,
所以我們只要將android的java source code撿一撿,照package放到sources這個目錄就可以了。
所以參考link中的python script就是在幹這件事,不過我有兩點建議:
1. 在沒有make過的source code目錄下執行這個script:因為out這個目錄下有重複的java code
(而且out這個目錄有夠肥)
2. 把zippath = match.group(1).replace('.', '/') + '/' + file改成
zippath = 'sources/' + match.group(1).replace('.', '/') + '/' + file
這樣解出來的檔案和目錄就會放在sources目錄裡面了.

最近還在研究怎麼debug native code,之前make的gdb不能debug multi-thread的程式 = ="
看看android-ndk有沒有進展好了