2014年12月8日 星期一

又有 APP 被 Google 下架

最近得知又有一位朋友的 app 被 Google 下架,身旁的朋友曾被 Google 下架的,我知道的就有好幾位。

我也是苦主之一,事實上應該算是全球第一位被 Google 下架 app 的開發者,早在 2009 我和 Jason Cheng 共同開發的 aNetShare 就被下架過 (見 www.cool3c.com/article/7186),理由當然很瞎。

開發者條款中很多禁止事項都寫得很含糊,解釋權又在 Google 身上,硬要說你違反那一條,都可以ㄠ的過去。我覺得 Google Play 最差的部分是沒給你改的機會,不管你已經累積多少使用者,投入多少資源,說下架就下架,這不是一個友善的 B2B 做生意方式。App Store 的事先審核,在這個面向來說,就是優點。可以做的,有無違反商標,Apple 都事先告訴你。不像 Google, 在你投入這麼多資源後,一覺醒來時,突然發覺全部被 reset to zero. 那可是會想殺人的。

我每次遇到相關的 Google 負責人員,都會向他們提到此事,不過這麼多年過去,看來他們舞照跳,馬照跑,外頭求救無門的 app 苦主還是有增無減。

要看原本我在 Facebook 的 po 文,請點 www.facebook.com/samlu128/posts/10201968529373734

在 Google 的服務有所改善之前 (看來是沒希望了),最好的方式還是熟讀底下這些與 APP 相關的協議


繼續閱讀全文...

2014年5月12日 星期一

為什麼你的 app 要在每個 Android 版本上測試?

先看一下底下的程式片段。這樣的程式在某些手機上會當掉,你有看出來這問題出在那?

程式中的 registerReceiver(null, IntentFilter) 用的是一個很典型取得當前手機電量的方法。由於 ACTION_BATTERY_CHANGED 是一種 "sticky intent",因此我們可以透過傳 null 給 receiver 及適當的 IntentFilter,來取得該 intent 所攜帶的 bundle 資料。

乍看之下,這樣的程式好像沒有問題。不過我們先來看一下 Context.registerReceiver() 的文件說明。看到沒,文件中的 Note 說明了這個函式不能在 BroadcastReceiver 中被呼叫。

Note: this method cannot be called from a BroadcastReceiver component; that is, from a BroadcastReceiver that is declared in an application's manifest. It is okay, however, to call this method from another BroadcastReceiver that has itself been registered at run time with registerReceiver(BroadcastReceiver, IntentFilter), since the lifetime of such a registered BroadcastReceiver is tied to the object that registered it.

嚴格地說,應該是呼叫 registerReceiver() 的 ctx 不能是一個 BroadcastReceiver 或其繼承物件。程式當掉的原因看起來似乎是清楚了。

不過另個問題來了,多數工程師寫程式時,沒法記住所有的特別限制。而這樣的問題,在編譯階段無法被偵測到,只能靠動態測試。就以我來說,我寫程式多半在最新的 Android 版本 (4.4.2) 上開發、測試。上述的程式在我手機上,當時竟然是可以正確執行無誤。在 app 要釋出前,我還在 4.3.x, 4.2.x 的手機上測過,也都沒有問題。直到一位使用者的回報,我才發現這樣的程式在 4.1.x 的手機上執行,會有問題。

看起來正確的結果應該是,上述程式在4.1(含)以前的手機上執行,都會出問題。但是在4.2(含)之後的手機上,卻可以正確執行無誤。

為什會這樣?我們看看 frameworks\frameworks\base\core\java\android\app\ContextImpl.java 原始碼,就可以找到答案。

底下是 Android 4.1(含)以前的原始碼:

底下是 Android 4.2(含)之後的原始碼:

很不幸,你在文件中找不到這個 API 規格變動的相關說明。這個因 APIs 規格變動,所引起的問題,還不是唯一的例子。事實上,類似的問題,不管是 Android, iOS 或是其它作業系統,都會發生。所以當你開發好你的 app 時,千萬記得要在各個作業系統版本上都測試一遍。只不過要做到這樣,你得先建立自動化測試的機制與完善的測試案例。隨著 Android 作業系統的快速演進與增加,慢慢地你會發現 app 的測試成本,將不下於開發成本。


繼續閱讀全文...

2014年4月27日 星期日

Hardware Acceleration 的陷阱

最近寫了個程式,剛好要利用 Canvas 中的 clipPath() 函式來繪圖。結果是,不管我如何呼叫 Canvas.clipPath(),似乎一點作用也沒有。最後才發現,原來是 hardware acceleration (硬體加速) 這個系統功能搞的鬼。

Hardware acceleration 是從 Android 3.0 開始,Android 繪圖系統利用 GPU 支援的 OpenGL 渲染功能來加速 2D 圖像繪製的一種新功能。硬體加速?可以加速繪圖的速度,這聽起來似乎很美好。不過實際上,這個功能有很多的限制。例如,Canvas.clipPath() 在 API 17 之前就是不支援的。

這個連結中 列出所有硬體加速功能的限制。很複雜吧!更糟的是,只要你的 minSdkVersion 或是 targetSdkVersion 設定為 14 以上,這硬體加速功能,預設竟然是開啟的!

如果你的應用需要用到的 Canvas 的各式繪圖函式,而結果又出乎你預期時,我會建議你先關掉硬體加速功能,試試看是否因為這個原因所導致的。

要關閉硬體加速功能,最簡單的方法是加上 android:hardwareAccelerated="false" 這個屬性到 AndroidManifest.xml 中的 Application 標籤中。不過這個方法會讓你應用中的所有 View 的繪製都不允許硬體加速。這似乎有點極端,所以 Android 也提供 Window based 或 View based 的關閉方法。當你確定應用的不正常行為是因硬體加速功能所導致,這時你可以利用前述方法,將某些 Views 或 Windows 的硬體加速功能關掉即可。


繼續閱讀全文...

2014年4月20日 星期日

好用的 Activity.getView()

在 Activity 中最常看到的函式呼叫,就是 findViewById(),常見用法如下:

由於 findViewById() 傳回的是 View 這個通用型別。因此實務上每次都要強制轉型成實際的型別,這樣的程式碼看起來實在有點礙眼。

要解決這樣的問題,你只要在 Activity 中,加入底下這個 getView() 函式。

之後,你就可以將原先的 findViewById() 呼叫改寫成底下這個方式:

一個小技巧,卻可以讓你的程式碼看起來清爽許多。當然在 Fragment 中,你也可加上自己的 getView(),至於如何寫?建議自己研究一下。


繼續閱讀全文...

2014年1月6日 星期一

冬天是為了迎接更美麗的春天

APP 如果說是一個產業,他的春天是開始於 2009,2011 則是百花盛開的季節。不過在 2014 的開始,我感覺 APP 產業 (我只開發工具類 apps, 以下觀點對於遊戲類 apps 可能不適用) 已開始邁向寒冬,這第一波寒流尤其對個人開發者或新創小團隊會是個嚴厲的考驗,建議及早作好過冬準備。

由於我之前一直鼓吹 APP 對於小開發團隊,會是個很好的發展。現在我有這個不對勁的感覺,我覺得我有這個義務,要儘早告訴大家。

說寒冬,可能是誇張了點,不過 app 開發者獲利往下滑與新 apps 難獲利的趨勢,應該在 2014 年 Q2-Q3 會更加明顯。如果打算離職寫 app 的朋友,我建議延後你的計畫;想要大舉擴編 app 團隊,開發新 apps,最好三思;新 app 的損益平衡點時間,你得加倍計算。

至於現有的大咖 app 開發團隊,會延後感受到這波寒流。說到這,這有點像是房地產,現在的大咖 app 開發團隊,就像是市區豪宅,而新加入的 app 開發團隊,就像是市郊小套房。當產業空頭來到時,總是先跌套房,再跌豪宅;先從市郊跌起,最後才是市中心。

在之前,開發一套工具 app,從開發完成到有足夠的收入,可以養活自己,樂觀估計最少要 1 年。現在加上這第一波寒流,時間鐵定要拉長,所以我說要及早作好過冬準備,免得到時彈盡糧絕。

這是我個人透過我旗下 apps ,再加上一些不便透露的數字,所獲得的 "感覺" (所以有一定的不可靠性與主觀偏差)。我不是分析師,也沒更多客觀數據可以向你證明我說的正確性有多高。

我希望我的感覺是錯的,也不想危言聳聽,如果其他人有不同的感覺,那就把我這篇說的忘掉。日後我如果有更新的"感覺",再和大家更新。

至於 APP 產業會不會消失,那倒不至於,因為需求是真的,也還在。只是參與的人多了,他需要時間去蕪存菁。APP 產業也需要有些結構性的改變,才可讓市場活絡起來。

我相信寒冬過後,會是更美麗的春天

PS: 從 2009 年到 2013 我一直致力於 Android Apps 開發之各項教學與推廣活動。到了 2014 年的開始,我想這項階段性任務應該是到了他該結束的時候。也就是說,從 2014 年開始,請不要找我接演講、授課或採訪等相關活動,謝謝曾經幫助過我的各位好朋友。


繼續閱讀全文...