Android開發藝術探索 - 第1章 Activity的生命週期和啓動模式

1. 生命週期

  1. 基本情況
    • onStart和onStop根據是否可見被回調;onResume和onPause根據是否在前臺被回調。實際使用中沒有其他區別。
    • onRestart當由不可見變爲可見時回調。
    • 由A啓動B,在A的onPause執行完之前,B不會被創建。所以在onPause中不能執行過多的操作。
  2. 異常情況
    設備配置改變或者內存不足時,Activity重建。
    • onSaveInstanceState & onRestoreInstanceState
      • 在onStop之前回調onSaveInstanceState保存狀態,與onPause無絕對的先後順序(Android P開始,發生在onStop之後);重建時在onStart之後回調onRestoreInstanceState恢復狀態。
      • onSaveInstanceState在所有Activity重新返回時可能已經被銷燬的情況下,都會被調用。所以,按home鍵/打開新Activity/鎖屏操作,也會觸發回調;onRestoreInstanceState只有在Activity真的被銷燬重建時才被調用。
      • onCreate中也可以根據Bundle是否爲空,進行數據恢復;onRestroeInstanceState被調用時Bundle一定不爲空。
      • 每個View也實現了這兩個回調,所以不同的View會有自己的默認保存和恢復操作。
    • android:configChanges指定希望去處理的一些configs,而不是讓Activity重建。常見的如locale/orientation/screenSize/keyboardHidden,當這些configs改變時,Activity不會重建,而是回調onConfigurationChanged。screenSize爲API 13引入,屏幕旋轉時也會改變,所以要與orientation同時指定。
    • 內存不足時系統按照優先級去殺死目標Activity所在的進程。如果一個進程沒有四大組件在執行,優先級會很低。

2. 啓動模式

綜合官方文檔 Understand Tasks and Back Stack

  1. task和back stack
    • task是一系列Activity的集合,Activity通過back stack進行維護。
    • 當啓動了新的task或者點擊了home鍵,當前的task會被放置到background。
  2. launch mode
    定義了啓動的Activity與當前task是如何關聯的。兩種方式指定:
    • 通過android:launchMode
      • standard
        創建一個新的Activity,運行在啓動該Activity的Activity所在的task中。
      • singleTop
        如果要創建的Activity已經位於task的棧頂,僅回調該Activity的onNewIntent;否則創建新的Activity,無論在task中是否存在該Activity的實例。
      • singleTask
        check task name in taskAffinity
        if (task exist) {
            if (activity exist) {
                clearTop
                call onNewIntent
            } else {
                create activity and add to task
            }
        } else {
            create task
            create activity and add to task
        }
        
      • singleInstance
        在singleTask的基礎上,限定了該Activity獨佔一個task。
    • 通過intent.addFlags。可以修改目標Activity launchMode中定義的行爲。實際情況中,各種flag的組合加上launchMode的設置,最終的行爲遠比四種預設要複雜。
      • FLAG_ACTIVITY_NEW_TASK
        在新的task中(taskAffinity指定)啓動該Activity。找到目標task之後,後續的行爲則按照launchMode的設置處理。
        例如,Activity launchMode爲standard,當intent中設置該flag,然後啓動該Activity,則會在taskAffinity設置的task(此時一般是默認值)中創建該Activity,無論在該task中是否有其實例存在。
      • FLAG_ACTIVITY_SINGLE_TOP
        同singleTop。
      • FLAG_ACTIVITY_CLEAR_TOP
        • 會銷燬目標Activity之上的所有Activity,之後的行爲分兩種情況:
          ①如果目標Activity是standard,則目標Activity銷燬重建,新的intent會被傳遞進去。因爲startand的Activity總是會被重建(官方解釋)。
          ②如果目標Activity是其他mode,或者intent中指定了FLAG_ACTIVITY_SINGLE_TOP,則目標Activity不會被重建,通過onNewIntent傳遞新的intent
        • 當目標Activity爲standard,與FLAG_ACTIVITY_NEW_TASK結合並不能實現singleTask的效果,因爲standard的Activity會被重建。所以要另外加上FLAG_ACTIVITY_SINGLE_TOP纔可以達到效果。
  3. activity的一些屬性設置
    • taskAffinity:指定task name,默認值爲package name。用於Application,指定所有Activity的task name;用於Activity,指定該Activity要運行的task的name。
    • allowTaskReparenting:允許Activity在其taskAffinity指定的task移到前臺的時刻,從之前的task轉移到該task。If an APK file contains more than one “app” from the user’s point of view, you probably want to use the taskAffinity attribute to assign different affinities to the activities associated with each “app”.
    • 其他見Clearing the back
    • Activity設置如下IntentFilter,會在啓動器中顯示圖標,以提供task的入口:
      <intent-filter ... >
          <action android:name="android.intent.action.MAIN" />
          <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
      
      對於singleTask和singleInstance的Activity可以添加該設置,方便用戶啓動相應的task。

3.IntentFilter

綜合官方文檔 Intents and Intent Filters & Intent & IntentFilter
Activity啓動分顯式和隱式。隱式需要Intent匹配目標Activity的IntentFilter,一個Activity可以有多個IntentFilter。
當系統接收到隱式Intent,會基於三個方面查找最合適的目標Activity:

  1. Action test
    filter可以聲明0到多個action。
    Intent當中的action必須匹配filter其中一個,才能匹配。如果filter沒有聲明任何的action,則所有的Intent都不能匹配;如果Intent中沒有指定action,則只要filter中至少有一個action就可以匹配。
  2. Category test
    filter可以聲明0到多個category。
    Intent當中所有的category都能匹配於filter中的,則匹配。即Intent當中的category只要是filter中的category的子集,就可以匹配。
    如果Intent未指定category,startActivitystartActivityForResult會自動爲Intent添加android.intent.category.DEFAULT,所以多數情況下filter中至少要定義該category纔可以被隱式調用。
  3. Data test
    filter可以聲明0到多個data。
    data包括兩部分,URI和data type(MIME media type)。
    • URI結構如下:

      <scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]
      

      path/pathPrefix/pathPattern:path指完整路徑;pathPattern可使用通配符;pathPrefix指示前綴信息。
      這些屬性在data中都是可選的,同時存在一定的依賴性:scheme未指定,host就被忽略;host未指定,port就被忽略;scheme和host都未指定,path就被忽略。
      URI的匹配規則是:只要filter中聲明的部分Intent中也存在,則匹配。Intent中多的部分不影響匹配。

    • data匹配規則

      • Intent當中不包含URI和MIME:filter中也沒有指定URI和MIME。
      • Intent中只包含URI,沒有MIME(沒有直接指定,從URI中也無法判斷):URI匹配filter中的URI,且filter也沒有指定MIME。
      • Intent中只包含MIME,沒有URI:MIME匹配filter中的MIME,且filter沒有指定URI。
      • Intent中包含URI和MIME(直接指定,或從URI中判斷出):MIME要匹配filter中的MIME;URI要匹配filter中的URI,或者Intent中是contentfile類型的URI同時filter沒有指定URI。所以如果filter中只列出了MIME,則他會被假定支持contentfile類型的URI。
      • 如果Intent中指定了URI或者MIME,但是filter中沒有data,則不匹配。
    • 在隱式啓動之前,可以通過PackageManager的queryIntentActvities方法,或者Intent的resolveActivity方法查詢是否有匹配的Activity存在。

    • 因爲data標籤可以有多個,所以同一個filter下的多個data會形成一個總的匹配規則。例如:

    <intent-filter>
        <data android:scheme="http"/>
        <data android:scheme="content"/>
        <data android:host="123"/>
        <data android:mimeType="image/*"/>
    </intent-filter>
    
    則Intent需要執行setDataAndType(Uri.parse("http://123"), "image/*"))或者setDataAndType(Uri.parse("content://123"), "image/*"))才能匹配。

總結:

  • action和category:Intent中有的只要IntentFilter中也有,就匹配。filter中定義了一些操作集,Intent則定義了一個操作。
  • data:Intent中指定的data和type,在IntentFilter存在一個完全匹配,就匹配。其中,MIME type需要相等才匹配;URI的匹配重點在於scheme,只要filter中的範圍限定大於Intent即匹配。另外scheme有潛在的默認的值:content和file,如果filter中不指定scheme,Intent中指定了這兩種URI,可以匹配;Intent中不指定URI,也可以匹配。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章