文 / 陳彧堃
本文指出“流暢的環境”、“友好的體驗”和“節省電量”是保證Android應用擁有良好用戶體驗的三要素。
Android開發目前是移動開發中的“當紅炸子雞”,大量Java程序員湧向Android,同時會習慣性地將桌面和Web端的開發/設計經驗帶到移動設備上。這樣的好處是充分利用了移動開發和桌面/Web服務的共性,比如廣泛使用的列表、本地數據庫等常用組件;壞處是移動和桌面/Web的使用場景和載體完全不同,直接移植桌面端開發的經驗有害無益。
比如,手機主要在碎片時間使用,用戶容易對復雜的界面設計感到疲憊;同時,移動環境中上網慢,網絡連接頻率和失敗重發機制的設計更有講究;此外,手機電池續航能力差,後台復雜的計算會加速耗電速度。這些開發理念直接影響用戶最終體驗,下面我們來討論一下在Android中如何以用戶體驗為導向進行開發優化。
雖然不用深入了解底層,但需要對系統有基本的了解。Android系統分層清晰,最底層是Linux Kernel 2.6,之上包含了Webkit、SQLite、OpenGL ES等基礎C/C++庫,同時Dalvik虛擬機運行於Kernel之上,幫助應用進行底層內存管理(這樣使Android應用無法直接進行內存釋放)。這些庫一方面被系統大量使用,另一方面也通過Framework層提供接口給開發者。此外,Framework層還提供其他系統級的服務,如消息通知服務、位置獲取服務、設備信息讀取服務等。
由此可見Android對於開發者非常開放和靈活,盡管如此,開發時仍然要注意不要過於隨意,以免產品過於復雜而讓用戶不知所措。當然,除了少數系統級應用開發需要深入了解Framework層實現機制之外,一般第三方應用開發者並不需要深入了解每一層原理,應把重點放在如何理解和靈活運用龐大的Android SDK API。
本文主要圍繞用戶的三種感覺來說明如何進行開發。
流暢的環境
讓用戶感覺使用非常流暢。遲緩會潛移默化地留下不好的印象。用戶看見App的圖標,便會在心中和“遲緩”、“卡”、“不穩定”畫上等號,產生“打開畏懼症”。
用戶滑動Listview、Gallery、Coverflow時覺得卡,多半是因為相應Adapter對getView的處理不夠好。每個Item都會和數據源綁定,而數據源的獲取方式有多種:網絡、本地文件、SQLite數據庫、SharedPreference以及內存,它們的傳輸時間分別是7秒、2秒、1秒、100毫秒、5毫秒。
對於最耗時的網絡請求,很多人會采用異步操作,不會讓用戶耗費精力在網絡等待過程中。但在I/O以及SQLite查詢時,用戶的等待時間容易被忽略,從而降低滑動的流暢感。Android用戶常常遇到的ANR(Application Not Responding),便是這個問題的升級版。要知道,Activity Manager和Window Manager監視著應用程序的響應,當發現按鍵或觸摸發生後5秒還沒執行完處理邏輯,或是BroadcastReceiver處理時間超過了10秒,系統便會拋出ANR錯誤,並提醒用戶強制終止應用。
我的建議如下:
對於無法在短時間完成的操作,在獨立線程中處理,Android有多種異步處理模型可供使用,包括Thread-Handler、AsyncTask以及Loader and CursorLoader。
盡可能減少復雜計算和降低I/O,充分估計對象的使用頻率,選擇合適的數據源。個人認為大部分應用中不會存在太多太大的對象,可以考慮將數據緩存在內存中。如果應用中有太多圖片不能一直緩存,可采用LRU(Least Recently Used ,最近最少使用)算法將不常用的緩存清理出內存,這樣緩存大小可控,從而不會出現Out of Memory(內存溢出)的Bug。
但要注意,算法是把雙刃劍,如果你享受到類似LRU帶來的提速後的爽快,就可能會挖空心思探索更高效的算法。這時要慎重,後面會講到看上去很牛的算法帶來的問題。
另外,網絡等待雖然是最耗時,但卻容易被忽略。因為粗看上去網絡是不可控的,與開發無關。一般會設置幾秒鐘的超時,超時則重發。事實上,在國內,中國移動的GRPS網絡占主導,所以手機上網普遍很慢,HTTP連接上下行10秒是很正常的,超時設置20秒都不為過。同時,根據友盟對Android應用使用的統計,用戶在每個App上的一次啟動花費時間是1分鐘左右,理論上有3次重發機會,但一次超時(假設是20秒)後,用戶就已經失去信心,不會再等待一次了。所以在開發時,要結合具體使用場景,設計數據預取機制,盡量降低網絡請求次數,同時考慮gzip、protobuf等數據壓縮和編碼機制,保證一次取到的數據不至於太大而造成額外延時。
Android架構圖(圖片來源:http://developer.Android.com/guide/basics/what-is-Android.html)
友好的體驗
不友好的體驗來自三個方面。
其一是Android的碎片化帶來了UI適配問題。Android機型眾多,和iPhone相比,界面適配飽受诟病。要保證應用能運行在不同分辨率的手機上,需要理解Android提供的自動適配方案。事實上,Android系統為UI適配做了充分的考慮,只要理解系統對此的精心設計,就能在開發時少走很多彎路,給不同分辨率的用戶提供友好的呈現界面。
簡單來說可做如下解釋:
開發時避免絕對布局(AbsoluteLayout),因為這會讓你的應用只在測試機上“看上去很美”,放到別處就橫七豎八。
界面控件大小單位多用DIP(Device Independent Pixels),理由同上。
圖片盡量使用系統提供的NinePatch技術,能使同一張小圖片在不同分辨率的屏幕上保持精度自由縮放。
其二是濫用通知服務,導致用戶很容易被打斷。典型場景是在通知欄上的各種通知消息,有關無關的都推送,讓用戶感覺不適。建議是通知適可而止,除非是對用戶真正有用的信息,否則最好讓用戶進入程序後再提示。
其三是主要來自設計師的問題,就是照搬iPhone應用的設計。Android的系統特點不同於iPhone,如程序的棧式管理機制、菜單按鈕、返回按鈕,從而用戶的預期也不盡相同。比如我發現很多設計師喜歡拋棄返回鍵,模仿iPhone給每一頁設計一個軟返回按鈕。生硬做作的移植會導致與用戶預期不一致,是徹底的設計敗筆。
節省電量
隨時都得插在牆上充電的設備,不叫移動設備。如果你的App讓用戶一直守著牆角,用戶也會很快把你丟到牆角。你會問:“他怎麼知道我的應用耗電?”很抱歉,目前來看,Android用戶中有大量發燒友和技術高手,同時系統很不客氣地記錄了每個應用的耗電量,於是用戶偶爾會去系統後台查查耗電大戶,之後會毫不客氣地打開卸載工具。
所以需注意以下幾點:
第一,不要絞盡腦汁設計復雜算法,不要在後台跑服務,不要網斷了還不停重試。在開發一個模塊前先想想會不會費電,如果會,就不要去做。代碼是為了服務用戶,而不是折騰用戶。
高手喜歡挑戰,尤其在手機上實現精巧的算法,這樣能帶來更強的征服感。有人曾在手機上實現了布隆過濾器(一個龐大精巧的類哈希表,多用於在服務器端如垃圾郵件查找),其內存消耗和計算復雜度都遠遠高於普通的HashMap,且實現並不容易。結果App發布之後,出現用戶抱怨耗電量大,並且經常出現Bug,最後還是老老實實換成了HashMap。任何算法的目的都是為了服務用戶,如果簡單自然的方法能更好地做到這點,何樂而不為?如果真的在客戶端找不到簡單的算法,則需要反思——為什麼在手機上需要復雜的計算?是否該將這些計算放在服務器端?
第二,不要在後台濫用Service。Android非常開放,開發者可在後台觸發任何處理邏輯,肆意占用CPU和內存。一般來說,Service的目的是為了監控變化,包括系統和網絡變化。系統變化可通過注冊BroadcastReceiver監聽控制,比如應用安裝和卸載等事件,這樣耗電量非常小,完全可替代在Service中輪播。網絡請求無法用BroadcastReceiver監聽,但是有兩個建議。
無嚴苛的實時性要求,可延長輪播間隔,如6小時自動請求一次,同時時間隔可通過服務器在線更新。這樣既省電,偶爾急需實時推送時也可在線調整時間間隔。
對實時性有要求,考慮使用成熟的推送服務,如Google的C2DM(http://code.google.com/Android/c2dm/),和亞馬遜的AWS SDK (http://aws.amazon.com/sdkforAndroid/)。
第三,網絡請求不要太頻繁。系統組件中最耗電的是屏幕,其次就是網絡。前文已經提到過,網絡出錯重發會降低用戶體驗,還會耗費電力。可通過數據預取結合數據壓縮算法減少網絡請求次數。
總之,在開發時我們要替用戶思考是否做到了“流暢、友好、省電”,以保證App擁有不錯的用戶體驗。
作者陳彧堃,曾任職於微軟研究院,從事數據挖掘及機器學習的相關研究,對時空數據挖掘,普適計算等領域有深入了解。2010年4月加入友盟,開始打造友盟移動開發平台。