## 1.1 Activity生命周期全面分析
### 1.1.1 典型情況下生命周期分析
(1)一般情況下,當當前Activity從不可見重新變為可見狀態時,`onRestart`方法就會被調用。
(2)當用戶打開新的Activity或者切換到桌面的時候,回調如下:`onPause`?->?`onStop`,但是如果新Activity采用了透明主題,那么`onStop`方法不會被回調。當用戶再次回到原來的Activity時,回調如下:`onRestart`?->?`onStart`?->?`onResume`。
(3)`onStart`和`onStop`對應,它們是從Activity是否可見這個角度來回調的;`onPause`和`onResume`方法對應,它們是從Activity是否位于前臺這個角度來回調的。
(4)從Activity A進入到Activity B,回調順序是`onPause(A) -> onCreate(B) -> onStart(B) -> onResume(B) -> onStop(A)`,所以不能在onPause方法中做重量級的操作。
### 1.1.2 異常情況下生命周期分析
(1)`onSaveInstanceState`方法只會出現在Activity被異常終止的情況下,它的調用時機是在`onStop`之前,它和`onPause`方法沒有既定的時序關系,可能在它之前,也可能在它之后。當Activity被重新創建的時候,`onRestoreInstanceState`會被回調,它的調用時機是`onStart`之后。
系統只會在Activity即將被銷毀并且有機會重新顯示的情況下才會去調用onSaveInstanceState方法。
當Activity在異常情況下需要重新創建時,系統會默認為我們保存當前Activity的視圖結構,并且在Activity重啟后為我們恢復這些數據,比如文本框中用戶輸入的數據、listview滾動的位置等,這些view相關的狀態系統都會默認為我們恢復。具體針對某一個view系統能為我們恢復哪些數據可以查看view的源碼中的onSaveInstanceState和onRestoreInstanceState方法。
(2)Activity按優先級的分類
前臺Activity;可見但非前臺Activity;后臺Activity
(3)`android:configChanges="xxx"`屬性,常用的主要有下面三個選項:
`local`:設備的本地位置發生了變化,一般指切換了系統語言;
`keyboardHidden`:鍵盤的可訪問性發生了變化,比如用戶調出了鍵盤;
`orientation`:屏幕方向發生了變化,比如旋轉了手機屏幕。
配置了`android:configChanges="xxx"`屬性之后,Activity就不會在對應變化發生時重新創建,而是調用Activity的`onConfigurationChanged`方法。
##1.2 Activity的啟動模式
### 1.2.1 啟動模式
(1)當任務棧中沒有任何Activity的時候,系統就會回收這個任務棧。
(2)從非Activity類型的Context(例如ApplicationContext、Service等)中以`standard`模式啟動新的Activity是不行的,因為這類context并沒有任務棧,所以需要為待啟動Activity指定`FLAG_ACTIVITY_NEW_TASK`標志位。
(3)任務棧分為前臺任務棧和后臺任務棧,后臺任務棧中的Activity位于暫停狀態,用戶可以通過切換將后臺任務棧再次調到前臺。
(4)參數`TaskAffinity`用來指定Activity所需要的任務棧,意為任務相關性。默認情況下,所有Activity所需的任務棧的名字為應用的包名。TaskAffinity屬性主要和`singleTask`啟動模式或者`allowTaskReparenting`屬性配對使用,在其他情況下沒有意義。
當TaskAffinity和singleTask啟動模式配對使用的時候,它是具有該模式的Activity的目前任務棧的名字,待啟動的Activity會運行在名字和TaskAffinity相同的任務棧中;
當TaskAffinity和allowTaskReparenting結合的時候,當一個應用A啟動了應用B的某個ActivityC后,如果ActivityC的allowTaskReparenting屬性設置為true的話,那么當應用B被啟動后,系統會發現Activity C所需的任務棧存在了,就將ActivityC從A的任務棧中轉移到B的任務棧中。
(5)singleTask模式的具體分析:當一個具有singleTask啟動模式的Activity請求啟動之后,系統首先會尋找是否存在A想要的任務棧,如果不存在,就重新創建一個任務棧,然后創建Activity的實例把它放到棧中;如果存在Activity所需的任務棧,這時候要看棧中是否有Activity實例存在,如果有,那么系統就會把該Activity實例調到棧頂,并調用它的onNewIntent方法(它之上的Activity會被迫出棧,所以singleTask模式具有FLAG_ACTIVITY_CLEAR_TOP效果);如果Activity實例不存在,那么就創建Activity實例并把它壓入棧中。
(6)設置啟動模式既可以使用xml屬性`android:launchMode`,也可以使用代碼`intent.addFlags()`。區別在于限定范圍不同,前者無法直接為Activity設置FLAG_ACTIVITY_CLEAR_TOP標識,而后者無法為Activity指定singleInstance模式。
### 1.2.2 Activity的Flags
`FLAG_ACTIVITY_NEW_TASK`,`FLAG_ACTIVITY_SINGLE_TOP`,`FLAG_ACTIVITY_CLEAR_TOP`
`FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS`:具有這個標記的Activity不會出現在歷史Activity列表中,當某些情況下我們不希望用戶通過歷史列表回到我們的Activity的時候這個標記比較有用,它等同于屬性設置`android:excludeFromRecents="true"`。
## 1.3 IntentFilter的匹配規則
(1)IntentFilter中的過濾信息有action、category、data,為了匹配過濾列表,需要同時匹配過濾列表中的action、category、data信息,否則匹配失敗。一個過濾列表中的action、category、data可以有多個,所有的action、category、data分別構成不同類別,同一類別的信息共同約束當前類別的匹配過程。只有一個Intent同時匹配action類別、category類別和data類別才算完全匹配,只有完全匹配才能成功啟動目標Activity。此外,一個Activity中可以有多個intent-filter,一個Intent只要能匹配任何一組intenf-filter即可成功啟動對應的Activity。
~~~
<intent-filter>
<action android:name="com.ryg.charpter_1.c" />
<action android:name="com.ryg.charpter_1.d" />
<category android:name="com.ryg.category.c" />
<category android:name="com.ryg.category.d" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
~~~
(2)action匹配規則
只要Intent中的action能夠和過濾規則中的任何一個action相同即可匹配成功,action匹配區分大小寫。
(3)category匹配規則
Intent中如果有category那么所有的category都必須和過濾規則中的其中一個category相同,如果沒有category的話那么就是默認的category,即`android.intent.category.DEFAULT`,所以為了Activity能夠接收隱式調用,配置多個category的時候必須加上默認的category。
(4)data匹配規則
data的結構很復雜,語法大致如下:
~~~
<data android:scheme="string"
android:host="string"
android:port="string"
android:path="string"
android:pathPattern="string"
android:pathPrefix="string"
android:mimeType="string" />
~~~
主要由`mimeType`和`URI`組成,其中mimeType代表媒體類型,而URI的結構也復雜,大致如下:
`<scheme>://<host>:<port>/[<path>]|[<pathPrefix>]|[pathPattern]`
例如`content://com.example.project:200/folder/subfolder/etc`
`scheme、host、port`分別表示URI的模式、主機名和端口號,其中如果scheme或者host未指定那么URI就無效。
`path、pathPattern、pathPrefix`都是表示路徑信息,其中path表示完整的路徑信息,pathPrefix表示路徑的前綴信息;pathPattern表示完整的路徑,但是它里面包含了通配符(*)。
data匹配規則:Intent中必須含有data數據,并且data數據能夠完全匹配過濾規則中的某一個data。
URI有默認的scheme!
如果過濾規則中的mimeType指定為`image/*`或者`text/*`等這種類型的話,那么即使過濾規則中沒有指定URI,URI有默認的scheme是content和file!如果過濾規則中指定了scheme的話那就不是默認的scheme了。
~~~
//URI有默認值
<intent-filter>
<data android:mimeType="image/*"/>
...
</intent-filter>
//URI默認值被覆蓋
<intent-filter>
<data android:mimeType="image/*" android:scheme="http" .../>
...
</intent-filter>
~~~
如果要為Intent指定完整的data,必須要調用`setDataAndType`方法!
不能先調用setData然后調用setType,因為這兩個方法會彼此清除對方的值。
~~~
intent.setDataAndType(Uri.parse("file://abc"), "image/png");
~~~
data的下面兩種寫法作用是一樣的:
~~~
<intent-filter>
<data android:scheme="file" android:host="www.github.com"/>
</intent-filter>
<intent-filter>
<data android:scheme="file"/>
<data android:host="www.github.com"/>
</intent-filter>
~~~
如何判斷是否有Activity能夠匹配我們的隱式Intent?
(1)`PackageManager`的`resolveActivity`方法或者Intent的`resolveActivity`方法:如果找不到就會返回null
(2)PackageManager的`queryIntentActivities`方法:它返回所有成功匹配的Activity信息
針對Service和BroadcastReceiver等組件,PackageManager同樣提供了類似的方法去獲取成功匹配的組件信息,例如`queryIntentServices`、`queryBroadcastReceivers`等方法
有一類action和category比較重要,它們在一起用來標明這是一個入口Activity,并且會出現在系統的應用列表中。
~~~
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
~~~
- 前言
- 讀書筆記(1)第1章 Activity的生命周期和啟動模式
- 讀書筆記(2)第2章 IPC機制
- 讀書筆記(3)第3章 View的事件體系
- 讀書筆記(4)第4章 View的工作原理
- 讀書筆記(5)第5章 理解RemoteViews
- 讀書筆記(6)第6章 Android的Drawable
- 讀書筆記(7)第7章 Android動畫深入分析
- 讀書筆記(8)第8章 理解Window和WindowManager
- 讀書筆記(9)第9章 四大組件的工作過程
- 讀書筆記(10)第10章 Android的消息機制
- 讀書筆記(11)第11章 Android的線程和線程池
- 讀書筆記(12)第12章 Bitmap的加載和Cache
- 讀書筆記(13)第13章 綜合技術