1. 程式人生 > >一篇就夠了系列之Activity全解析

一篇就夠了系列之Activity全解析

前言:

Activity作為Android四大元件之一Google官方文件,是Android開發中最基本最常用的東西,那麼,Activity的定義到底是什麼呢?
從下面幾個方面介紹下Activity:

  1. 生命週期
  2. 任務棧
  3. 啟動模式
  4. scheme跳轉協議

生命週期

這其實是一個老生常談的東西,更是每一個Android開發人員熟記於心的東西,直接上圖:
這裡寫圖片描述

每個狀態的特點大致為:

  • onCreate:初始化操作,不可見,不可觸控
  • onStart:建立完成,可見,不可觸控
  • onResume:執行狀態,可見,可觸控
  • onPause:暫定狀態,可見,不可觸控
  • onStop:停止狀態,不可見,不可觸控
  • onDestory:銷燬狀態

下面五個迴圈狀態需要注意:

  1. onCreate->onStart->onResume->onPause->onStop->onDestory(Activity建立到銷燬)
  2. onResume->onPause->onResume此時Activity一直是可見狀態,比如此時彈出了一個Diglog,生命週期就是該流程
  3. onStart->onResume->onPause->onStop->onRestart->onStart Activity A被Activity B所覆蓋,然後B銷燬,A重新出現在螢幕上,此時就是該生命週期的流程,這個和將App直接按Home建退到後臺是一樣的流程
  4. onCreate->onStart->onResume->onPause->onStop->APP process killed->onCreate 這個流程主要是Activity在後臺,記憶體不足時被系統給回收銷燬,然後再重新打開了。
  5. 螢幕在進行旋轉後,生命週期是:銷燬->建立,同時,會增加兩個方法:onSaveInstanceStateonRestoreInstanceState,此時完整的生命週期是: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在開發中使用到的設計知識點。歡迎大家留言評論交流。