Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android Activity啟動模式的功能驗證

Android Activity啟動模式的功能驗證

編輯:關於Android編程

之前一直都是看別人寫的啟動模式,發現網上大多數的內容都是抄襲來抄襲去,直到最近看了開發藝術這本書,發現之前對啟動模式的理解過於簡單,很多東西都沒有考慮到,為了加深理解,於是決定自己動手去驗證一下四個啟動模式。當然我們也從最簡單的啟動模式開始驗證。

為了打印方便,定義一個基礎Activity,在其onCreate方法和onNewIntent方法中打印出當前Activity的日志信息,主要包括所屬的task,當前類的hashcode,以及taskAffinity的值。之後我們進行測試的Activity都直接繼承該Activity

public class BaseActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.e(TAG, ===========================================onCreate=========================================================);
        Log.e(TAG, onCreate  + getClass().getSimpleName() +  TaskId:  + getTaskId() +  hasCode: + this.hashCode());
        dumpTaskAffinity();
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        Log.e(TAG, ===========================================onNewIntent=========================================================);
        Log.e(TAG, onNewIntent  + getClass().getSimpleName() +  TaskId:  + getTaskId() +  hasCode: + this.hashCode());
        dumpTaskAffinity();
    }

    protected void dumpTaskAffinity(){
        try {
            ActivityInfo info = this.getPackageManager()
                    .getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
            Log.e(TAG, taskAffinity:+info.taskAffinity);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
    }
}
standard模式

這個模式是默認的啟動模式,即標准模式,在不指定啟動模式的前提下,系統默認使用該模式啟動Activity,每次啟動一個Activity都會重寫創建一個新的實例,不管這個實例存不存在,這種模式下,誰啟動了該模式的Activity,該Activity就屬於啟動它的Activity的任務棧中。


新建一個Activity,並聲明在manifest文件中

對於standard模式,android:launchMode可以不進行聲明,因為默認就是standard。

StandardActivity 的代碼如下,入口Activity中有一個按鈕進入該Activity,這個Activity中又有一個按鈕啟動StandardActivity。

public class StandardActivity extends BaseActivity {
    private Button jump;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_standard);

        jump= (Button) findViewById(R.id.jump);
        jump.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(StandardActivity.this, StandardActivity.class);
                startActivity(intent);
            }
        });
    }

}
看下gif動態圖,有點卡頓,湊合看,我們首先進入StandardActivity,進入後再點擊**測試Standard**的按鈕,再按四次返回鍵不斷返回。 ![這裡寫圖片描述](http://img.blog.csdn.net/20151023135647145) 輸出的日志如下 ![這裡寫圖片描述](http://img.blog.csdn.net/20151023135714701) 可以看到日志輸出了四次StandardActivity的和一次MainActivity的,從MainActivity進入StandardActivity一次,後來我們又按了三次按鈕,總共四次StandardActivity的日志,並且所屬的任務棧的id都是62,這也驗證了**誰啟動了該模式的Activity,該Activity就屬於啟動它的Activity的任務棧中**這句話,因為啟動StandardActivity的是MainActivity,而MainActivity的taskId是62,因此啟動的StandardActivity也應該屬於id為62的這個task,後續的3個StandardActivity是被StandardActivity這個對象啟動的,因此也應該還是62,所以taskId都是62。並且每一個Activity的hashcode都是不一樣的,說明他們是不同的實例,即**每次啟動一個Activity都會重寫創建一個新的實例** singleTop,棧頂復用模式

這個模式下,如果新的activity已經位於棧頂,那麼這個Activity不會被重寫創建,同時它的onNewIntent方法會被調用。如果棧頂不存在該Activity的實例,則情況與standard模式相同。

SingleTopActivity代碼和StandardActivity類似,只不過記得在manifest文本後中修改啟動模式。

操作和standard模式類似,直接貼輸出日志

這裡寫圖片描述

我們看到,除了第一次進入SingleTopActivity這個Activity時,輸出的是onCreate方法中的日志,後續的都是調用了onNewIntent方法,並沒有調用onCreate方法,並且四個日志的hashcode都是一樣的,說明棧中只有一個實例。這是因為第一次進入的時候,棧中沒有該實例,則創建,後續的三次發現棧頂有這個實例,則直接復用,並且調用onNewIntent方法。那麼假設棧中有該實例,但是該實例不在棧頂情況又如何呢。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPs7Sw8fPyLTTTWFpbkFjdGl2aXR51tC9+Mjrtb1TaW5nbGVUb3BBY3Rpdml0eaOsyLu689TZzPjXqrW9T3RoZXJBY3Rpdml0edbQo6zU2bTTT3RoZXJBY3Rpdml0edbQzPi72FNpbmdsZVRvcEFjdGl2aXR5o6zU2bTTU2luZ2xlVG9wQWN0aXZpdHnM+LW9U2luZ2xlVG9wQWN0aXZpdHnW0KOsv7S/tNX7uPa5/bPMtcTI1da+oaM8L3A+DQo8cD48aW1nIGFsdD0="這裡寫圖片描述" src="/uploadfile/Collfiles/20151026/201510260841332.png" title="\" />

我們看到從MainActivity進入到SingleTopActivity時,新建了一個SingleTopActivity對象,並且task id與MainActivity是一樣的,然後從SingleTopActivity跳到OtherActivity時,新建了一個OtherActivity,此時task中存在三個Activity,從棧底到棧頂依次是MainActivity,SingleTopActivity,OtherActivity,此時如果再跳到SingleTopActivity,即使棧中已經有SingleTopActivity實例了,但是依然會創建一個新的SingleTopActivity實例,這一點從上面的日志的hashCode可以看出,此時棧頂是SingleTopActivity,如果再跳到SingleTopActivity,就會復用棧頂的SingleTopActivity,即會調用SingleTopActivity的onNewIntent方法。這就是上述日志的全過程。

對以上內容進行總結

standard啟動模式是默認的啟動模式,每次啟動一個Activity都會新建一個實例不管棧中是否已有該Activity的實例。 singleTop模式分3種情況
當當前棧中已有該Activity的實例並且該實例位於棧頂時,不會新建實例,而是復用棧頂的實例,並且會將Intent對象傳入,回調onNewIntent方法。 當當前棧中已有該Activity的實例但是該實例不在棧頂時,其行為和standard啟動模式一樣,依然會創建一個新的實例 當當前棧中不存在該Activity的實例時,其行為同standard啟動模式。 standard和singleTop啟動模式都是在原任務棧中新建Activity實例,不會啟動新的Task,即時你指定了taskAffinity屬性。

那麼什麼是taskAffinity屬性呢,可以簡單的理解為任務相關性。

這個參數標識了一個Activity所需任務棧的名字,默認情況下,所有Activity所需的任務棧的名字為應用的包名。 我們可以單獨指定每一個Activity的taskAffinity屬性覆蓋默認值 一個任務的affinity決定於這個任務的根activity(root activity)的taskAffinity。 在概念上,具有相同的affinity的activity(即設置了相同taskAffinity屬性的activity)屬於同一個任務。 為一個activity的taskAffinity設置一個空字符串,表明這個activity不屬於任何task。

很重要的一點taskAffinity屬性不對standard和singleTop模式有任何影響,即時你指定了該屬性為其他不同的值,這兩種啟動模式下不會創建新的task

我們指定之前的例子的taskAffinity分別為其他不同的值,入口Activity不指定(不指定即默認值,即包名)



 data-snippet-id=ext.3870fe3824a5400f186776e7330c225e data-snippet-saved=false data-codota-status=done>



分別啟動這兩個Activity看日志輸出
這裡寫圖片描述

我們看到入口Activity的taskAffinity值就是包名,就是默認情況下不指定的。然後StandardActivity和SingleTopActivity的taskAffinity值被我們覆蓋了,分別為不同的值,但是這兩個Activity啟動的時候任務棧並沒有新建,而是直接在原來的Task中啟動,這說明這個taskAffinity對這兩種啟動模式沒有什麼影響。其實該屬性主要是配合SingleTask啟動模式使用的。接下來我們看該啟動模式,可以說這個啟動模式是最復雜的。

singleTask,即棧內復用模式

這個模式十分復雜,有各式各樣的組合。在這個模式下,如果棧中存在這個Activity的實例就會復用這個Activity,不管它是否位於棧頂,復用時,會將它上面的Activity全部出棧,並且會回調該實例的onNewIntent方法。其實這個過程還存在一個任務棧的匹配,因為這個模式啟動時,會在自己需要的任務棧中尋找實例,這個任務棧就是通過taskAffinity屬性指定。
如果這個任務棧不存在,則會創建這個任務棧。

指定Activity為singleTask模式

 data-snippet-id=ext.7dc6c6c1e3bf6482f27caef77d76a7a9 data-snippet-saved=false data-codota-status=done>

現在我們不指定任何taskAffinity屬性,對它做類似singleTop的操作,即從入口MainActivity進入SingleTaskActivity,然後跳到OtherActivity,再跳回到SingleTaskActivity。看看整個過程的日志。

這裡寫圖片描述

當我們從MainActiviyty進入到SingleTaskActivity,再進入到OtherActivity後,此時棧中有3個Activity實例,並且SingleTaskActivity不在棧頂,而在OtherActivity跳到SingleTaskActivity時,並沒有創建一個新的SingleTaskActivity,而是復用了該實例,並且回調了onNewIntent方法。並且原來的OtherActivity出棧了,具體見下面的信息,使用命令adb shell dumpsys activity activities可進行查看

Running activities (most recent first):
TaskRecord{434c6d90 #77 A=cn.edu.zafu.lifecycle U=0 sz=2}
Run #2: ActivityRecord{4358c850 u0 cn.edu.zafu.lifecycle/.singleTask.SingleTaskActivity t77}
Run #1: ActivityRecord{43576720 u0 cn.edu.zafu.lifecycle/.MainActivity t77}

可以看到當前棧中只有兩個Activity,即原來棧中位於SingleTaskActivity 之上的Activity都出棧了。

我們看到使用singleTask啟動模式啟動一個Activity,它還是在原來的task中啟動。其實是這樣的,我們並沒有指定taskAffinity屬性,這說明和默認值一樣,也就是包名,當MainActivity啟動時創建的Task的名字就是包名,因為MainActivity也沒有指定taskAffinity,而當我們啟動SingleTaskActivity ,首先會尋找需要的任務棧是否存在,也就是taskAffinity指定的值,這裡就是包名,發現存在,就不再創建新的task,而是直接使用。當該task中存在該Activity實例時就會復用該實例,這就是棧內復用模式。

這時候,如果我們指定SingleTaskActivity 的taskAffinity值。



還是之前的操作。但是日志就會變得不一樣。

這裡寫圖片描述

我們看到SingleTaskActivity所屬的任務棧的TaskId發生了變換,也就是說開啟了一個新的Task,並且之後的OtherActivity也運行在了該Task上

打印出信息也證明了存在兩個不同的Task

Running activities (most recent first):
TaskRecord{441ab678 #79 A=cn.edu.zafu.lifecycle.singleTask U=0 sz=1}
Run #3: ActivityRecord{432fa178 u0 cn.edu.zafu.lifecycle/.singleTask.SingleTaskActivity t79}
TaskRecord{43ccd2a8 #78 A=cn.edu.zafu.lifecycle U=0 sz=1}
Run #2: ActivityRecord{42d5e7a8 u0 cn.edu.zafu.lifecycle/.MainActivity t78}

如果我們指定MainActivity的taskAffinity屬性和SingleTaskActivity一樣,又會出現什麼情況呢。

這裡寫圖片描述

沒錯,就是和他們什麼都不指定是一樣的。

這時候,就有了下面的結論

singleTask啟動模式啟動Activity時,首先會根據taskAffinity去尋找當前是否存在一個對應名字的任務棧
如果不存在,則會創建一個新的Task,並創建新的Activity實例入棧到新創建的Task中去 如果存在,則得到該任務棧,查找該任務棧中是否存在該Activity實例
如果存在實例,則將它上面的Activity實例都出棧,然後回調啟動的Activity實例的onNewIntent方法 如果不存在該實例,則新建Activity,並入棧

此外,我們可以將兩個不同App中的Activity設置為相同的taskAffinity,這樣雖然在不同的應用中,但是Activity會被分配到同一個Task中去

我們再創建另外一個應用,指定它的taskAffinity和之前的一樣,都是cn.edu.zafu.lifecycle.singleTask

 data-snippet-id=ext.2a85fc5c61692987f887b62d8f971259 data-snippet-saved=false data-csrftoken=2wu1oWya-HfKcIQWn2pJusfFmfj6GXqvoVl0 data-codota-status=done>        
        

然後啟動一個應用,讓他跳轉到該Activity後,再按home鍵後台,啟動另一個應用再進入該Activity,看日志

這裡寫圖片描述

我們看到,指定了相同的taskAffinity的SingleTaskActivity和OtherActivity被啟動到了同一個task中,taskId都為274。

singleInstance模式

該模式具備singleTask模式的所有特性外,與它的區別就是,這種模式下的Activity會單獨占用一個Task棧,具有全局唯一性,即整個系統中就這麼一個實例。以singleInstance模式啟動的Activity在整個系統中是單例的,如果在啟動這樣的Activiyt時,已經存在了一個實例,那麼會把它所在的任務調度到前台,重用這個實例。

增加一個Activity,聲明如下

                
                
            
         data-snippet-id=ext.45abd061bf5fadc9a880d13c41037be0 data-snippet-saved=false data-csrftoken=RN3AjodK-9XTCkyqgmQYO94ylFNd2SB7x45w data-codota-status=done>        
            
                
                
            
        

使用下面的方式分別在兩個應用中啟動它

Intent intent = new Intent();
intent.setAction(cn.edu.zafu.lifecycle);
startActivity(intent);

做的操作和上一次是一樣的,查看日志

這裡寫圖片描述

我們看到,第一個應用啟動SingleInstanceActivity時,由於系統中不存在該實例,所以新建了一個Task,按home鍵後,使用另一個App進入該Activity,由於系統中已經存在了一個實例,不會再創建新的Task,直接復用該實例,並且回調onNewIntent方法。可以從他們的hashcode中可以看出這是同一個實例。

對以上內容的總結就是

SingleInstance模式啟動的Activity在系統中具有全局唯一性。

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved