一篇就夠了系列之Activity全解析
前言:
Activity作為Android四大元件之一Google官方文件,是Android開發中最基本最常用的東西,那麼,Activity的定義到底是什麼呢?
從下面幾個方面介紹下Activity:
- 生命週期
- 任務棧
- 啟動模式
- scheme跳轉協議
生命週期
這其實是一個老生常談的東西,更是每一個Android開發人員熟記於心的東西,直接上圖:
每個狀態的特點大致為:
- onCreate:初始化操作,不可見,不可觸控
- onStart:建立完成,可見,不可觸控
- onResume:執行狀態,可見,可觸控
- onPause:暫定狀態,可見,不可觸控
- onStop:停止狀態,不可見,不可觸控
- onDestory:銷燬狀態
下面五個迴圈狀態需要注意:
- onCreate->onStart->onResume->onPause->onStop->onDestory(Activity建立到銷燬)
- onResume->onPause->onResume此時Activity一直是可見狀態,比如此時彈出了一個Diglog,生命週期就是該流程
- onStart->onResume->onPause->onStop->onRestart->onStart Activity A被Activity B所覆蓋,然後B銷燬,A重新出現在螢幕上,此時就是該生命週期的流程,這個和將App直接按Home建退到後臺是一樣的流程
- onCreate->onStart->onResume->onPause->onStop->APP process killed->onCreate 這個流程主要是Activity在後臺,記憶體不足時被系統給回收銷燬,然後再重新打開了。
- 螢幕在進行旋轉後,生命週期是:銷燬->建立,同時,會增加兩個方法:onSaveInstanceState,onRestoreInstanceState,此時完整的生命週期是:onCreate->onStart->onResume->onPause->onSaveInstanceState->onStop->onDestory->onCreate->onStart->onRestoreInstanceState->onResume->Running狀態 ,利用那兩個方法,可以在銷燬之前進行資訊儲存,建立的時候再直接去除,比如程式碼:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i("wy","oncreate");
if(savedInstanceState!=null){
String str=savedInstanceState.getString("wy");
}
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
Log.i("wy","onRestoreInstanceState");
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("wy","wy");
Log.i("wy","onSaveInstanceState");
}
一般的視屏播放都是這樣進行螢幕旋轉狀態儲存的。
任務棧
定義:任務是指在執行特定作業時與使用者互動的一系列 Activity。 這些 Activity 按照各自的開啟順序排列在堆疊(即返回棧)中。
這張圖片說明了Activity 1,2,3在同一個棧中建立及銷燬的示意圖。很明顯,一般情況下,新建立的Activity例項會在棧的頂部,Running狀態,銷燬後,下面的Activity會出現在棧頂,此時該Activity就是Running狀態,實際上,一個應用,可以有多個任務棧同時存在,這就涉及到Activity的啟動模式了。
啟動模式
在清單檔案中宣告 Activity 時,您可以使用 元素的 launchMode 屬性指定 Activity 應該如何與任務關聯。
launchMode 屬性指定有關應如何將 Activity 啟動到任務中的指令。您可以分配給 launchMode 屬性的啟動模式共有四種:
“standard”(預設模式)
預設。系統在啟動 Activity 的任務中建立 Activity 的新例項並向其傳送 Intent。Activity 可以多次例項化,而每個例項均可屬於不同的任務,並且一個任務可以擁有多個例項。
“singleTop”
如果當前任務的頂部已存在 Activity 的一個例項,則系統會通過呼叫該例項的 onNewIntent() 方法向其傳送 Intent,而不是建立 Activity 的新例項。Activity 可以多次例項化,而每個例項均可屬於不同的任務,並且一個任務可以擁有多個例項(但前提是位於返回棧頂部的 Activity 並不是 Activity 的現有例項)。
例如,假設任務的返回棧包含根 Activity A 以及 Activity B、C 和位於頂部的 D(堆疊是 A-B-C-D;D 位於頂部)。收到針對 D 類 Activity 的 Intent。如果 D 具有預設的 “standard” 啟動模式,則會啟動該類的新例項,且堆疊會變成 A-B-C-D-D。但是,如果 D 的啟動模式是 “singleTop”,則 D 的現有例項會通過 onNewIntent() 接收 Intent,因為它位於堆疊的頂部;而堆疊仍為 A-B-C-D。但是,如果收到針對 B 類 Activity 的 Intent,則會向堆疊新增 B 的新例項,即便其啟動模式為 “singleTop” 也是如此。
注:為某個 Activity 建立新例項時,使用者可以按“返回”按鈕返回到前一個 Activity。 但是,當 Activity 的現有例項處理新 Intent 時,則在新 Intent 到達 onNewIntent() 之前,使用者無法按“返回”按鈕返回到 Activity 的狀態。
“singleTask”
系統建立新任務並例項化位於新任務底部的 Activity。但是,如果該 Activity 的一個例項已存在於一個單獨的任務中,則系統會通過呼叫現有例項的 onNewIntent() 方法向其傳送 Intent,而不是建立新例項。一次只能存在 Activity 的一個例項。
注:儘管 Activity 在新任務中啟動,但是使用者按“返回”按鈕仍會返回到前一個 Activity。
“singleInstance”.
與 “singleTask” 相同,只是系統不會將任何其他 Activity 啟動到包含例項的任務中。該 Activity 始終是其任務唯一僅有的成員;由此 Activity 啟動的任何 Activity 均在單獨的任務中開啟。
說到Manifest中的註冊檔案,關於Activity的一共有以下:
<activity android:allowEmbedded=["true" | "false"]
android:allowTaskReparenting=["true" | "false"]
android:alwaysRetainTaskState=["true" | "false"]
android:autoRemoveFromRecents=["true" | "false"]
android:banner="drawable resource"
android:clearTaskOnLaunch=["true" | "false"]
android:configChanges=["mcc", "mnc", "locale",
"touchscreen", "keyboard", "keyboardHidden",
"navigation", "screenLayout", "fontScale",
"uiMode", "orientation", "screenSize",
"smallestScreenSize"]
android:documentLaunchMode=["intoExisting" | "always" |
"none" | "never"]
android:enabled=["true" | "false"]
android:excludeFromRecents=["true" | "false"]
android:exported=["true" | "false"]
android:finishOnTaskLaunch=["true" | "false"]
android:hardwareAccelerated=["true" | "false"]
android:icon="drawable resource"
android:label="string resource"
android:launchMode=["standard" | "singleTop" |
"singleTask" | "singleInstance"]
android:maxRecents="integer"
android:multiprocess=["true" | "false"]
android:name="string"
android:noHistory=["true" | "false"]
android:parentActivityName="string"
android:permission="string"
android:process="string"
android:relinquishTaskIdentity=["true" | "false"]
android:resizeableActivity=["true" | "false"]
android:screenOrientation=["unspecified" | "behind" |
"landscape" | "portrait" |
"reverseLandscape" | "reversePortrait" |
"sensorLandscape" | "sensorPortrait" |
"userLandscape" | "userPortrait" |
"sensor" | "fullSensor" | "nosensor" |
"user" | "fullUser" | "locked"]
android:stateNotNeeded=["true" | "false"]
android:supportsPictureInPicture=["true" | "false"]
android:taskAffinity="string"
android:theme="resource or theme"
android:uiOptions=["none" | "splitActionBarWhenNarrow"]
android:windowSoftInputMode=["stateUnspecified",
"stateUnchanged", "stateHidden",
"stateAlwaysHidden", "stateVisible",
"stateAlwaysVisible", "adjustUnspecified",
"adjustResize", "adjustPan"] >
. . .
</activity>
scheme跳轉協議
這裡的scheme是一種頁面內跳轉協議,主要用於支援一下幾種場景:
伺服器下發跳轉路徑,客戶端根據伺服器下發跳轉路徑跳轉相應的頁面;
H5頁面點選錨點,根據錨點具體跳轉路徑App端跳轉具體的頁面;
從一個App跳轉到另一個App的頁面
首先,我們需要知道URI的概念:
URI:通用資源識別符號(Universal Resource Identifier),URI主要分三個部分:scheme, authority and path。其中authority又分為host和port。格式如下:
scheme://host:port/path, 比如:wy://myproject:8080/data/src/name,
就安卓手機本身而言,比如手機聯絡人的資訊,都是通過這樣的格式進行提供,並且其scheme一般是“content”。
1.請求到伺服器的請求資料後,可以前後端進行一個URI格式的定義,然後跳轉相應的頁面。比如
詳情頁:wy://myproject:8080/info
訂單頁:wy://myproject:8080/order
相應的Activity的manifest中的註冊時這樣的:
<activity
android:name=".InfoActivity"
android:label="@string/title_info"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.VIEW"></action>
<category android:name="android.intent.category.DEFAULT"></category>
<category android:name="android.intent.category.BROWSABLE"></category>
<data
android:scheme="wy"
android:host="myproject"
android:port="8080"
android:path="/info">
</data>
</intent-filter>
</activity>
<activity
android:name=".OrderActivity"
android:label="@string/title_Order"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.VIEW"></action>
<category android:name="android.intent.category.DEFAULT"></category>
<category android:name="android.intent.category.BROWSABLE"></category>
<data
android:scheme="wy"
android:host="myproject"
android:port="8080"
android:path="/order">
</data>
</intent-filter>
</activity>
注意:
<action android:name="android.intent.action.VIEW"></action>
<category android:name="android.intent.category.DEFAULT"></category>
上面兩句一定不能少,
<category android:name="android.intent.category.BROWSABLE"></category>
這句是想用js事件的,最好加上
<data
android:scheme="wy"
android:host="myproject"
android:port="8080"
android:path="/order">
</data>
這裡面四個屬性關係是一次下降,即scheme如果沒有設定,下面三個設定的屬性都會失效。
網頁連結跳轉為:
tv.setText(Html.fromHtml("<a href='wy://myproject:8080/info'>點我一下</a>"));
注意:Java程式碼中的連結格式資料一定要**大於等於**manifest中註冊的格式,比如針對以上的data格式,如果
tv.setText(Html.fromHtml("<a href='wy://myproject:8080'>點我一下</a>"));
tv.setText(Html.fromHtml("<a href='wy://myproject'>點我一下</a>"));
tv.setText(Html.fromHtml("<a href='wy://'>點我一下</a>"));
都是不行的,但是如果,manifest中data格式是:
<data
android:scheme="wy"
</data>
那麼以上幾種都是可以跳轉的。主要就是上面要注意的幾點問題,不然無法進行跳轉。
App內部隱式跳轉:
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse("wy://myproject:8080/info"));
startActivity(intent);
從一個App跳轉到另一個App也是類似。
以上,主要就是Activity在開發中使用到的設計知識點。歡迎大家留言評論交流。