Android之Activity系列總結(三)--Activity的四種啟動模式

來源:互聯網
上載者:User

標籤:oom   otto   blog   關聯   odi   頁面   cbo   chmod   情況   

一、返回棧簡介

任務是指在執行特定作業時與使用者互動的一系列 Activity。 這些 Activity 按照各自的開啟順序排列在堆棧(即返回棧,也叫任務棧)中。

首先介紹一下任務棧:

(1)程式開啟時就建立了一個任務棧, 用於儲存當前程式的activity,所有的activity屬於一個任務棧。 
(2)一個任務棧包含了一個activity的集合, 去有序的選擇哪一個activity和使用者進行互動:只有在任務棧棧頂的activity才可以跟使用者進行互動。 
(3)任務棧可以移動到後台, 並且保留了每一個activity的狀態. 並且有序的給使用者列出它們的任務, 而且還不丟失它們狀態資訊。 
(4)退出應用程式時:當把所有的任務棧中所有的activity清除出棧時,任務棧會被銷毀,程式退出。

,是一個簡單的任務棧執行個體:

圖 1. 顯示任務中的每個新 Activity 如何向返回棧添加項目。 使用者按“返回”按鈕時,當前 Activity 隨即被銷毀,而前一個 Activity 恢複執行。

任務棧的缺點: 
(1)每開啟一次頁面都會在任務棧中添加一個Activity,而只有任務棧中的Activity全部清除出棧時,任務棧被銷毀,程式才會退出,這樣就造成了用,戶體驗差, 需要點擊多次返回才可以把程式退出了。 
(2)每開啟一次頁面都會在任務棧中添加一個Activity還會造成資料冗餘, 重複資料太多, 會導致記憶體溢出的問題(OOM)。

為瞭解決任務棧的缺點,我們引入了啟動模式。

二、啟動模式

啟動模式(launchMode)在多個Activity跳轉的過程中扮演著重要的角色,它可以決定是否產生新的Activity執行個體,是否重用已存在的Activity執行個體,是否和其他Activity執行個體公用一個task裡。這裡簡單介紹一下task的概念,task是一個具有棧結構的對象,一個task可以管理多個Activity,啟動一個應用,也就建立一個與之對應的task。

Activity一共有以下四種launchMode:

  • standard
  • singleTop
  • singleTask
  • singleInstance

如何配置Activity的啟動模式? 

啟動模式允許您定義 Activity 的新執行個體如何與當前任務關聯。 您可以通過兩種方法定義不同的啟動模式:

  • 使用資訊清單檔

    在資訊清單檔中聲明 Activity 時,您可以指定 Activity 在啟動時應該如何與任務關聯。

  • 使用 Intent 標誌

    調用 startActivity() 時,可以在 Intent 中加入一個標誌,用於聲明新 Activity 如何(或是否)與當前任務關聯。

註:兩種方式優先順序:Intent 中所定義的優先順序要高於資訊清單檔中所定義。

某些適用於資訊清單檔的啟動模式不可用作 Intent 標誌,同樣,某些可用作 Intent 標誌的啟動模式無法在資訊清單檔中定義。

下面我們一一介紹以下四種啟動模式及其應用情境:

1、standard 

standard模式是預設的啟動模式,不用為配置android:launchMode屬性即可,當然也可以指定值為standard。

activity_main.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    android:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    tools:context="com.geniusvjr.standard_demo.MainActivity">    <TextView        android:id="@+id/tv"        android:layout_width="match_parent"        android:layout_height="wrap_content"/>    <Button        android:id="@+id/btn_skip"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:text="跳轉到MainActivity"/></LinearLayout>

MainActivity.java

public class MainActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        TextView textView = (TextView) findViewById(R.id.tv);        textView.setText(this.toString());        Button button = (Button) findViewById(R.id.btn_skip);        button.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                Intent intent = new Intent(MainActivity.this, MainActivity.class);                startActivity(intent);            }        });    }}

MainActivity介面中的TextView用於顯示當前Activity執行個體的序號,Button用於跳轉到下一個FirstActivity介面。 
然後我們連續點擊幾次按鈕,將會出現下面的現象: 

都是MainActivity的執行個體,但是序號不同。 
stardard模式的原理如下:

,每次跳轉系統都會在task中產生一個新的MainActivity執行個體,並且放於棧結構的頂部,當我們按下後退鍵時,才能看到原來的MainActivity執行個體。 
這就是standard啟動模式,不管有沒有已存在的執行個體,都產生新的執行個體。

2、singleTop

我們在上面的基礎上為指定屬性android:launchMode=”singleTop”,系統就會按照singleTop啟動模式處理跳轉行為。我們重複上面幾個動作,將會出現下面的現象:

我們看到這個結果跟standard有所不同,三個序號是相同的,也就是說使用的都是同一個MainActivity執行個體;如果按一下後退鍵,程式立即退出,說明當前棧結構中只有一個Activity執行個體。singleTop模式的原理如所示:

正如所示,跳轉時系統會先在棧結構中尋找是否有一個MainActivity執行個體正位於棧頂,如果有則不再產生新的,而是直接使用。也許朋友們會有疑問,我只看到棧內只有一個Activity,如果是多個Activity怎麼辦,如果不是在棧頂會如何?我們接下來再通過一個樣本來證實一下大家的疑問。

建立一個SecondActivity:

public class SecondActivity extends Activity{    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_second);        TextView textView = (TextView) findViewById(R.id.tv);        textView.setText(this.toString());        Button button = (Button) findViewById(R.id.btn_second);        button.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                Intent intent = new Intent(SecondActivity.this, MainActivity.class);                startActivity(intent);            }        });    }}

將之前的MainActivity跳轉代碼改為:

Intent intent = new Intent(MainActivity.this, SecondActivity.class);                startActivity(intent);

這時候,FirstActivity會跳轉到SecondActivity,SecondActivity又會跳轉到FirstActivity。示範結果如下:

我們看到,兩個MainActivity的序號是不同的,證明從SecondActivity跳轉到MainActivity時產生了新的MainActivity執行個體。原理圖如下:

我們看到,當從SecondActivity跳轉到MainActivity時,系統發現存在有FirstActivity執行個體,但不是位於棧頂,於是重建一個執行個體。 
這就是singleTop啟動模式,如果發現有對應的Activity執行個體正位於棧頂,則重複利用,不再產生新的執行個體。

這種啟動模式通常適用於接受到訊息後顯示的介面,例如QQ接受到訊息後彈出Activity,如果一次來10條訊息,總不能一次彈10個Activity。

3、singleTask 

在上面的基礎上我們修改MainActivity的屬性android:launchMode=”singleTask”,示範結果如下: 

我們注意到,在上面的過程中,MainActivity的序號是不變的,SecondActivity的序號卻不是唯一的,說明從SecondActivity跳轉到MainActivity時,沒有產生新的執行個體,但是從MainActivity跳轉到SecondActivity時產生了新的執行個體。

SecondActivity跳轉到MainActivity後的棧結構變化的結果,我們注意到,SecondActivity消失了,沒錯,在這個跳轉過程中系統發現有存在的FirstActivity執行個體,於是不再產生新的執行個體,而是將MainActivity之上的Activity執行個體統統出棧,將MainActivity變為棧頂對象,顯示到幕前。也許朋友們有疑問,如果將SecondActivity也設定為singleTask模式,那麼SecondActivity執行個體是不是可以唯一呢?在我們這個樣本中是不可能的,因為每次從SecondActivity跳轉到MainActivity時,SecondActivity執行個體都被迫出棧,下次等MainActivity跳轉到SecondActivity時,找不到存在的SecondActivity執行個體,於是必鬚生成新的執行個體。但是如果我們有ThirdActivity,讓SecondActivity和ThirdActivity互相跳轉,那麼SecondActivity執行個體就可以保證唯一。 
這就是singleTask模式,如果發現有對應的Activity執行個體,則使此Activity執行個體之上的其他Activity執行個體統統出棧,使此Activity執行個體成為棧頂對象,顯示到幕前。

4、singleInstance 

這種啟動模式比較特殊,因為它會啟用一個新的棧結構,將Activity放置於這個新的棧結構中,並保證不再有其他Activity執行個體進入。 
我們修改MainActivity的launchMode=”standard”,SecondActivity的launchMode=”singleInstance”,由於涉及到了多個棧結構,我們需要在每個Activity中顯示當前棧結構的id,所以我們為每個Activity添加如下代碼:

TextView textView = (TextView) findViewById(R.id.tv);        textView.setText("current task id:" + this.getTaskId());

結果如下: 

我們發現這兩個Activity執行個體分別被放置在不同的棧結構中,關於singleInstance的原理圖如下

我們看到從MainActivity跳轉到SecondActivity時,重新啟用了一個新的棧結構,來放置SecondActivity執行個體,然後按下後退鍵,再次回到原始棧結構;圖中下半部分顯示的在SecondActivity中再次跳轉到MainActivity,這個時候系統會在原始棧結構中產生一個MainActivity執行個體,然後回退兩次,注意,並沒有退出,而是回到了SecondActivity,為什麼呢?是因為從SecondActivity跳轉到MainActivity的時候,我們的起點變成了SecondActivity執行個體所在的棧結構,這樣一來,我們需要“迴歸”到這個棧結構。

如果應用1的任務棧中建立了MainActivity執行個體,如果應用2也要啟用MainActivity,則不需要建立,兩應用共用該Activity執行個體;比如你的瀏覽器已經開啟,此時另一個應用也要訪問瀏覽器,就會直接在當前的瀏覽器中訪問,否則開啟瀏覽器。這樣節省系統資源。(如果手機裡有多個瀏覽器,則需要選擇其中一個,選擇的已經開啟)。

代碼下載: http://download.csdn.net/detail/jycboy/9747445

應用情境: 

singleTop適合接收通知啟動的內容顯示頁面。例如,某個新聞用戶端的新聞內容頁面,如果收到10個新聞推送,每次都開啟一個新聞內容頁面是很煩人的。

singleTask適合作為程式進入點。例如瀏覽器的主介面。不管從多少個應用啟動瀏覽器,只會啟動主介面一次,其餘情況都會走onNewIntent,並且會清空主介面上面的其他頁面。之前開啟過的頁面,開啟之前的頁面就ok,不再建立。

singleInstance適合需要與程式分離開的頁面。例如鬧鈴提醒,將鬧鈴提醒與鬧鈴設定分離。singleInstance不要用於中間頁面,如果用於中間頁面,跳轉會有問題,比如:A -> B (singleInstance) -> C,完全退出後,在此啟動,首先開啟的是B。

onNewIntent()

大家遇到一個應用的Activity供多種方式調用啟動的情況,多個調用希望只有一個Activity的執行個體存在,這就需要Activity的onNewIntent(Intent intent)方法了。只要在Activity中加入自己的onNewIntent(intent)的實現加上Manifest中對Activity設定lanuchMode=“singleTask”就可以。

       onNewIntent()非常好用,Activity第一啟動的時候執行onCreate()---->onStart()---->onResume()等後續生命週期函數,也就時說第一次啟動Activity並不會執行到onNewIntent(). 而後面如果再有想啟動Activity的時候,那就是執行onNewIntent()---->onResart()------>onStart()----->onResume().  如果android系統由於記憶體不足把已存在Activity釋放掉了,那麼再次調用的時候會重新啟動Activity即執行onCreate()---->onStart()---->onResume()等。

     當調用到onNewIntent(intent)的時候,需要在onNewIntent() 中使用setIntent(intent)賦值給Activity的Intent.否則,後續的getIntent()都是得到老的Intent。

protected void onNewIntent(Intent intent) { super.onNewIntent(intent); setIntent(intent);//must store the new intent unless getIntent() will return the old one processExtraData();}

不要忘記,系統可能會隨時殺掉後台啟動並執行 Activity ,如果這一切發生,那麼系統就會調用 onCreate 方法,而不調用 onNewIntent 方法,一個好的解決方案就是在 onCreate 和 onNewIntent 方法中調用同一個處理資料的方法,如下所示:

public void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  setContentView(R.layout.main);  processExtraData();}
protected void onNewIntent(Intent intent) {  super.onNewIntent(intent);   setIntent(intent);//must store the new intent unless getIntent() will return the old one  processExtraData()}private void processExtraData(){  Intent intent = getIntent();  //use the data received here}

 

 

Activity系列文章:

Android之Activity系列總結(一)--Activity概覽

Android之Activity系列總結(二)--任務和返回棧

Android 旋轉螢幕--處理Activity與AsyncTask的最佳解決方案(處理運行時變更)

 Activity之掃視畫面(Overview Screen)

 

Android之Activity系列總結(三)--Activity的四種啟動模式

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.