《Android 開(kāi)發(fā)藝術(shù)探索》筆記1--Activity的生命周期和模式

Activity的生命周期和啟動(dòng)模式.png

Activity的生命周期

image
  • onCreate: 聲明周期的第一個(gè)方法.做一些初始化的動(dòng)作,例如setContentView
  • onRestart: 表示Activity重新啟動(dòng).當(dāng)界面從不可見(jiàn)變?yōu)榭梢?jiàn)時(shí)調(diào)用,場(chǎng)景Home鍵切換,從任務(wù)棧返回
  • onStart: 表示Activity正在被啟動(dòng).Activity為理論可見(jiàn)(取決上層界面是否透明),但不是前臺(tái)無(wú)法操作.
  • onResume: 表示Activity已經(jīng)可見(jiàn),并且為前臺(tái). 與onStart主要是前臺(tái)后臺(tái),有無(wú)焦點(diǎn)的區(qū)別
  • onPause: 表示Activity正在停止. 做一些存儲(chǔ)數(shù)據(jù),停止動(dòng)畫(huà)操作.不能太耗時(shí),這會(huì)影響新的Activity啟動(dòng),onPause()必須先執(zhí)行完,新Activity的onResume()才會(huì)執(zhí)行.
  • onStop: 表示Activity即將停止,可以做稍微重量級(jí)的回收工作,同樣不能太耗時(shí).
  • onDestroy: 表示Activity即將被銷毀. 可以做一些最終的資源釋放

場(chǎng)景 當(dāng)界面A打開(kāi)界面B時(shí)的生命周期調(diào)用順序: A的onPause()-> B的onCreate() -> B的onStart() -> B的onResume() -> A的onStop().

  • onStart()onStop() 是針對(duì)Activity是否可見(jiàn)的角度回調(diào)
  • onResume()onPause() 是針對(duì)Activity是否位于前臺(tái)的角度回調(diào)的

異常情況下的生命周期

當(dāng)系統(tǒng)配置發(fā)生改變的時(shí)候

Activity就會(huì)因?yàn)楫惓G闆r被銷毀并重新創(chuàng)建.例如橫豎屏切換,語(yǔ)言切換等.

當(dāng)異常狀態(tài)發(fā)生的時(shí)候. 在界面銷毀前會(huì)調(diào)用onSaveInstanceState()進(jìn)行當(dāng)前界面的數(shù)據(jù)保存,如文本輸入的數(shù)據(jù),listView滾動(dòng)的位置等. 在重建后會(huì)調(diào)用onRestoreInstanceState()進(jìn)行因?yàn)楫惓V亟ǖ脑紨?shù)據(jù)的恢復(fù).

準(zhǔn)確的說(shuō)onSaveInstanceState() 會(huì)在onStop()之前執(zhí)行, 而onRestoreInstanceState會(huì)在onStart之后執(zhí)行.

當(dāng)屏幕發(fā)生旋轉(zhuǎn)時(shí),聲明周期調(diào)用過(guò)程如下:

image

這里我們要清楚,當(dāng)發(fā)生了異常情況下,系統(tǒng)會(huì)幫我們自動(dòng)恢復(fù)大部分的數(shù)據(jù),但是如果我們想要自己從異常中恢復(fù).那么我們可以通過(guò)onCreate()onRestoreInstanceState()中的參數(shù)Bundle來(lái)進(jìn)行值得保存.

  • 當(dāng)正常情況下onCreate中的Bundle類型參數(shù)是為null的. 而onRestoreInstanceState是不會(huì)被調(diào)用的.
  • 當(dāng)異常發(fā)生后的重建,onCreate,onRestoreInstanceState都會(huì)被觸發(fā),并且其中的Bundle類型參數(shù)都不為null

資源內(nèi)存不足時(shí)低優(yōu)先級(jí)的Activity被殺死

這種場(chǎng)景不好模擬,但是在存儲(chǔ)和恢復(fù)的過(guò)程是與上面的過(guò)程一致的.

關(guān)于Activity的優(yōu)先級(jí)的高低

  • 前臺(tái)Activity–正在和用戶交互的Activity,優(yōu)先級(jí)最高
  • 可見(jiàn)但是非前臺(tái)Activity–比如Activity中彈出了一個(gè)對(duì)話框,導(dǎo)致Activity可見(jiàn)但是無(wú)法操作.
  • 后臺(tái)Activity–已經(jīng)被暫停的Activity,比如執(zhí)行了onStop,優(yōu)先級(jí)最低.

禁止異常重建Activity

如果不想Activity重建.可以通過(guò)清單文件中對(duì)activity標(biāo)簽進(jìn)行配置.

android:configChanges="orientation|screenSize"

當(dāng)給一個(gè)Activity聲明了上述的屬性之后,當(dāng)手機(jī)旋轉(zhuǎn)的時(shí)候,activity不會(huì)重建,也就沒(méi)有任何聲明周期方法的回調(diào), 但是會(huì)調(diào)用onConfigurationChanged()方法.

  • orientation: 屏幕方向發(fā)生了改變,手機(jī)旋轉(zhuǎn)
  • screenSize: 屏幕的尺寸信息發(fā)生改變,此屬性和編譯版本有關(guān)系,當(dāng)minSdkVersion和targetSdkVersion均低于13,此選項(xiàng)不會(huì)導(dǎo)致界面重啟.如果高于那么會(huì)重啟.

所以這兩個(gè)最好成對(duì)出現(xiàn).

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

Activity的啟動(dòng)模式

當(dāng)我們打開(kāi)的activity會(huì)被系統(tǒng)以任務(wù)棧的形式來(lái)存儲(chǔ)起來(lái).后進(jìn)先出.當(dāng)每一個(gè)任務(wù)棧為空的時(shí)候這個(gè)棧就會(huì)被回收

  • standard: 默認(rèn)模式,就是新進(jìn)入的壓在已存在的界面之上.
  • singleTop: 棧頂復(fù)用模式.
  • singleTask: 棧內(nèi)復(fù)用模式
  • singleInstance: 棧內(nèi)單例模式

singleTop

如果新Activity已經(jīng)位于任務(wù)棧的棧頂,那么此Activity不會(huì)被重新創(chuàng)建,同時(shí)onNewIntent()會(huì)被回調(diào).

如果使用此模式,那么在任務(wù)棧中棧頂?shù)綏5蜑镃BA的情況下,再次打開(kāi)C,那么C界面的onCreate()onStart()不會(huì)被調(diào)用,真正的調(diào)用時(shí)onPause()–>onNewIntent()–>onResume()

singleTask

如果用棧內(nèi)復(fù)用,當(dāng)打開(kāi)C時(shí)候,會(huì)查詢所有的任務(wù)棧,如果有任務(wù)棧包含C,那么把這個(gè)任務(wù)棧移動(dòng)到所有棧的首位,并清除掉這個(gè)棧內(nèi)C到棧頂?shù)钠渌鸄ctivity,最后調(diào)用C的onNewIntent()方法. 如果沒(méi)有那就直接在所需任務(wù)棧的棧頂創(chuàng)建C的實(shí)例.

這里由于singleTask默認(rèn)具有clearTop的效果,所以會(huì)清除C以上activity的出棧. 這里和具體的啟動(dòng)模式有關(guān).

所需任務(wù)棧: 和一個(gè)參數(shù)有關(guān)系,TaskAffinity.這個(gè)參數(shù)標(biāo)識(shí)了一個(gè)Activity所需要的任務(wù)棧的名字,默認(rèn)情況下所有的Activity的所需的任務(wù)棧的名字都為應(yīng)用包名.

這里我們可以通過(guò)命令adb shell dumpsys activity測(cè)試一下,打開(kāi)順序?yàn)?strong>MainActivity–>ModeSingleTopArc

當(dāng)我們不指定taskAffinity的所需棧的時(shí)候,查看任務(wù)棧的結(jié)果為:

image.png

如果指定了taskAffinity 這個(gè)時(shí)候任務(wù)棧的狀況為:


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

          android:launchMode="singleTask"

          android:taskAffinity=".nishibendan"/>

image

一般情況下TaskAffinity屬性一般和singleTask啟動(dòng)模式或者allowTaskReparenting屬性配對(duì)使用,其他情況下使用沒(méi)有意義. 另外任務(wù)棧分為前臺(tái)任務(wù)棧和后臺(tái)任務(wù)棧,后臺(tái)任務(wù)棧中的activity屬于暫停狀態(tài),用戶可以切換將后臺(tái)調(diào)到前臺(tái).

singleInstance

這個(gè)模式是加強(qiáng)版的singleTask,除了singleTask具有的屬性之外,還具有創(chuàng)建新棧的能力,這個(gè)棧只有這一個(gè)實(shí)例. 就是說(shuō)如果假設(shè)EActivity沒(méi)有被創(chuàng)建過(guò),那么創(chuàng)建時(shí),首先會(huì)創(chuàng)建一個(gè)新的任務(wù)棧,然后創(chuàng)建實(shí)例放入這個(gè)新的棧內(nèi),然后下一個(gè)實(shí)例不會(huì)和這個(gè)EActivity所屬棧共存,會(huì)創(chuàng)建一個(gè)新的棧繼續(xù)存放.

給Activity添加啟動(dòng)模式

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

優(yōu)先級(jí) 代碼設(shè)置優(yōu)先于xml布局中的設(shè)置.

限定范圍不同

  • xml無(wú)法直接為其設(shè)置FLAG_ACTIVITY_CLEAR_TOP.
  • 代碼添加flag無(wú)法添加singleInstance模式.

注意: 如果通過(guò)代碼添加添加Intent.FLAG_ACTIVITY_NEW_TASK和xml中設(shè)置singleTask是不一樣的.代碼動(dòng)態(tài)添加是沒(méi)有clean_top的效果,看圖:

image.png

Activity的Flags

  • 影響啟動(dòng)模式的標(biāo)識(shí)位: 可以設(shè)定Activity的啟動(dòng)模式

  • 影響運(yùn)行時(shí)的標(biāo)識(shí)位: 可以影響運(yùn)行的Activity的運(yùn)行狀態(tài)

  • FLAG_ACTIVITY_NEW_TASK相對(duì)于xml中的singleTask,但是動(dòng)態(tài)的時(shí)候需要給intent添加兩個(gè)標(biāo)志位,否則無(wú)法達(dá)到效果,如下

intent3.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent3.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
  • FLAG_ACTIVITY_SINGLE_TOP相對(duì)于xml中的singleTop
  • FLAG_ACTIVITY_CLEAR_TOP在同一個(gè)任務(wù)棧中所有位于它上面的Activity都要出棧,此標(biāo)志一般和singleTask一起出現(xiàn). 如果啟動(dòng)模式為standard它連同上面的都要出棧,之后系統(tǒng)會(huì)創(chuàng)建一個(gè)新的實(shí)例放入棧頂.

IntentFilter匹配規(guī)則

  • 顯示調(diào)用: 需要指定組件信息如包名,類名.
  • 隱式調(diào)用: 不需要指定組件信息, Intent需要匹配目標(biāo)組件IntentFilter中所設(shè)置的過(guò)濾信息即可.

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

關(guān)于每一項(xiàng)的具體匹配規(guī)則

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

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

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

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

最簡(jiǎn)單的隱式打開(kāi)

//代碼中

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>

一個(gè)比較完整的匹配代碼

//代碼中

  Intent intent = new Intent();

  intent.setAction("com.test1");

  intent.addCategory("com.category1");

//intent.setDataAndType(Uri.parse("content://"),"audio/plain");//如果過(guò)濾規(guī)則中沒(méi)有聲明URI的屬性,那么會(huì)有默認(rèn)值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>

在進(jìn)行data屬性匹配的時(shí)候盡量使用setDataAndType, 因?yàn)樵创a中setDatasetType會(huì)把彼此屬性置為null.

Intent-filter匹配規(guī)則對(duì)于Service和BroadcastReceiver也是同樣. 不過(guò)建議Service的使用盡量使用顯示調(diào)用服務(wù).

判斷是否有匹配的Intent

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

使用演示:

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

參數(shù)二使用PackageManager.MATCH_DEFAULT_ONLY,意義在僅僅匹配哪些在intent-filter中聲明<category android:name="android.intent.category.DEFAULT"/>,只要使用這個(gè)標(biāo)記不返回null,那么startActivity就一定可以打開(kāi). 如果不用這個(gè)標(biāo)記就會(huì)把沒(méi)有設(shè)置default的匹配出來(lái).從而導(dǎo)致判斷失敗.因?yàn)椴缓蠨EFAULT這個(gè)category的Activity是無(wú)法接收隱式Intent的

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

參看文章

《Android 開(kāi)發(fā)藝術(shù)探索》書(shū)集
《Android 開(kāi)發(fā)藝術(shù)探索》 01-Activity的生命周期和模式
https://github.com/feiwodev/AndroidDevelopmentArt

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容