《Android 開發藝術探索》筆記1--Activity的生命周期和模式

Activity的生命周期和啟動模式.png

Activity的生命周期

image
  • onCreate: 聲明周期的第一個方法.做一些初始化的動作,例如setContentView
  • onRestart: 表示Activity重新啟動.當界面從不可見變為可見時調用,場景Home鍵切換,從任務棧返回
  • onStart: 表示Activity正在被啟動.Activity為理論可見(取決上層界面是否透明),但不是前臺無法操作.
  • onResume: 表示Activity已經可見,并且為前臺. 與onStart主要是前臺后臺,有無焦點的區別
  • onPause: 表示Activity正在停止. 做一些存儲數據,停止動畫操作.不能太耗時,這會影響新的Activity啟動,onPause()必須先執行完,新Activity的onResume()才會執行.
  • onStop: 表示Activity即將停止,可以做稍微重量級的回收工作,同樣不能太耗時.
  • onDestroy: 表示Activity即將被銷毀. 可以做一些最終的資源釋放

場景 當界面A打開界面B時的生命周期調用順序: A的onPause()-> B的onCreate() -> B的onStart() -> B的onResume() -> A的onStop().

  • onStart()onStop() 是針對Activity是否可見的角度回調
  • onResume()onPause() 是針對Activity是否位于前臺的角度回調的

異常情況下的生命周期

當系統配置發生改變的時候

Activity就會因為異常情況被銷毀并重新創建.例如橫豎屏切換,語言切換等.

當異常狀態發生的時候. 在界面銷毀前會調用onSaveInstanceState()進行當前界面的數據保存,如文本輸入的數據,listView滾動的位置等. 在重建后會調用onRestoreInstanceState()進行因為異常重建的原始數據的恢復.

準確的說onSaveInstanceState() 會在onStop()之前執行, 而onRestoreInstanceState會在onStart之后執行.

當屏幕發生旋轉時,聲明周期調用過程如下:

image

這里我們要清楚,當發生了異常情況下,系統會幫我們自動恢復大部分的數據,但是如果我們想要自己從異常中恢復.那么我們可以通過onCreate()onRestoreInstanceState()中的參數Bundle來進行值得保存.

  • 當正常情況下onCreate中的Bundle類型參數是為null的. 而onRestoreInstanceState是不會被調用的.
  • 當異常發生后的重建,onCreate,onRestoreInstanceState都會被觸發,并且其中的Bundle類型參數都不為null

資源內存不足時低優先級的Activity被殺死

這種場景不好模擬,但是在存儲和恢復的過程是與上面的過程一致的.

關于Activity的優先級的高低

  • 前臺Activity–正在和用戶交互的Activity,優先級最高
  • 可見但是非前臺Activity–比如Activity中彈出了一個對話框,導致Activity可見但是無法操作.
  • 后臺Activity–已經被暫停的Activity,比如執行了onStop,優先級最低.

禁止異常重建Activity

如果不想Activity重建.可以通過清單文件中對activity標簽進行配置.

android:configChanges="orientation|screenSize"

當給一個Activity聲明了上述的屬性之后,當手機旋轉的時候,activity不會重建,也就沒有任何聲明周期方法的回調, 但是會調用onConfigurationChanged()方法.

  • orientation: 屏幕方向發生了改變,手機旋轉
  • screenSize: 屏幕的尺寸信息發生改變,此屬性和編譯版本有關系,當minSdkVersion和targetSdkVersion均低于13,此選項不會導致界面重啟.如果高于那么會重啟.

所以這兩個最好成對出現.

日常開發中我們比較常用的local,orientation,keyboardHidden,uiMode. local為本地語言切換. uiMode界面模式發生切換,如夜間模式(API8中增加)

Activity的啟動模式

當我們打開的activity會被系統以任務棧的形式來存儲起來.后進先出.當每一個任務棧為空的時候這個棧就會被回收

  • standard: 默認模式,就是新進入的壓在已存在的界面之上.
  • singleTop: 棧頂復用模式.
  • singleTask: 棧內復用模式
  • singleInstance: 棧內單例模式

singleTop

如果新Activity已經位于任務棧的棧頂,那么此Activity不會被重新創建,同時onNewIntent()會被回調.

如果使用此模式,那么在任務棧中棧頂到棧低為CBA的情況下,再次打開C,那么C界面的onCreate()onStart()不會被調用,真正的調用時onPause()–>onNewIntent()–>onResume()

singleTask

如果用棧內復用,當打開C時候,會查詢所有的任務棧,如果有任務棧包含C,那么把這個任務棧移動到所有棧的首位,并清除掉這個棧內C到棧頂的其他Activity,最后調用C的onNewIntent()方法. 如果沒有那就直接在所需任務棧的棧頂創建C的實例.

這里由于singleTask默認具有clearTop的效果,所以會清除C以上activity的出棧. 這里和具體的啟動模式有關.

所需任務棧: 和一個參數有關系,TaskAffinity.這個參數標識了一個Activity所需要的任務棧的名字,默認情況下所有的Activity的所需的任務棧的名字都為應用包名.

這里我們可以通過命令adb shell dumpsys activity測試一下,打開順序為MainActivity–>ModeSingleTopArc

當我們不指定taskAffinity的所需棧的時候,查看任務棧的結果為:

image.png

如果指定了taskAffinity 這個時候任務棧的狀況為:


<activity android:name=".launchmode.ModeSingleTaskAct"

          android:launchMode="singleTask"

          android:taskAffinity=".nishibendan"/>

image

一般情況下TaskAffinity屬性一般和singleTask啟動模式或者allowTaskReparenting屬性配對使用,其他情況下使用沒有意義. 另外任務棧分為前臺任務棧和后臺任務棧,后臺任務棧中的activity屬于暫停狀態,用戶可以切換將后臺調到前臺.

singleInstance

這個模式是加強版的singleTask,除了singleTask具有的屬性之外,還具有創建新棧的能力,這個棧只有這一個實例. 就是說如果假設EActivity沒有被創建過,那么創建時,首先會創建一個新的任務棧,然后創建實例放入這個新的棧內,然后下一個實例不會和這個EActivity所屬棧共存,會創建一個新的棧繼續存放.

給Activity添加啟動模式

  1. 通過清單文件中activity標簽添加android:launchMode="singleTask|singleTop|singleInstance"
  2. 通過代碼中startActivity(Intent)中的intent通過addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)

優先級 代碼設置優先于xml布局中的設置.

限定范圍不同

  • xml無法直接為其設置FLAG_ACTIVITY_CLEAR_TOP.
  • 代碼添加flag無法添加singleInstance模式.

注意: 如果通過代碼添加添加Intent.FLAG_ACTIVITY_NEW_TASK和xml中設置singleTask是不一樣的.代碼動態添加是沒有clean_top的效果,看圖:

image.png

Activity的Flags

  • 影響啟動模式的標識位: 可以設定Activity的啟動模式

  • 影響運行時的標識位: 可以影響運行的Activity的運行狀態

  • FLAG_ACTIVITY_NEW_TASK相對于xml中的singleTask,但是動態的時候需要給intent添加兩個標志位,否則無法達到效果,如下

intent3.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent3.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
  • FLAG_ACTIVITY_SINGLE_TOP相對于xml中的singleTop
  • FLAG_ACTIVITY_CLEAR_TOP在同一個任務棧中所有位于它上面的Activity都要出棧,此標志一般和singleTask一起出現. 如果啟動模式為standard它連同上面的都要出棧,之后系統會創建一個新的實例放入棧頂.

IntentFilter匹配規則

  • 顯示調用: 需要指定組件信息如包名,類名.
  • 隱式調用: 不需要指定組件信息, Intent需要匹配目標組件IntentFilter中所設置的過濾信息即可.

IntentFilter的過濾信息有action,category,data. 一個Activity可以有多個Intentfilter,只要intent能匹配任意一組intent-filter即可成功啟動.

關于每一項的具體匹配規則

  • action: 本身是字符串. Intent匹配中存在action且必須和過濾規則中的其中一個action相同,那么action就算匹配ok. xml中必須有一個action聲明.
  • category: 本身是字符串.Intent匹配中intent可以不存在category,但是如果添加了category那么必須要和定義的intent-filter中的category一致,否則失敗. 如果我們在Intent不添加的時候,那么系統會自動為我們添加一個預定義的屬性android.intent.category.DEFAULT. xml中需添加這個默認的category屬性.
  • data: xml過濾規則中可以不聲明,如果聲明只要匹配了一個就可以.一般情況下data有兩部分組成mimeType和URI. mimeType指媒體類型,URI規則如下:

<scheme>://<host>:<port>/[path]

如下實例: scheme:表示URI的模式,例如http, file, content.

http://www.baidu.com:80/search/info

最簡單的隱式打開

//代碼中

Intent intent = new Intent();

intent.setAction("com.test1");

startActivity(intent);

//xml中

<activity android:name=".intentfilter.FilterAct">

  <intent-filter>

      <action android:name="com.test1"/>

      <category android:name="android.intent.category.DEFAULT"/>

  </intent-filter>

</activity>

</pre>

一個比較完整的匹配代碼

//代碼中

  Intent intent = new Intent();

  intent.setAction("com.test1");

  intent.addCategory("com.category1");

//intent.setDataAndType(Uri.parse("content://"),"audio/plain");//如果過濾規則中沒有聲明URI的屬性,那么會有默認值content和file的屬性

  intent.setDataAndType(Uri.parse("http://"),"audio/plain");

  startActivity(intent);

//xml中

<activity android:name=".intentfilter.FilterAct">

  <intent-filter>

      <action android:name="com.test1"/>

      <action android:name="com.test2"/>

      <category android:name="com.category1"/>

      <category android:name="com.category2"/>

      <category android:name="android.intent.category.DEFAULT"/>

      <data android:mimeType="text/plain"/>

      <data android:mimeType="audio/plain" android:scheme="http"/>

  </intent-filter>

</activity>

在進行data屬性匹配的時候盡量使用setDataAndType, 因為源碼中setDatasetType會把彼此屬性置為null.

Intent-filter匹配規則對于Service和BroadcastReceiver也是同樣. 不過建議Service的使用盡量使用顯示調用服務.

判斷是否有匹配的Intent

  • PackageManager的resolveActivity()方法
  • Intent的resolveActivity()方法

使用演示:

ResolveInfo resolveInfo = getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);

參數二使用PackageManager.MATCH_DEFAULT_ONLY,意義在僅僅匹配哪些在intent-filter中聲明<category android:name="android.intent.category.DEFAULT"/>,只要使用這個標記不返回null,那么startActivity就一定可以打開. 如果不用這個標記就會把沒有設置default的匹配出來.從而導致判斷失敗.因為不含有DEFAULT這個category的Activity是無法接收隱式Intent的

這樣如果返回的為null,那就是沒有匹配到,如果不為null那就是可以匹配.

參看文章

《Android 開發藝術探索》書集
《Android 開發藝術探索》 01-Activity的生命周期和模式
https://github.com/feiwodev/AndroidDevelopmentArt

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,461評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,538評論 3 417
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,423評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,761評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,207評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,419評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,959評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,678評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,978評論 2 374