1. 程式人生 > >Android--四大元件之Activity(一)

Android--四大元件之Activity(一)

####1. Activity是什麼?
####2. 生命週期
######1). Activity跳轉
######2). 從後臺啟動
######3). 橫豎屏切換
####3. 啟動模式
######1). 任務棧
######2). laucherMode
######3). Intent的Flag
######4). startActivityForResult

#Activity是什麼?
Activity是Android四大元件之一,可以用於view的顯示,但其最主要的任務是承擔使用者和app之間的互動。
在MVC模式中,Activity主要充當著C(Controller控制層)的作用,同時也負責Dialog,Toast,PopupWindow等;而在MVP模式中,Activity僅充當V(View檢視)的角色。
#生命週期
說到Activity,不可避免會談到其生命週期,在一次正常的啟動到銷燬的過程中,Activity的生命週期包含6部分:

onCreate() --> onStart() --> onResume() --> onPause() --> onStop() --> onDestroy()

然後考慮到Activity進入後臺重新進入會呼叫onRestart(),所以其生命週期為:
Activity生命週期
上圖比較詳細的描述了正常情況下Activity的生命週期切換。那下面我們看看一些特定情況下生命週期變化。
####Activity跳轉
從Activity A跳到Activity B時,生命週期是怎樣的?
Activity A會有一個完整的啟動過程,onCreate() --> onStart() --> onResume(),而且會在Activity B的onCreate()執行前就觸發onPause(),而Activity A的onStop()會在Activity B執行完onResume()以後再執行。
此時兩個Activity的生命週期順序是(控制檯log):

05-19 22:42:28.543 7534-7534/com.itlao5.demo I/System.out: ActivityA onCreate()
05-19 22:42:28.544 7534-7534/com.itlao5.demo I/System.out: ActivityA onStart()
05-19 22:42:28.546 7534-7534/com.itlao5.demo I/System.out: ActivityA onResume()
05-19 22:42:51.360 7534-7534/com.itlao5.demo I/System.out: ActivityA onPause()
05-19 22:42:51.427 7534-7534/com.itlao5.demo I/System.out: ActivityB onCreate()
05-19 22:42:51.428 7534-7534/com.itlao5.demo I/System.out: ActivityB onStart()
05-19 22:42:51.430 7534-7534/com.itlao5.demo I/System.out: ActivityB onResume()
05-19 22:42:51.927 7534-7534/com.itlao5.demo I/System.out: ActivityA onStop()

當然,從Activity B返回Activity A時,生命週期的執行情況類似。

05-19 22:55:43.390 7534-7534/com.itlao5.demo I/System.out: ActivityB onPause()
05-19 22:55:43.411 7534-7534/com.itlao5.demo I/System.out: ActivityA onRestart()
05-19 22:55:43.413 7534-7534/com.itlao5.demo I/System.out: ActivityA onStart()
05-19 22:55:43.420 7534-7534/com.itlao5.demo I/System.out: ActivityA onResume()
05-19 22:55:43.910 7534-7534/com.itlao5.demo I/System.out: ActivityB onStop()
05-19 22:55:43.911 7534-7534/com.itlao5.demo I/System.out: ActivityB onDestroy()

####從後臺啟動
Activity處於前臺時,點選home鍵,然後重新進入Activity(Activity沒有銷燬),此時不會再執行onCreate,而是會執行onRestart() --> onStart() --> onResume():

05-19 22:43:51.588 7534-7534/com.itlao5.demo I/System.out: ActivityA onPause()
05-19 22:43:51.654 7534-7534/com.itlao5.demo I/System.out: ActivityA onStop()
05-19 22:45:50.567 7534-7534/com.itlao5.demo I/System.out: ActivityA onRestart()
05-19 22:45:50.585 7534-7534/com.itlao5.demo I/System.out: ActivityA onStart()
05-19 22:45:50.593 7534-7534/com.itlao5.demo I/System.out: ActivityA onResume()

而當在後臺時,Activity已經被銷燬了,則會重新進入onCreate()的建立過程,而不會執行onRestart()。
####橫豎屏切換
如果沒有鎖定Activity的橫豎屏,則系統會根據手機橫豎屏狀況來調整Activity的橫豎屏顯示,此時,會有一些生命週期的變化。
此時的橫豎屏切換會根據android:configChanges的設定,產生三種不同的結果:
1.當不設定android:configChanges時,橫豎屏切換生命週期是這樣的:

(Activity啟動) onCreate --> onStart --> onResume --> (切橫屏) onSaveInstanceState --> onPause --> onStop --> onDestroy --> onCreate --> onStart --> onRestoreInstanceState --> onResume --> (切豎屏) onSaveInstanceState --> onPause --> onStop -->
onDestroy --> onCreate --> onStart --> onRestoreInstanceState --> onResume --> onSaveInstanceState --> onPause --> onStop --> onDestroy --> onCreate --> onStart --> onRestoreInstanceState --> onResume

可以看到,當從橫屏切換為豎屏時,Activity會有一個銷燬到重新建立的過程,而從豎屏切換為橫屏時,Activity會銷燬及建立兩次。
2. 設定Activity的android:configChanges="orientation"時,切屏還是會重新呼叫各個生命週期,切橫、豎屏時只會執行一次。

(Activity啟動) onCreate --> onStart --> onResume --> (切橫屏) onSaveInstanceState --> onPause --> onStop --> onDestroy --> onCreate --> onStart --> onRestoreInstanceState --> onResume --> (切豎屏) onSaveInstanceState --> onPause --> onStop -->
onDestroy --> onCreate --> onStart --> onRestoreInstanceState --> onResume --> onConfigurationChanged

我們看到,雖然切換為豎屏時,生命週期只執行了一次,但是,在最後海執行了onConfigurationChanged。
3. 改成 android:configChanges="orientation|keyboardHidden"之後

(Activity啟動) onCreate --> onStart --> onResume --> (切橫屏) onConfigurationChanged --> (切豎屏) onConfigurationChanged --> onConfigurationChanged

此時,豎屏切換為橫屏,僅執行一次onConfigurationChanged,而橫屏切換為豎屏時,執行兩次onConfigurationChanged。
######需要注意的是:以上結果僅適用於targetSdkVersion<13時,而從Android 3.2(API 13)開始

  1. 以上1.2都會執行相同的操作,橫豎屏切換的生命週期完全一致,都是

(Activity啟動) onCreate --> onStart --> onResume --> (切橫屏) onConfigurationChanged --onSaveInstanceState --> onPause --> onStop --> onDestroy --> onCreate --> onStart --> onRestoreInstanceState --> onResume --> (切豎屏) onConfigurationChanged – > onSaveInstanceState --> onPause --> onStop --> onDestroy --> onCreate --> onStart --> onRestoreInstanceState --> onResume

2.在設定Activity的android:configChanges=“orientation|keyboardHidden"後,還是與1一樣會重新呼叫各個生命週期的。因為screen size也開始跟著裝置的橫豎切換而改變。因此,阻止程式在橫豎屏切換時重新載入Activity,除了設定"orientation”,還必須加上"ScreenSize",即android:configChanges=“orientation|keyboardHidden|ScreenSize”。此時的生命週期,無論橫豎屏切換都只執行onConfigurationChanged:

(Activity啟動) onCreate --> onStart --> onResume --> (切橫屏) onConfigurationChanged --> (切豎屏) onConfigurationChanged

#啟動模式
啟動模式也是談Activity時不可避免的,Activity有四種啟動模式:

standard、singleTop、singleTask、singleInstance

可以根據不同的需求,來使用對應的啟動模式,防止Activity的重複建立。
在談到啟動模式之前,我們先了解一下Activity的任務棧:
####任務棧
任務棧,是一種用來儲存Activity例項的容器,儲存形式為棧(先進後出),它包含兩個操作,壓棧和出棧,不能直接更改棧裡面已存在Activity的順序,只能通過壓棧和出棧來調整順序。
當啟動Application時,預設會建立一個棧,所有該應用新建立的Activity都預設儲存在該棧。當棧處於前臺時,即為前臺任務棧;而當我們按Home鍵或者切換到其他應用時,棧即為後臺任務棧。Android當前顯示的是前臺任務棧的Top Activity。
####launchMode
當然,僅僅依靠著一個先進後出的棧,很難滿足我們的需求,比如我們的棧存在Activity A B C D,我們需要進入Activity B,但是又不想重新建立,並且當我們點返回的時候希望回到Activity A,那麼我們就不能按照簡單的建立一個Activity,而是需要給Activity一些特權,這些特權就是通過設定啟動模式來實現。我們可以通過AndroidManifest檔案中的屬性andorid:launchMode或者通過Intent的flag來設定Activity的啟動模式。
上面瞭解了任務棧,接下來我們再來看看Activity的四種啟動模式:
#####standard
預設模式,不需配置。在這個模式下,每次都會預設建立一個新的Activity例項。因此,在這種模式下,在一個棧中可以有多個相同的例項,也允許多個相同Activity疊加。
應用場景:絕大多數Activity
示例:棧中有Activity a b c d, 啟動一個Activity c,則棧變為a b c d c

######需要注意的是:當跨程序呼叫預設模式的Activity時,在Android 5.0(API 21)以前,會將Activity增加在棧頂部,即兩個程序的Activity會加入同一個棧中;而在Android 5.0及以後版本中,會重新建立一個新的棧,把另一個程序的Activity加入到新棧中。
#####singleTop
棧頂複用模式,即當棧的頂部是需要開啟的Activity時,則不重新建立,此時會執行Activity的onNewIntent()方法。這樣就避免了棧頂部Activity例項的重複建立。
應用場景:從外部進入的時候(如從通知欄進入到應用的Activity),或者為了解決重複跳轉問題(設定singleTop也可作為一個解決按鈕快速點選導致重複開啟Activity的方案)
示例:棧中有Activity a b c d, 啟動一個Activity d,則棧仍然是 a b c d,而如果是啟動一個Activity c,則棧變為a b c d c
######注意:和standard一樣,跨程序呼叫時,Android 5.0以後才新建立棧,並將Activity加入其中。
#####singleTask
棧內複用模式,如果棧中已經存在該Activity的例項,那麼,當再次開啟該Activity時,會直接使用該例項,執行其onNewIntent()方法。與singleTop的區別是,singleTask只要棧記憶體在,不管是否為棧頂Activity,都會複用,而且會清除棧中處於該Activity例項之上的所有Activity。
應用場景:應用首頁(應用首頁設定singleTask,可以保證從首頁返回,可以順利推出應用)
示例:棧中有Activity a b c d, 其中b是singleTask,則當啟動一個Activity b時,棧變為是 a b。
######需要注意的是:多個棧之前跳轉時,當一個前臺棧跳轉到一個全是singleTask Activity的後臺棧時,會將後臺棧移入前臺棧中。比如:後臺棧有singleTask Activity1 – singleTask Activity2,前臺棧有Activity3 – Activity4,如果從Activity4啟動Activity2,則最終的結果是,整個棧變為前臺棧,Activity3 – Activity4 – singleTask Activity1 – singleTask Activity2
#####singleInstance
單例模式,系統中只會存在唯一的例項,只要存在該Activity例項,則無論哪個Activity啟動,都會使用該例項。
應用場景:系統手機來電頁,或者提供給第三方使用的輔助類應用的公用Activity
#####Intent的Flag
作為啟動模式的另一種設定方式,Intent提供了多種Flag,比如:
FLAG_ACTIVITY_NEW_TASK:開啟一個新的任務棧來存放啟動的Activity,一般在service中使用該Flag(因為service中並不存在Activity任務棧,所以需要新建立)。
FLAG_ACTIVITY_SINGLE_TOP:這個與singleTop一致。
FLAG_ACTIVITY_CLEAR_TOP:與singleTask效果相同。
FLAG_ACTIVITY_NO_HISTORY:設定該Flag,則當該Activity啟動其他Activity後,該Activity就消失了,不會保留在Activity棧中。
#####startActivityForResult
除了startActivity,Android還提供了startActivityForResult,用於開啟Activity後可以接收返回值。有時,我們會發現,在通過startActivityForResult啟動一個Activity後,onActivityResult中接收不到Activity的返回值。此時,也許是受你的Activity啟動模式影響。
在Android5.0以前,當Activity A開啟另一個Activity B時
1、如果Activity B是singleTask或者singleInstance,則無論A是什麼啟動模式,onActivityResult無法接收返回值;
2、如果Activity A是singleInstance,無論B是什麼樣的啟動模式,都無法接收到返回值。

以上問題在Android5.0以後得到了修復,無論A、B是何種啟動模式,都可以得到返回值。這是因為ActivityStackSupervisor類中的startActivityUncheckedLocked方法在5.0中進行了修改。在5.0之前,當啟動一個Activity時,系統將首先檢查Activity的launchMode,如果為A頁面設定為SingleInstance或者B頁面設定為singleTask或者singleInstance,則會在LaunchFlags中加入FLAG_ACTIVITY_NEW_TASK標誌,而如果含有FLAG_ACTIVITY_NEW_TASK標誌的話,onActivityResult將會立即接收到一個cancle的資訊,而5.0之後這個方法做了修改,修改之後即便啟動的頁面設定launchMode為singleTask或singleInstance,onActivityResult依舊可以正常工作,也就是說無論設定哪種啟動方式,StartActivityForResult和onActivityResult()這一組合都是有效的。所以如果你目前正好基於5.0做相關開發,不要忘了向下相容。

原文:簡書ThinkinLiu 部落格: IT老五

更多啟動模式相關的內容可以參考 https://www.jianshu.com/p/2a9fcf3c11e4

未完待續,Activity其他相關介紹後續文章再寫