1. 程式人生 > >安卓四大元件 之 Activity 之 任務棧和回收棧(Tasks and Back Stack)

安卓四大元件 之 Activity 之 任務棧和回收棧(Tasks and Back Stack)

本文摘要

|---Saving Activity State  

|---Managing Tasks

    |---Defining launch modes

    |---Handling affinities

    |---Clearing the back stack

|---Starting a task

應用程式(也就是我們日常接觸到的“App”)中往往包含著很多activity。每個activity都應該圍繞著某種具體功能來設計,使用者不僅可以使用該功能,而且可以通過這個activity去開啟其他activity。比如,郵件App中有一個activity是專門用來展示未讀郵件列表的,當用戶選中其中一封郵件時,一個新的activity就會被開啟用以閱讀這封郵件。


一個應用程式中的activity可以開啟另一個應用程式中的activity。當然了,前提是這兩個應用程式同在一臺裝置上。舉個例子,如果我們的應用程式需要傳送一封郵件,那麼我們可以定義一個intent來呼叫其它應用程式中的activity幫我們實現傳送的功能。另外,如果有需要,還可以用這個intent攜帶一些資料,如收件人地址和要傳送的訊息內容等。這條intent發出後,另一個應用程式中宣告自己可以處理此類intent的activity就會被開啟。在我們這個例子中,我們定義的這條intent想要做的是傳送一封郵件,所以郵箱App中負責撰寫郵件的activity開啟了(如果同一裝置上存在多個activity支援同一型別的intent,那麼系統會提示使用者從中選擇一個)。當郵件傳送完畢,郵箱App中負責撰寫郵件的activity就會關閉,然後我們的activity會重新展現在螢幕上。儘管負責傳送郵件的activity來自另一個應用程式,但給人的感覺好像它就是我們的應用程式的一部分。這兩個並不存在於相同應用程式中的activity之所以能夠流暢的呼叫,就是因為在安卓將它們放置在同一個任務棧中。


任務棧就是使用者在完成某一事情時要與之互動的一系列activity的集合。這些activity被有序地放置在回收棧中,排列的順序就是其被開啟的先後順序——先開啟者置於棧底,後開啟者依次向上羅列。不理解?沒關係,請看下面這張圖:

【小貼士】平時擺弄手機App時,相信你有此體會:通常情況下,開了幾個頁面(activity)之後,點選手機上的返回鍵,就可以逐層後退。就比如上圖中的例子,當我們操作到3號activity時,按一下返回鍵就可以退到2號activity介面,再按一下返回鍵,1號activity就又出現在螢幕上了,如果再按返回鍵,就退回到手機桌面了。其實這就是因為任務棧在幫我們保留所有操作過的activity,而且是按照我們操作的順序保留的。最主要的是,任務棧幫我們保留了每一個操作過的activity的狀態,比如螢幕滾動的位置、我們在可以編輯文字的地方輸入的內容等等,這使得我們返回到操作過的頁面時,看到頁面還是我們剛剛操作時的樣子。其實我們也可以通過一些方法讓activity在開啟時不被放置入任務棧(後面會有介紹),如果一個activity在開啟時沒有被放入任務棧,那麼使用者在按下後退按鈕或者點選返回按鍵時,就永遠不會再返回到這個activity頁面了哦。


通常,裝置的Home屏是任務棧開始的地方。因為當用戶在Home屏上點選某個應用程式的快捷方式(或者在應用程式啟動器中點選它的App圖示)時,該應用程式的任務棧就會來到前臺。如果此時該應用程式不存在任務棧——即該程式近期沒有被開啟過,那麼一個新的任務棧就會被建立,並且該應用程式中充當主介面的那個activity開啟,這個activity就作為這個任務棧的根activity。

【小貼士】什麼是application launcher?請見下圖:


當一個正在活動的activity開啟另一個activity時,新開啟的activity(被開啟者)會被置於任務棧的棧頂,並且會獲得使用者焦點。而之前那個activity(開啟者)仍被保持在棧中,但它已處於停止狀態。 當activity停止時,它與使用者的互動狀態會被系統保持住。當用戶點選返回按鈕時,處於前臺的activity(被開啟者)被系統從棧頂拽出來銷燬掉,於是,之前的那個activity(開啟者)帶著其被停止時的UI狀態重新展現在螢幕上了(這個activity的UI狀態是被重置的)。無論棧中有多少個activity,它們都不會被重新排序,對於Activity來說,它只會被放進棧中或從棧中被取出(activity被開啟時會被放在棧頂;當用戶採用點選“返回”按鈕的方式離開activity時,它會從棧頂被拽出來並銷燬掉)。由此可見,回收棧採用的是一種“後進先出”的存取機制。那麼,每當一個新的activity開啟時,任務棧裡時什麼狀態?每當使用者點選後退按鈕時,任務棧又會發生什麼變化?下面這張圖用時間軸的形式向我們展現每當開啟新的activity時以及每當使用者點選後退按鈕時任務棧的變化:


通過上面這張圖,我們可以看到,每當新的activity開啟時,它們都是如何被放入回收棧的。當用戶點選後退按鈕時,當前顯示在螢幕上的activity(也就是位於棧頂的activity)會被銷燬,它之前的那個activity會重新回到螢幕上(也就是重新成為棧頂)。

如果使用者繼續點選後退鍵,那麼,每點選一次,系統就會到回收棧中“拽”出位於棧頂的那個activity並將其銷。當回收棧裡的activity全部被消滅了之後,螢幕上顯示的是桌面的Home屏(當然,也有可能是任務棧開啟後的首個activity)。注意:如果所有activity都從回收站中拽出來銷燬掉了,那麼由這些activity組成的任務棧也就不復存在了。

如果使用者正在操作某個應用程式時按了HOME鍵,或者使用者在當前應用程式還處於前臺的情況下開啟了另一個應用程式,那麼使用者要離開的應用程式所在的任務棧(包含著一個或多個activity)可以整體轉移到後臺。如果任務棧整體轉移到後臺,其中所包含的所有activity都將處於停止狀態。雖然如此,回收棧會將這個任務棧以及棧中每個activity被停止時的狀態都完好無損地保持住。也就是說,對於該任務棧來說,它僅僅是失去了焦點而已,就像下面這張圖所展現的:


整體移到後臺的任務棧當然也是可以整體重回前臺的,這種機制使得使用者可以回到之前離開的頁面上繼續操作。舉個例子,假設當前任務棧(任務棧A)中有三個activity,即當前活動的activity下面還壓著另外兩個activity。使用者按下Home鍵,然後從應用程式啟動器(application launcher)中開啟了一個新的應用程式,當Home屏顯示在螢幕上時,任務棧A就已經移到後臺了;當新的應用程式啟動時,系統會為這個新的應用程式建立一個任務棧——也就是任務棧B——用於放置新應用程式的activity。使用者對這個應用程式完成了操作之後,又回到Home介面,然後選擇了之前開啟任務棧A的應用程式。這時,任務棧A來到了前臺,這個棧中的三個activity都完好無損,位於棧頂的那個activity被重新展現在螢幕上。這時,使用者仍然可以通過Home鍵回到Home屏,然後點選開啟了任務棧B的那個應用程式的圖示使它再次回到前臺。這個例子實際上講的就是安卓平臺的多工處理。

【小貼士】關於讓開啟任務棧B的應用程式回到前臺的方法,除了通過Home屏之外,還有一種方法,就是通過一個類似於電腦中的工作管理員一樣的東西——overview screen——來選擇已經開啟的應用程式,一般來說,長按Home鍵可以調出這個介面,但有的手機會單獨設定一個用於開啟overview screen的按鈕。

【注意】在後臺可以同時保持多個任務棧。然而,如果使用者開了多個應用程式,那麼這些應用程式所對應的任務棧都會保持在後臺。此時,系統很有可能會開始對後臺的activity進行銷燬,目的是為了回收記憶體資源。而這會直接導致activity狀態的丟失。

由於回收棧中的activity不會被重新排序,所以如果一個activity已經被打開了,(也就是說它的例項已經存在於回收棧中了,注意,回收棧和任務棧中存放的都是activity的例項哦)那麼如果需要再次開啟這個activity,已經存在於回收棧中的例項是不會被挪動的。系統會再建立一個該activity的例項並將它置於棧頂。舉個例子,使用者打開了A, B, C三個activity,順序是A-B-C。也就是說,activityC位於棧頂,activityA位於棧底。如果使用者想要再次開啟activityB,會有一個新的B的例項被創建出來並置於棧頂,原來的那個activityB的例項還在回收棧裡原地不動,此時,回收棧中是這個樣子的:(棧底)A-B-C-B(棧頂)。由此可見,應用程式中的activity不僅有可能被例項化多次,甚至可以在不同的任務棧中被例項化多次。如圖所示


所以,如果使用者點選後退按鈕返回的話,回收棧中的每個activity例項都會按照它們被開啟的順序的逆序展現,而且它們都保持著自己的UI狀態。當然,我們也可以讓某個activity不被多次例項化。具體方法會在後面的【任務棧的管理】章節中進行討論。

下面我們來總結一下activity和任務棧的預設行為準則:

*   當ActivityA開啟了ActivityB,A就被停止了。但是系統會保留與它相關的狀態以及資料(比如螢幕滾動的位置,以及表單中輸入的文字)。如果使用者在B處於活動狀態時點選了後退按鈕,那麼A就會重新回到前臺。而且A再次呈現在使用者眼前時的狀態,與剛剛被停止時的狀態是一樣的。

*   當用戶按下Home按鈕,當前activity就會被停止。同時,它所在的任務棧也被整體移到後臺。而該任務棧中的每一個activity的狀態和資料都會被系統完好的儲存下來。這樣,如果過了一會兒使用者再次點選該應用程式的圖示,這個被整體移到後臺的任務棧就會整體回到前臺,且位於棧頂的那個activity會被重新顯示在螢幕上。

*   如果使用者按下了返回鍵,那麼當前的activity就會被系統從回收棧的棧頂拽走並銷燬。那麼原本位於次頂層的那個activity就變成了棧頂,這就意味著它被顯示在螢幕上了。如果一個activity被系統銷燬了,系統也就不會再保留它的狀態和資料了。

*   Activity可以被多次例項化。也就是說,它既可以在同一個回收棧中被例項化多次,也可以在不同的回收棧中多次例項化。


Activity狀態資料的儲存

--------------------------------------------------------------------------------------------------------------------

正如前面介紹的那樣:activity被停止時的狀態以及資料都會被系統自動保留。正是因為這樣,當用戶點選後退按鈕回到之前操作過的activity頁面時,該activity還保持著使用者離開時的樣子。然而,我們還是應該主動使用回撥方法來保留activity的狀態資訊,尤其是在activity被銷燬或者需要被重新建立(recreate)的情況下——當系統停止某個activity時(比如一個新的Activity開啟了或任務棧移到了後臺),如果系統需要回收記憶體資源,那麼它就有可能完全銷燬這個activity。那麼,該activity的狀態資料也就隨之消失了。雖然activity本身被銷燬了,但它在回收棧中所佔的位置仍然保留著。但是當這個被銷燬的activity所在的位置回到棧頂時,它必須要被重建(是recreate,不是resume哦!)為了避免丟失使用者的操作資料,我們必須在activity的類檔案中通過實現onSaveInstanceState()回撥方法來預先儲存這個activity的狀態資料。


任務棧的管理

--------------------------------------------------------------------------------------------------------------------

前面已經說過,安卓管理任務棧和回收棧的方式是將activity按照其被開啟的先後順序放在同一個任務棧裡,同時也將這些activity放在回收棧裡。回收棧的存取模式為“後進先出”,這種存取模式很好地支撐著絕大多數應用程式的activity的執行。而且我們既不用操心activity是如何與任務棧相關聯的,也不用管它們是如何在回收棧中存在的。儘管如此,任務棧和回收棧的這種預設行為還是可以人為改變的。比如,我們編寫的應用程式中會包含若干個activity,其中某個activity在被開啟時需要放在一個新開啟的任務棧中(不可以被放入原有的任務棧);再比如,當開啟某個activity時,我們可以不讓它的新例項在棧頂被創建出來,而是使用該activity的已經存在的例項;再再比如,當用戶離開任務棧的時候,我們可以讓回收棧中只有根activity(任務棧開啟時放進來的第一個activity),而不放置任何其他的activity。想要做到這些甚至更多,有兩種方法:1-對manifest檔案中<activity>元素進行配置;2-向startActivity()方法中傳遞帶有特定flag的intent。

1-可以供我們使用的<activity>屬性主要有:

taskAffinity
launchMode
allowTaskReparenting
clearTaskOnLaunch
alwaysRetainTaskState
finishOnTaskLaunch

2-可以使用的intent flag主要有:

FLAG_ACTIVITY_NEW_TASK
FLAG_ACTIVITY_CLEAR_TOP
FLAG_ACTIVITY_SINGLE_TOP

【提示】一般情況下,不應該改變應用程式中的activity和任務棧的預設行為模式。但如果真的有必要修改,那麼一定要給予提示並對activity在啟動過程中的可用性做測試,另外還要保證通過返回鍵可以從其他activity和任務棧能夠返回到這個被改變了預設行為模式的activity。後退按鈕的導向軌跡可能會與使用者期望的軌跡產生衝突,一定要做好測試工作。


Defining launch modes


Handling affinities


Clearing the back stack


Starting a task

本文總結

所有activity都屬於任務棧;任務棧中包含著一系列activity,這些activity在任務棧中的排列順序取決於使用者開啟它們的先後順序——最先開啟的位於棧底,後開啟的依次向上羅列;任務棧可以移到後臺併為其中的每一個activity保持其停止時的狀態,目的是為了讓使用者在操作其他任務棧的同時不丟失他們曾經操作過的activity

相關推薦

四大元件 Activity 任務回收Tasks and Back Stack

本文摘要 |---Saving Activity State   |---Managing Tasks     |---Defining launch modes     |---Handling affinities     |---Clearing the back s

Android官方文件—APP元件ActivitiesTasks and Back Stack

任務和回退堆疊 應用程式通常包含多個Activity。每個Activity都應圍繞使用者可以執行的特定操作進行設計,並可以啟動其他Activity。例如,電子郵件應用程式可能有一個Activity來顯示新訊息列表。當用戶選擇Activity時,將開啟一個新Activity以

四大元件Activity學習

在安卓中各大控制元件都要依附Activity來完成與使用者的互動,Activity(活動)作為控制元件的平臺。介面的實現都要用到Activity,簡單的說Activity就是安卓的UI部分。 Activity的生命週期 一個Activity的建立與銷燬要經歷一下幾個方法: onCreat

四大元件學習Broadcast

Broadcast(廣播) 安卓中,廣播是一種常用與應用程式之間進行訊息傳遞的方式。首先講需要傳遞的資訊和用於過濾的資訊裝入(Action 、Category)並通過SendBroadcast()、sendOrderBroadcast()或sendStickyBroadcast()方法,把 In

四大元件學習Service

Service 按官方原文件所說:Service是一個沒有介面在後臺執行耗時操作的應用元件。其他元件能夠啟動Service,並且當用戶切換到相應的應用場景,Service一樣能在後臺持續執行。另外一個元件還能繫結到Service與之互動(IPC通訊),所有這些活動都是在後臺進行的,Service

四大元件bindService使用

bindServer使用場景 1、在同個app之間呼叫(即是同一個程序中) 2、在不同app之間呼叫(即是跨程序間通訊) 同個app間呼叫(只有一次啟動該服務) BinderActicityA public class BinderActicityA exten

深入剖析Android四大元件(九)——ActivityAppCompatActivity與toolbar的結合

對於技術類的部落格,我們永遠追尋最新API腳步,在API22之前我們使用標題欄基本都是在ActionBarActivity的Activity中處理的,而API22之後,谷歌遺棄了ActionBarActivity,推薦我們也可以說是強制我們使用AppCompatActivit

Android學習--四大元件

 Android有四大元件,分別是Activity,Service,Content Provider和Broadcast Receiver。   首先說明Activity元件,開發一個應用程式不用到Activity是很難的,一個Activity通常就是一個獨立的視窗或

Android 四大元件

Android開發的四大元件  Android四大元件分別為activity、service、content provider、broadcast receive  一、Activity    Activity生命週期的方法是成對出現的 onCreate(

四大元件之一ContentProvider內容提供者

前提:這裡我們需要準備好資料庫,及資料的建立。可以使用Naviact,當然也可以用安卓程式碼。這裡我們用安卓程式碼來實現。 1.建立一個專案,名為contentprovider . 在xml檔案裡寫兩個輸入框,一個按鈕,用來新增資料。如下圖: ------------

手機拍照、裁剪、及相簿選擇圖片裁剪適配7.0

網上介紹安卓7.0呼叫系統拍照的部落格有很多,但感覺都不是很清晰,遂決定自己來寫。 Demo要實現的功能: 1.支援拍照並且可以對圖片進行裁剪 2.支援從相簿中選擇圖片並進行裁剪 3.無論是拍照的照片還是從相簿中選擇的照片(都是裁剪後的)統一儲存在同一個目錄下 問題

Tasks and Back Stack(任務返回)

以下是講解activity的任務和返回棧,從android開發文件中翻譯而來。 一個app通常都包含多個activities,每個activity 的設計都是基於使用者可以執行特定行為,使用者也可以開啟其他activities。比如一個email app

四大控制元件BroadcastReceiver詳解

BroadcastReceiver詳解 廣播的概念 Android:系統在產生某個事件時傳送廣播,應用程式使用廣播接收者接收這個廣播,就知道系統產生了什麼事件。 Android系統在執行的過程中,會產生很多事件,比如開機、電量改變、收發簡訊、撥打電話、螢

控制元件單選按鈕 (RadioButtonRadioGroup)

概述: RadioButton是單選按鈕,可提供若干選項方便使用者進行選擇操作,且在一組選項中只能選擇一個。 RadioGroup繼承自ViewGroup和RadioButton結合使用,將若干RadioButton選項組合為一組。 屬性和方法: RadioButton

控制元件按鈕Button

概述: android按鈕可包含文字、圖片和圖片及文字,分為Button和ImagetButton兩個控制元件。 Button是TextView的直接子類,主要響應使用者的單擊操作,如常見的“確定”、“登入”、“註冊”按鈕等 屬性和方法: XML屬性 方法 備註

-深入淺出MVVM教程》應用篇

getter apk 更新 processor 技術 tac bind end mat 背景 這幾年 MVP 架構在安卓屆非常流行,幾乎已經成為主流框架,它讓業務邏輯 和 UI操作相對獨立,使得代碼結構更清晰。 MVVM 在前端火得一塌糊塗,而在安卓這邊卻基本沒見到幾個人在

投屏助手(B1358)輔助調試

技術 ash 命令 tro mob 16px lan log 遠程控制 Android遠程桌面助手的中文版——安卓投屏助手正式上線。安卓投屏和遠程控制的軟件其實已經非常多了,如Vysor、Total Control、Mobizen、Apo

中高階開發面試知識點——快取

前言 幾乎所有的專案都做了快取,但是快取做的怎麼樣,其實只有我們自己知道。快取做的好,沒有網路也能流暢的使用;再多的資料請求都不會出現卡頓延遲等待很久的情況。 程式中除了圖片快取(三級快取),還有資訊快取。當用戶無法聯網時,app會預設顯示快取的資料。 前言快取方式 SQLite 下載完資料檔案後,

開發自定義View跑馬燈:MarqueeView

*本篇文章已授權微信公眾號 guolin_blog(郭霖)獨家釋出 好久沒寫東西了,感覺有點虛度光陰了,也感覺有點生疏了,剛好最近專案裡面有個跑馬燈的需求,TextView一通設定之後還是出現各種衝突,尤其是當TextView與EditText共存的時

投屏助手(B1358)輔助除錯

      Android遠端桌面助手的中文版——安卓投屏助手正式上線。安卓投屏和遠端控制的軟體其實已經非常多了,如Vysor、Total Control、Mobizen、ApowerMirror、TeamViewer、向日葵遠端控制手機軟體等等,為啥還要再搞個安卓投屏助手出來?原因無它,稱手的工具可以提高工