Android之Activity啓動模式及其應用場景

Android之Activity啓動模式及其應用場景

任務棧:

  • 程序打開時就創建了一個任務棧, 用於存儲當前程序的activity,所有的activity屬於一個任務棧。
  • 一個任務棧包含了一個activity的集合, 去有序的選擇哪一個activity和用戶進行交互:只有在任務棧棧頂的activity纔可以跟用戶進行交互。
  • 任務棧可以移動到後臺, 並且保留了每一個activity的狀態. 並且有序的給用戶列出它們的任務, 而且還不丟失它們狀態信息。
  • 退出應用程序時:當把所有的任務棧中所有的activity清除出棧時,任務棧會被銷燬,程序退出。

任務棧的缺點:

  • 每開啓一次頁面都會在任務棧中添加一個Activity,而只有任務棧中的Activity全部清除出棧時,任務棧被銷燬,程序纔會退出,這樣就造成了用,戶體驗差, 需要點擊多次返回纔可以把程序退出了。
  • 每開啓一次頁面都會在任務棧中添加一個Activity還會造成數據冗餘, 重複數據太多, 會導致內存溢出的問題(OOM)。

爲了解決任務棧的缺點,引入了啓動模式

啓動模式在多個Activity跳轉的過程中扮演着重要的角色,它可以決定是否生成新的Activity實例,是否重用已存在的Activity實例,是否和其他Activity實例公用一個task裏。這裏簡單介紹一下task的概念,task是一個具有棧結構的對象,一個task可以管理多個Activity,啓動一個應用,也就創建一個與之對應的task。

Activity一共有以下四種launchMode:

  • standard
  • singleTop
  • singleTask
  • singleInstance

配置Activity的啓動模式
直接在AndroidManifest.xml配置的android:launchMode屬性爲以上四種之一即可。

四種啓動模式及其應用場景:

standard

standard模式是默認的啓動模式,不用爲配置android:launchMode屬性即可,當然也可以指定值爲standard

activity_main.xml

<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"
    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界面。每次跳轉系統都會在task中生成一個新的MainActivity實例,並且放於棧結構的頂部,當我們按下後退鍵時,才能看到原來的MainActivity實例。
這就是standard啓動模式,不管有沒有已存在的實例,都生成新的實例。

singleTop

在上面的基礎上爲指定屬性android:launchMode=”singleTop”,系統就會按照singleTop啓動模式處理跳轉行爲。

跟standard有所不同,三個序列號是相同的,也就是說使用的都是同一個MainActivity實例;如果按一下後退鍵,程序立即退出,說明當前棧結構中只有一個Activity實例。跳轉時系統會先在棧結構中尋找是否有一個MainActivity實例正位於棧頂,如果有則不再生成新的,而是直接使用。

新建一個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。

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,讓SecondActivityThirdActivity互相跳轉,那麼SecondActivity實例就可以保證唯一。

這就是singleTask模式,如果發現有對應的Activity實例,則使此Activity實例之上的其他Activity實例統統出棧,使此Activity實例成爲棧頂對象,顯示到幕前。

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());

從MainActivity跳轉到SecondActivity時,重新啓用了一個新的棧結構,來放置SecondActivity實例,然後按下後退鍵,再次回到原始棧結構;在SecondActivity中再次跳轉到MainActivity,這個時候系統會在原始棧結構中生成一個MainActivity實例,然後回退兩次,注意,並沒有退出,而是回到了SecondActivity,從SecondActivity跳轉到MainActivity的時候,起點變成了SecondActivity實例所在的棧結構,這樣一來,需要“迴歸”到這個棧結構。

應用場景:

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

  • singleTask適合作爲程序入口點。例如瀏覽器的主界面。不管從多少個應用啓動瀏覽器,只會啓動主界面一次,其餘情況都會走onNewIntent,並且會清空主界面上面的其他頁面。之前打開過的頁面,打開之前的頁面就ok,不再新建。

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章