【原】Activity的LaunchMode和taskAffinity


  做項目到現在都一直沒有理解LaunchMode有什么用,或許根本就沒真正花心思去看,所以今天把這部分整理下。

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

  Task和Back Stack簡介

  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壓入棧頂。

  

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

  

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

  back stack

  如果繼續按返回鍵,當前棧頂的activity彈出並被destory,直到home界面。當所有的activity都彈出了,這個task也就消亡了。

  當開始一個新的task時,前一個task被設置為后台,在后台,所有的activity都處理stop狀態,但是back stack保留了所有后台activity的狀態信息,只是丟失了焦點。

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的表現:

//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 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM