Android應用程序生命周期中的活動與圖標
為您的 Android 移動應用程序添加導航風格
Activity 類是 Android 移動應用程序的基礎,您可以使用它來優化應用程序與用戶和移動設備的交互。讓應用程序生命周期中的交互方式與您的期望完全一致,并使用圖標與操作欄引導用戶使用 UI 導航與其他應用程序功能。
簡介
如今移動設備的功能已經強大到難以置信,比眾多開發人員用來編寫首個程序的桌面計算機還要強大得多。因此,大家很容易忘記移動設備仍然屬于資源有限的環境。 開發移動應用程序時,決不能忘記運行應用程序的環境所具有的局限性。尤其是當應用程序要與其他應用程序競爭系統資源時 — 其中有些應用程序對于用戶的日常行為而言比您的應用程序更加重要。
確保應用程序廣受歡迎的途徑之一是保證它節省系統資源。在 Android 中,使用和保持系統資源的機制都是 Activity
類。您越了解這個基本類(與 Java Servlet
十分相似)的生命周期,調整 Android 移動應用程序的資源使用與性能的能力就越強。
我們將從快速了解 Activity
類生命周期開始。通過一個示例應用的演示,您將了解處理 Android 應用程序生命周期內每個階段的方法。掌握這些方法協同工作的原理之后,就能聰明地使用系統資源。然后更新演示應用程序的導航系統,使用 操作圖標 代替菜單按鈕來實現用戶交互。圖標在移動應用程序 UI 中是十分標準的,而較新的 Android 設備(版本 4.2.2 及更高的版本)已經棄用了選項菜單,而改用操作欄。掌握如何將這些特性與您的 Android 移動應用程序集成在一起將使您受益無窮!
Activity 類生命周期
Activity
的生命周期直接對應著 Android 移動應用程序的生命周期。當用戶與應用程序或運行應用程序的設備進行交互時,Android 平臺將在 Activity
實例上執行回調。當用戶啟動應用程序時,初始的 Activity
將執行一個已定義的生命周期。當應用程序轉入后臺時,它執行生命周期的一個不同階段,而當應用程序關閉時則執行另一個階段。圖 1 顯示了每個交互階段的 Android Activity
生命周期。
圖 1. Android 的 Activity 生命周期
Android 移動應用程序生命周期包含四個階段:
- 啟動
- 暫停與恢復
- 停止與重啟
- 銷毀
后面的內容將會講述每個階段及其回調方法(可在 Activity
實例內部實現)。
Activity 生命周期中的啟動
在 前面的文章中,您已經使用了對應啟動 Activity
的回調方法,即 onCreate
。您可能也熟悉 onStart
與 onResume
,啟動時也會調用這兩個方法。現在,在 Activity
生命周期的上下文中考慮這些方法。
在 Eclipse Android 開發環境中,選擇 Override/Implement Methods... 選項即可輕松重寫方法,如 圖 2 中所示。
圖 2. 重寫 Activity 生命周期回調方法
接下來,選擇 onStart
與 onResume
方法:
圖 3. 選擇回調
現在使用 Android 的 Log
類加入一些跟蹤語句,就像我在 清單 1 中所做的那樣。
清單 1. 實現 Android Activity 回調
- @Override
- protected void onResume() {
- super.onResume();
- Log.d("overheardword", "onResume Invoked");
- }
- @Override
- protected void onStart() {
- super.onStart();
- Log.d("overheardword", "onStart Invoked");
- }
啟動應用程序的一個實例并通過 LogCat 查看日志,對結果進行檢查,如 圖 4 中所示。
圖 4. LogCat 的調試語句
您很可能已經猜到,首次加載應用程序時將調用 onCreate
,而在其他階段的上下文中使用 onStart
與 onResume
更加方便,比如當應用程序轉入后臺和重啟時。
Activity 生命周期中的暫停與恢復
因 為移動設備通常會運行多個應用程序,而它們會以各種方式來吸引用戶的注意力,因此您的應用程序應該知道何時讓另一個應用程序占據設備屏幕并使用更多資源。 有時,用戶在使用應用程序時需要接電話,或者是應用程序可能會彈出一個對話框,比如信息請求或錯誤消息。上述每種操作都將部分地阻斷當前的 Activity
。
當一個 Activity
被部分阻斷時,將調用 onPause
方法。當暫停的 Activity
重新獲得焦點時,將調用 onResume
。暫停與恢復表示受影響的活動被部分阻斷,而非完全隱藏。
當應用程序完全隱藏時,例如用戶打電話,還會調用 onPause
,但在這種情況下還會繼續調用 onStop
。當應用程序再次轉入前臺時,將先調用 onRestart
,再調用 onStart
,最后調用 onResume
。
下面解釋實現 onPause
、onRestart
與 onStop
時發生的事情。如果您已經有了本系列文章中使用的 Android 應用程序,那么在代碼中添加一些日志語句,然后運行應用程序。按下 Home 按鈕完全隱藏實例,然后單擊它的圖標再次運行。您應該看到調用了一系列方法。首先看到的是 onPause
,然后是 onStop
。 單擊圖標重新運行應用程序時,調用的方法依次是 onRestart
、onStart
和 onResume
。
銷毀 Activity
也是運行應用程序的常規過程中會發生的事情。例如,可以調用 Activity
實例的 finish
方法來終止該實例。這里的關鍵在于,因為關閉了一個 Activity
,所以它將遵循與被隱藏相同的生命周期,但它最后會回調 onDestroy
。
在 清單 2 中,我使用自己的 Overheard Word 應用程序演示了這個過程,具體做法是在向上劃動手勢時調用了 finish
方法。
清單 2. 銷毀 Activity 實例
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
- try {
- final SwipeDetector detector = new SwipeDetector(e1, e2, velocityX, velocityY);
- if (detector.isDownSwipe()) {
- return false;
- }else if (detector.isUpSwipe()) {
- finish();
- }else if (detector.isLeftSwipe()) {
- Toast.makeText(getApplicationContext(), "Left Swipe", Toast.LENGTH_SHORT).show();
- }else if (detector.isRightSwipe()) {
- Toast.makeText(getApplicationContext(), "Right Swipe", Toast.LENGTH_SHORT).show();
- }
- } catch (Exception e) {
- // nothing
- }
- return false;
- }
最常用的 Activity
生命周期方法是 onCreate
、onRestart
與 onDestroy
。例如,我使用了 onRestart
來刷新應用程序 UI 視圖的眾多方面之一,并使用 onDestroy
釋放到數據庫的連接,比如 Android 設備上本地運行的 SQLite。
現在可能還不明顯,但一旦開始與外部資源協作 — 比如外部 Web 服務或設備的文件系統或數據庫 — 這些生命周期階段將變得十分重要。
接下來解釋如何使用兩個 Activity
hook 方法 —onCreateOptionsMenu
與 onOptionsItemSelected
— 實現應用程序菜單行為。讓這兩個方法同步后,我們將把它們的功能連接到圖標,以實現額外的 UI 效果。
使用菜單與動作進行導航
當我在 Eclipse 中創建 Overheard Word 項目時,定義的第一個 Activity
有一個存根方法 onCreateOptionsMenu
。正如您猜想的那樣,此方法用于創建一個選項菜單。在老式的 Android 設備上,選項菜單由 Menu 按鈕表示。在較新的設備上,它被表示為一系列垂直的點,顯示在應用程序本身之中。較新的 Android 設備不一定有菜單按鈕。
在表示老式設備的模擬器實例中,有一個名為 "Menu" 的按鈕。只要單擊它,應用程序實例就會顯示選項菜單。在這個例子中,我們將看到用于導航的選項。例如,如果用戶按下 Home 按鈕,就會看到如 圖 5 中所示的內容。
圖 5. 一個未實現的菜單項
平板電腦上沒有菜單按鈕。用戶無法從 菜單 中選擇項目,而是被要求發起各種 操作。這個較新的 UI 欄稱為 操作欄,如 圖 6 中所示。
圖 6. Android 的新操作欄
盡 管菜單按鈕與操作欄的行為方式很類似,但操作欄只在較新的設備上能實現。由于我們的目標是老版本的 Android 系統(記住,約有 50% 的 Android 設備運行 Gingerbread!),因此我將使用更加熟悉的菜單按鈕進行演示。稍后,我將說明如何更新導航代碼以實現操作欄,滿足您以較新版本的 Android 及對應設備為目標的愿望。
創建一個選項菜單
為了翻新 Overheard Word 以提高用戶交互的效率,第一步最好是實現選項菜單,讓用戶能退出應用程序。退出應用程序是 Activity
生命周期的階段之一,因此我們將使用 Activity
方法實現這個功能。
記住,在 Android 應用程序中,所有與 UI 相關的業務都對應一個 XML 文件,因此定義 UI 的方法就是編輯布局的 XML 文件。Android 應用程序的 XML 文件位于項目的 res
文件夾的特定目錄中(例如,布局文件位于 layout
目錄中)。
為了快速進行練習,可以看一看 Overheard Word 主活動中 onCreateOptionsMenu
方法的默認實現方法 — 您看出了什么門道?
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.overheard_word, menu);
- return true;
- }
如果您正在考慮查找 menu
資源目錄中一個名為 overheard_word.xml
的 XML 文件,那么您就離成為一名 Android 專家不遠了!
我... 退出!
接下來,我們將編輯菜單資源 XML 文件,添加一個名為 quit
的菜單項。一開始需要在您的 res/values
目錄中找到 strings.xml
文件。找到之后,創建一個如下所示的新項:
<string name="quit_menu">Quit</string>
這個標記定義了單詞 Quit,可以通過標識符 quit_menu
來引用它(順便說一句,這對于應用程序的國際化很有好處)。接下來,打開 menu
目錄中的 overheard_word.xml
文件。 在這個文件中,將標題修改為 @string/quit_menu
,從而將單詞 Quit 鏈接到菜單項。
現在,啟動模擬器并按下 Menu 按鈕。應該看到一個菜單出現在屏幕下方,其中有一個選項:Quit。但選擇它什么效果也沒有,因為目前還沒有實現它。
我們將在一分鐘內為 Quit 選項添加實現代碼。但首先,我們要考慮移動應用程序的任意功能部分的另一重要元素,即它的外觀。您可能已經注意到,如今大量移動 UI(甚至越來越多的 Web 應用程序 UI)都使用圖標進行導航。下面將說明如何使用免費圖標替換通用詞按鈕。
移動 UI 設計中的圖標
在進入移動開發領域之前,我對圖標也有涉獵,但很少在我的企業應用程序中使用它們。當 Web 應用程序的交互性開始變得更加突出時,我發現自己使用圖標的次數也在增加。但直到我開始從事移動開發,圖標才真正成為我的工作重點。
如 果在 Android 移動 UI 設計中使用圖標,需要充分了解設備的分辨率。Android 設備生態系統非常龐大,您的應用程序可能需要在各種設備上運行,從低分辨率的小屏設備一直到配備 7 英寸大屏的高分辨率平板電腦。一個在手持設備上顯示效果良好的圖標,在平板電腦上可能顯得十分粗糙。
幸運的是,您可以控制應用程序圖標在不同設備上的外觀。快速訪問 Android 移動應用程序的 res
目錄。您應該可以看到一些名為 drawable-something-pdi
的目錄(這里的 "something" 是任意字母序列)。這些目錄對應各種設備屏幕的分辨率。在這些目錄中放置大小正確的圖標與其他圖像文件,可以確保您的圖標在不同類型的設備上正確顯示。
例如,對于分辨率超高的設備,Android 將使用 drawable-xxhdpi
目錄中的圖標。此目錄中的啟動圖標應該為 96 x 96 像素,并且至少為 320 dpi。drawable-ldpi
目錄中的啟動圖標應該為 36 x 26 像素和 120 dpi。您也可以選擇創建一個默認的 drawable
目錄,當 Android 無法找到指定圖標分辨率對應的文件時,將使用此目錄下的圖標。
為了簡單起見,我將為我的 Overheard Word 應用程序創建一個 drawable
目錄。我在此目錄中放置一個 26 x 26 的圖標文件(.png 格式),用于退出選項。
圖 7. 為 drawable 目錄添加一個圖標
我的下一個步驟是在選項菜單中引用該圖標,具體做法是在我的 overheard_word.xml
文件中更新 menu
項,如下所示:
- android:icon="@drawable/quit_icon"
如果您嚴格遵循我的步驟,則應更新同一元素的 id
。為它指定一個描述性的字符串值,如下所示:
- android:id="@+id/quit_item"
進行下一個步驟,即在 onOptionsItemSelected
方法內部實現退出行為時,使用一個描述性的、易于理解的字符串值是很有幫助的。我們將能夠通過 quit_item
的 ID 在選擇事件中引用菜單項。現在啟動模擬器并按下 Menu 按鈕。我認為您會喜歡所看到的情景!
圖 8. 好圖標!(由 Glyphish 提供)
實現菜單行為
現在我有一個外觀漂亮的圖標,可以用于 Quit 菜單項(我希望您也有一個圖標用于自己的應用程序),但我仍然需要添加代碼,從而告訴應用程序當按鈕被按下時應該做什么。
實現選項菜單中的任意行為都要從重寫 onOptionsItemSelected
方法開始。因此重寫該方法,然后更新代碼,使其看起來像下面這樣(但記住要針對您自己的應用程序調整菜單項 ID):
清單 3. 處理菜單項選擇
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.quit_item:
- this.finish();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
注意,這只是一條簡單的 switch
語句。如果選擇 quit_item
的 id
,將調用 finish
方法來關閉應用程序。
在模擬器中試用這段新代碼:按 Menu 按鈕,選擇退出(X)選項,并觀察 LogCat 上出現的內容。您應該看到一個完整的 Activity
生命周期,依次分為如下幾個階段:onPause
、onStop
和 onDestroy
。
Android 3.x 中的操作欄
如前所述,較新版本的 Android(Honeycomb 及更高版本)使用操作欄取代了選項菜單。較新的設備甚至不一定有 Menu 按鈕,因此了解應用程序的導航(或其他功能)也能用在操作欄中。
確保通過為選項菜單編碼而實現的導航功能也能用在操作欄中,并不需要做很多工作。您已經實現了所有需要的方法,剩下的工作只是對 XML 源文件進行一些修改。
首先需要創建一個模擬器實例,用于模擬使用操作欄而非菜單按鈕的設備。最簡單的做法是模擬一臺平板電腦。在 Android SDK 安裝中啟動 Android SDK Manager 命令行應用程序(是位于 tools
目錄中的 android
命令)。SDK Manager 啟動并運行后,從 Tools 菜單選擇 Manage AVDs... 選項。這將顯示一個對話框,在其中可以定義一個新的模擬器或 Android Virtual Device(或 AVD)。選擇 7.0'' WSVGA (Tablet) (1024 x 600: mdpi),然后將模擬器的目標設定為至少 Android 4.2.2。完成后,您就有了一個不響應菜單按鈕的模擬器,如 圖 9 中所示。
圖 9. 創建一個平板電腦模擬器
接 下來,在該平板電腦實例中啟動您的應用程序。您應該在右下角看到一條由三個點組成的垂直線。它看起來很棒,對嗎?默認情況下,Android 將保留菜單行為,甚至在較新的顯示設備中也是這樣。通過更新應用程序的 XML 資源,您可以升級操作欄的外觀與行為,讓它變得更加自然。
從應用程序的 AndroidManifest.xml
文件開始,您將在該文件中更新 SDK 目標:
- <uses-sdk android:minSdkVersion="11" android:targetSdkVersion="17" />
接下來,在 Eclipse 中進入您項目的 Properties 頁面,并將 Project Build Target 更新為高于 Android 4.2.2 的任意 Android 版本。單擊 OK 并讓項目重新編譯。然后在 menu
目錄中找到菜單 XML 文件。更新它的內容,如下所示,這將保留 Quit 的 item
定義。
- android:showAsAction="always"
最后,如果您的項目在 res
目錄下沒有兩個名為 values-v11
和 values-v14
的子目錄,那么創建它們。接下來,在 values-v11
目錄中添加以下 XML 文件:
- <resources>
- <style name="AppBaseTheme" parent="android:Theme.Holo.Light"></style>
- </resources>
在 values-v14
目錄中,添加此文件:
- <resources>
- <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar"></style>
- </resources>
現在重啟模擬器,您的新圖標應該出現在右上角:
圖 10. 一個帶圖標的操作欄
現在回到 menu
目錄中的菜單文件(其中定義了 quit
項),將 showAsAction
更改為 never
。重新運行您的應用程序,應該可以在右上角看到這三個垂直的點。
不要忘記重新設定
注意,如果希望將應用程序目標保持為 Gingerbread,您需要重新設定項目的編譯目標并撤消本節內容中對 XML 文件所做的更改。這些更改無法做到向后兼容!
圖標的更多樂趣
迄今為止,您已經為以 Gingerbread 為目標的應用程序添加了一個菜單選項,看到它被十分完美地轉換到實現操作欄的新設備上,并了解如何通過對應用程序的 XML 文件進行一些更新來升級該功能(如果您選擇這樣做)。
現在,讓我們總結一下您學到的關于圖標的內容。您已經完成了為應用程序的導航菜單添加一個圖標的主要工作,因此更新它的 main 圖標應該沒什么問題。對用戶而言,這個圖標就代表著您的應用程序,因此必須知道如何更新和定制它。
我將更新 Overheard Word 的圖標,您也可以對自己的應用程序這樣做。再說一遍,在 Internet 上進行快速搜索可以找到大量的圖標站點。在這些站點中,我發現了一個有趣的圖標集合,其中有一個圖標確實深受我的喜愛:
圖 11. 一個漂亮的新圖標
回想一下,這個圖標在不同的設備配置文件上呈現不同的效果。我希望確保用戶對 Overheard Word 產生良好的第一印象,因此讓圖標分辨率適合我的目標設備范圍是很有必要的。幸運的是,我知道一個真正好用的站點,可以幫我實現這個目標。
Android Asset Studio是一個 Google Code 項目,擁有大量可為 Android 開發人員提供助力的實用工具。我使用頻率很高的一個工具是 Launcher Icons。我要做的就是單擊 Launcher Icons 鏈接,然后上傳我的圖標。該實用工具會針對各種設備配置文件生成正確尺寸的圖標文件,然后我可以 zip 文件的格式下載它。該文件包含四個目錄,每個目錄下都包含一個我上傳文件的特定分辨率與尺寸的版本。
圖 12. Launcher Icons 為 Android 制作尺寸正確的圖標
接下來,我將每個目錄中的 ic_launcher.png
文件復制到我應用程序的 res
文件夾的相同子目錄中。注意,在此過程中,我可能會替換 Eclipse 生成的原始圖標文件。
最后,我再次運行應用程序,并等它出現在我的模擬器實例中。我單擊 Home 按鈕,然后查看結果:一個漂亮的應用程序圖標(對我而言)標志著 OverHeard Word 是所有用戶設備上最有趣的應用程序!
圖 13. 現在變得很形象!
結束語
在本文中,您了解了 Activity
的生命周期,并知道如何使用它來改進應用程序對設備資源的利用。您還學會了如何使用菜單和操作欄定義與實現導航構件,以及如何使用圖標替換單詞按鈕。
您 在本文中學到的所有內容對于構建 Android 應用程序都是至關重要的。Android 上的移動開發很容易上手,當然也樂趣無窮,但我希望您也要明白它與您所熟悉的 Java 開發屬于不同范式。Google Play 與其他應用程序商店中存在著成千上萬個應用程序,因此通常只有規劃良好、設計用心和編碼精妙的應用程序才能名列前茅。要學的地方還有很多!