Activity的LaunchMode和taskAffinity

    作項目到如今都一直沒有理解LaunchMode有什麼用,或許根本就沒真正花心思去看,因此今天把這部分整理下。html

    設置Activity的LaunchMode屬性能夠決定這個Activity是和當前Task保持關聯,仍是說每次運行這個Activity是新建一個實例,仍是保持單例。java

  Task和Back Stack簡介android

  task是一組Activities的集合,一組Activities被Stack(back stack)所管理。瀏覽器

  在一個應用中,有3個activities,分別是activity1,activity2,activity3,首先activity1被start,此時,若是應用沒有建立task則建立,並把activity1壓入棧頂,activity1觸發onCreate->onStart->onResume。

    接着activity1轉向到activity2時,activity1先觸發onPause,activity2觸發onCreate->onStart->onResume,而後activity1觸發onPause->onStop,activity2壓入棧頂。app

  

  以此類推,activity2轉向activity3也是同樣的步驟。那麼當前棧頂是activity3。ide

  

  當咱們按下手機上的返回鍵時,棧頂的activity3觸發onPause,activity2須要從狀態stop到pause,因此觸發了onPause->onStart->onResume,activity3觸發onStop->onDestory,由於activity3從棧頂彈出,因此觸發onDestory,此時,activity2在棧頂。ui

  back stack

  若是繼續按返回鍵,當前棧頂的activity彈出並被destory,直到home界面。當全部的activity都彈出了,這個task也就消亡了。this

  當開始一個新的task時,前一個task被設置爲後臺,在後臺,全部的activity都處理stop狀態,可是back stack保留了全部後臺activity的狀態信息,只是丟失了焦點。3d

task

  反覆的在兩個activity之間切換,activity會產生多個獨立的實例。日誌

  stack

  查閱有關Activity生命週期更多說明。

  兩種方式設置LaunchMode屬性

    1.  在 manifest文件中設置

<activity android:name=".activity.ActivityA"
        android:launchMode="standard">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

    2.  使用Intent flags設置
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setClass(ActivityA.this, ActivityB.class);
startActivity(intent);

 四種LaunchMode說明

  standard

   不作任何設置,默認模式就是standard,activity在每次start時,都會有一個新的實例被task管理。下面看下代碼實例。

//ActivityA.java
Intent intent = new Intent();
intent.setClass(ActivityA.this, ActivityB.class);
startActivity(intent);

//ActivityB.java
Intent intent = new Intent();
intent.setClass(ActivityB.this, ActivityA.class);
startActivity(intent);

操做1:在ActivityA(藍)和ActivityB(綠)之間重複切換,按返回鍵推到home界面。

  能夠發現(藍色86和綠色79的taskID)ActivityA和ActivityB都在同一個task,而且每次resume的實例都是不同的。這說明在一個activity能夠有多個實例在同一個task中。

在按返回按鍵時,將依次彈出stack。

singleTop

  和standard同樣,能夠屢次實例,但,若是處於當前棧頂而且接受到一個與當前activity同樣類型的intent,那麼不會建立一個新實例,而是觸發onNewIntent()事件。

//ActivityA.java
Intent intent = new Intent();
intent.setClass(ActivityA.this, ActivityA.class);
startActivity(intent);

@Override
protected void onNewIntent(Intent intent) {
    logger.d("onNewIntent " + this.hashCode() + " taskID "
                + this.getTaskId());
    super.onNewIntent(intent);
}

<activity android:name=".activity.ActivityA" android:label="ActivityA"
            android:launchMode="singleTop">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

操做1:點擊ActivityA上的按鈕

  發現當點擊按鈕是ActivityA->onPause->onNewIntent->onResume,沒有新建新的實例(藍62)。

  這個模式在這個場景下比較有用,好比:若是有一個其餘的應用想啓動你的Activity(launch mode爲singleTop),而你當前的Activity正好在棧頂,那麼就會調用到onNewIntent方法。原文貼上:If an instance of the activity already exists at the top of the current task, the system routes the intent to that instance through a call to its onNewIntent() method。

  singleTask

  系統會建立一個新task(若是沒有啓動應用)和一個activity新實例在新task根部,而後,若是activity實例已經存在單獨的task中,系統會調用已經存在activity的 onNewIntent()方法,而不是存在新實例,僅有一個activity實例同時存在。

<activity android:name=".activity.ActivityA" android:label="ActivityA" android:launchMode="standard">
  <intent-filter>
     <action android:name="android.intent.action.MAIN" />
      <category android:name="android.intent.category.LAUNCHER" />
  </intent-filter>
</activity>
<activity android:name=".activity.ActivityB" android:label="ActivityB" android:launchMode="singleTask">
  <intent-filter>
     <action android:name="android.intent.action.MAIN" />
    </intent-filter>
</activity>
<activity android:name=".activity.ActivityC" android:label="ActivityC" android:launchMode="standard">
  <intent-filter>
     <action android:name="android.intent.action.MAIN" />
    </intent-filter>
</activity>

 操做1:ActivityA->ActivityB->ActivityC->ActivityA->ActivityB->ActivityC

  

   能夠看到,當再次進入ActivityB時,沒有onCreate,而是onNewIntent(綠55)。

   這裏咱們也能夠發現一個現象,當在調用到ActivityB的onNewIntent時,以前的ActivityA和ActivityC都調用了onDestory。也就是說,系統發現棧中存在ActivityB的實例時,ActivityA和ActivityB都彈棧了。

   列出Log日誌(這裏設ActivityA的LaunchMode爲singleTask),ActivityB和ActivityC都在onNewIntent先後調用了onDestory。

  

  singleInstance

    和singleTask類似,除了系統不會讓其餘的activities運行在全部持有的task實例中,這個activity是獨立的,而且task中的成員只有它,任何其餘activities運行這個activity都將打開一個獨立的task。

<activity android:name=".activity.ActivityA" android:launchMode="singleTask">
  <intent-filter>
      <action android:name="android.intent.action.MAIN" />
      <category android:name="android.intent.category.LAUNCHER" />
  </intent-filter>
</activity>
<activity android:name=".activity.ActivityB" android:launchMode="singleInstance">
  <intent-filter>
     <action android:name="android.intent.action.MAIN" />
  </intent-filter>
</activity>
<activity android:name=".activity.ActivityC">
  <intent-filter>
     <action android:name="android.intent.action.MAIN" />
   </intent-filter>
</activity>

操做1:ActivityA->ActivityB->ActivityA

  

  能夠發現,兩個Activity是在不一樣的Task中,其次,當調用到onNewIntent時,ActivityB沒有被Destory,互不干涉。

  操做2:ActivityA->ActivityB->ActivityC,按返回鍵

  

  圖解:

  

  剛進入應用,建立TaskA,ActivityA爲棧頂,從ActivityA到ActivityB,ActivityB進入TaskB(若是再次進入ActivityB,則不建立Task,調用onNewIntent),此時TaskB中的ActivityB爲棧頂,從ActitivyB到ActivityC,ActivityC爲棧頂。

  一直按返回鍵,先從TaskA中依次將Activity彈出,而後再從TaskB中將ActiviyB彈出。ActiviyC->ActivityA->ActivityB。

  這裏分析一個問題,瀏覽器的LaunchMode爲singleTask,因此若是當你點擊一個鏈接下載文件時(由一個activity來處理下載,launchmode爲standard),若是再次進入瀏覽器,那麼下載頁面就被Destory了,那麼這裏咱們能夠把下載頁面LaunchMode設置爲singleInstance能夠解決這個問題。

  Affinity定義

  Affinity更像是代表了activity屬於哪一個task,默認狀況下,應用全部的activities都有相同的affinity,因此都是在相同的task中。而後你能夠編輯默認的affinity。Activities定義在不一樣的應用能夠共享一個affinity,或者activities定義在相同的應用中能夠被不一樣的affinities所關聯。

  你能夠編輯在<activity>元素中activity的taskAffinity屬性。

   先看看兩種不一樣的狀況下affinity的表現:

  • 當運行一個activity包含了FLAG_ACTIVITY_NEW_TASK標記
    //ActivityA.java
    Intent intent = new Intent();    
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    intent.setClass(ActivityA.this, ActivityB.class);
    startActivity(intent);

    <activity android:name=".activity.ActivityA" android:taskAffinity="com.android.demo.affinity1">
      <intent-filter>
         <action android:name="android.intent.action.MAIN" />
           <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity android:name=".activity.ActivityB" android:taskAffinity="com.android.demo.affinity2">
      <intent-filter>
         <action android:name="android.intent.action.MAIN" />
       </intent-filter>
    </activity>

    操做1:不一樣的affinity值,ActivityA->ActivityB

    若是已經存在相同affinity,那麼新activity運行在這個task中,不然,系統建立新task。

    操做2:相同的affinity值,ActivityA->ActivityB

    <activity android:name=".activity.ActivityA" android:taskAffinity="com.android.demo.affinity1">
      <intent-filter>
         <action android:name="android.intent.action.MAIN" />
           <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity android:name=".activity.ActivityB" android:taskAffinity="com.android.demo.affinity2">
      <intent-filter>
         <action android:name="android.intent.action.MAIN" />
       </intent-filter>
    </activity>

    能夠看出ActivityA和ActivityB都運行在同一個task中。

      使用來表示是否容許activity從新附屬其餘Task,仍是舉例說明吧。

      有兩個應用,Demo1和Demo2,Demo1中有2個Activity(ActivityA,ActivityC),ActivityA能夠轉向到ActivityC,Demo2中有一個Activity(ActivityB),也能夠轉向到ActivityC。

      操做1:設置ActivityC的allowTaskReparenting屬性爲true。

          運行Demo2,轉向到ActivityC,在ActivityC中打印信息,返回到HOME界面,運行Demo1。

    //Demo1 
    //ActivityA.java
    Intent intent = new Intent();
    intent.setClass(ActivityA.this, ActivityC.class);
    startActivity(intent);
    
    //ActivityC.java
    tv.setText(ActivityC.this.toString());
    
    //Demo2
    //ActivityB.java
    Intent intent = new Intent();
    intent.setClassName("com.android.demo","com.android.demo.activity.ActivityC");
    ActivityB.this.startActivity(intent);

    //Demo1
    <activity android:name=".activity.ActivityA">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>
    <activity android:name=".activity.ActivityC" android:allowTaskReparenting="true">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
      </intent-filter>
    </activity>
    
    
    //Demo2
    <activity android:name=".ActivityB">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>

    運行結果:(黃色Demo1,綠色Demo2)

      

        ActivityB轉向到ActivityC,此時ActivityC就關聯到Demo2的Task中,TaskID都爲231。在運行Demo1時,看到是ActivityC而不是ActivityA。當再次進入Demo2時就看不到ActivityC了。

      操做2:將ActivityC的taskAffinity設置爲"com.android.demo.activityc"。

          運行Demo2,轉向到ActivityC,在ActivityC中打印信息,返回到HOME界面,運行Demo1。

    //Demo1
    <activity android:name=".activity.ActivityC"
                android:taskAffinity="com.android.demo.activityc"
                android:allowTaskReparenting="true">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
      </intent-filter>
    </activity>

     運行結果:

      

       從結果中能夠看出,Demo1和Demo2都擁有ActivityC,也就是說有2個Task裏存在ActivityC,分別被Demo1和Demo2所使用。

      操做3:將ActivityC和ActivityB的taskAffinity都設爲"com.android.demo.activityc"。

          運行Demo2,轉向到ActivityC,在ActivityC中打印信息,返回到HOME界面,運行Demo1。

    //Demo2
    <activity android:name=".ActivityB" android:taskAffinity="com.android.demo.activityc">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>
    
    //Demo1
    <activity android:name=".activity.ActivityC"
                android:taskAffinity="com.android.demo.activityc"
                android:allowTaskReparenting="true">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
      </intent-filter>
    </activity>

     運行結果:

      

       和操做1相反,再進入Demo2時看到是ActivityC,進入Demo1都是看到ActivityA。

     

      寫到最後愈來愈崩潰了,若是有什麼地方寫的不對或不清楚請指明。

      轉帖請說明原文出處:http://www.cnblogs.com/SteveMing/archive/2012/04/24/2459575.html

相關文章
相關標籤/搜索