1. 程式人生 > >activity的啟動模式有哪些?是什麼含義?(一)

activity的啟動模式有哪些?是什麼含義?(一)

Activity的四種啟動模式如下:

standard、singleTop、singleTask、singleInstance

我們一邊講理論一邊結合案例來全面學習這四種啟動模式。
為了列印方便,定義一個基礎BaseActivity,在其onCreate方法和onNewIntent方法中打印出當前Activity的日誌資訊,主要包括所屬的task,當前類的hashcode,之後我們進行測試的Activity都直接繼承該BaseActivity

public class BaseActivity extends AppCompatActivity {

    @Override
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.i("maweiqi", "*****onCreate()方法******"); Log.i("maweiqi", "onCreate:" + getClass().getSimpleName() + " TaskId: " + getTaskId() + " hasCode:" + this.hashCode()); dumpTaskAffinity(); } @Override
protected void onNewIntent(Intent intent) { super.onNewIntent(intent); Log.i("maweiqi", "*****onNewIntent()方法*****"); Log.i("maweiqi", "onNewIntent:" + getClass().getSimpleName() + " TaskId: " + getTaskId() + " hasCode:" + this.hashCode()); dumpTaskAffinity(); } protected
void dumpTaskAffinity(){ try { ActivityInfo info = this.getPackageManager() .getActivityInfo(getComponentName(), PackageManager.GET_META_DATA); Log.i("maweiqi", "taskAffinity:"+info.taskAffinity); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } } }

standard-預設模式

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

配置形式:

<activity android:name=".SecondActivity" android:launchMode="standard"/>

使用案例:

對於standard模式,android:launchMode可以不進行宣告,因為預設就是standard。 SecondActivity的程式碼如下,入口MainActivity中有一個按鈕進入該Activity,這個Activity中又有一個按鈕啟動SecondActivity。

public class MainActivity extends BaseActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Button btn_standard= (Button)   findViewById(R.id.btn_standard);
    btn_standard.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
       Intent intent = new Intent(MainActivity.this, SecondActivity.class);
       startActivity(intent);
    }
   });
        Log.i("maweiqi", "*****onCreate()方法******");
        Log.i("maweiqi", "onCreate:" + getClass().getSimpleName() + " TaskId: " + getTaskId() + " hasCode:" + this.hashCode());
    }
}
public class SecondActivity extends BaseActivity {
    private Button jump;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

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

}

我們首先從MainActivity進入SecondActivity ,進入後再點選進入SecondActivity,連續進入四次SecondActivity 。

輸出的日誌如下:

  MainActivity TaskId: 2 hasCode:1249333352
SecondActivity TaskId: 2 hasCode:1249526392
SecondActivity TaskId: 2 hasCode:1249424816
SecondActivity TaskId: 2 hasCode:1249439692
SecondActivity TaskId: 2 hasCode:1249459968

可以看到日誌輸出了四次SecondActivity的和一次MainActivity的,從MainActivity進入StandardActivity一次,後來我們又按了三次按鈕,總共四次SecondActivity的日誌,並且所屬的任務棧的id都是2,這也驗證了誰啟動了該模式的Activity,該Activity就屬於啟動它的Activity的任務棧中這句話.因為啟動SecondActivity的是MainActivity,而MainActivity的taskId是2,因此啟動的SecondActivity也應該屬於id為2的這個task,後續的3個SecondActivity是被SecondActivity這個物件啟動的,因此也應該還是2,所以taskId都是2。並且每一個Activity的hashcode都是不一樣的,說明他們是不同的例項,即“每次啟動一個Activity都會重寫建立一個新的例項”

singleTop模式

這個模式下,如果新的activity已經位於棧頂,那麼這個Activity不會被重新建立,同時它的onNewIntent方法會被呼叫,通過此方法的引數我們可以去除當前請求的資訊。如果棧頂不存在該Activity的例項,則情況與standard模式相同。需要注意的是這個Activity它的onCreate(),onStart()方法不會被呼叫,因為它並沒有發生改變。

配置形式:

<activity android:name=".SingleTopActivity" android:launchMode="singleTop"/>
<activity android:name=".OtherTopActivity" android:launchMode="singleTop"/>

使用案例:

public class SingleTopActivity extends BaseActivity {
    private Button jump, jump2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_singletop);

        jump = (Button) findViewById(R.id.button);
        jump2 = (Button) findViewById(R.id.button2);
        jump.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(SingleTopActivity.this, SingleTopActivity.class);
                startActivity(intent);
            }
        });
        jump2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(SingleTopActivity.this, OtherTopActivity.class);
                startActivity(intent);
            }
        });
        Log.i("maweiqi", "*****onCreate()方法******");
        Log.i("maweiqi", "onCreate:" + getClass().getSimpleName() + " TaskId: " + getTaskId() + " hasCode:" + this.hashCode());
    }
}
public class OtherTopActivity extends AppCompatActivity {
    private Button jump;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_other);

        jump= (Button) findViewById(R.id.btn_other);
        jump.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(OtherTopActivity.this, SingleTopActivity.class);
                startActivity(intent);
            }
        });
        Log.i("maweiqi", "*****onCreate()方法******");
        Log.i("maweiqi", "onCreate:" + getClass().getSimpleName() + " TaskId: " + getTaskId() + " hasCode:" + this.hashCode());
    }
}

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

onCreateMainActivity TaskId: 3 hasCode:1249332216
onCreateSingleTopActivity TaskId: 3 hasCode:1249464444
onNewIntentSingleTopActivity TaskId: 3 hasCode:1249464444
onNewIntentSingleTopActivity TaskId: 3 hasCode:1249464444
onNewIntentSingleTopActivity TaskId: 3 hasCode:1249464444

我們看到,除了第一次進入SingleTopActivity這個Activity時,輸出的是onCreate方法中的日誌,後續的都是呼叫了onNewIntent方法,並沒有呼叫onCreate方法,並且四個日誌的hashcode都是一樣的,說明棧中只有一個例項。這是因為第一次進入的時候,棧中沒有該例項,則建立,後續的三次發現棧頂有這個例項,則直接複用,並且呼叫onNewIntent方法。那麼假設棧中有該例項,但是該例項不在棧頂情況又如何呢?
我們先從MainActivity中進入到SingleTopActivity,然後再跳轉到OtherActivity中,再從OtherActivity中跳回SingleTopActivity,再從SingleTopActivity跳到SingleTopActivity中,看看整個過程的日誌。

輸出的日誌如下:

onCreateSingleTopActivity TaskId: 4 hasCode:1249520904
onCreateOtherTopActivity TaskId: 4 hasCode:1249420244
onCreateSingleTopActivity TaskId: 4 hasCode:1249448776
onCreateSingleTopActivity TaskId: 4 hasCode:1249448776
onNewIntentSingleTopActivity TaskId: 4 hasCode:1249448776

我們看到從MainActivity進入到SingleTopActivity時,新建了一個SingleTopActivity物件,然後從SingleTopActivity跳到OtherActivity時,新建了一個OtherActivity,此時task中存在三個Activity,從棧底到棧頂依次是MainActivity,SingleTopActivity,OtherActivity,此時如果再跳到SingleTopActivity,即使棧中已經有SingleTopActivity例項了,但是依然會建立一個新的SingleTopActivity例項,這一點從上面的日誌的hashCode可以看出,此時棧頂是SingleTopActivity,如果再跳到SingleTopActivity,就會複用棧頂的SingleTopActivity,即會呼叫SingleTopActivity的onNewIntent方法。這就是上述日誌的全過程。

對以上內容進行總結

standard啟動模式是預設的啟動模式,每次啟動一個Activity都會新建一個例項不管棧中是否已有該Activity的例項。

singleTop模式分3種情況

1)當前棧中已有該Activity的例項並且該例項位於棧頂時,不會新建例項,而是複用棧頂的例項,並且會將Intent物件傳入,回撥onNewIntent方法

2)當前棧中已有該Activity的例項但是該例項不在棧頂時,其行為和standard啟動模式一樣,依然會建立一個新的例項

3)當前棧中不存在該Activity的例項時,其行為同standard啟動模式standard和singleTop啟動模式都是在原任務棧中新建Activity例項,不會啟動新的Task

singleTask模式

在這個模式下,如果棧中存在這個Activity的例項就會複用這個Activity,不管它是否位於棧頂,複用時,會將它上面的Activity全部出棧,並且會回撥該例項的onNewIntent方法。

配置形式:

<activity android:name=".SingleTaskActivity" android:launchMode="singleTask"/>
<activity android:name=".OtherTaskActivity" android:launchMode="singleTask"/>

使用案例:

public class SingleTaskActivity extends BaseActivity {
    private Button jump,jump2;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_task);

        jump = (Button) findViewById(R.id.btn_task);
        jump2 = (Button) findViewById(R.id.btn_other);
        jump.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(SingleTaskActivity.this, SingleTaskActivity.class);
                startActivity(intent);
            }
        });
        jump2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(SingleTaskActivity.this, OtherTaskActivity.class);
                startActivity(intent);
            }
        });
    }
}
public class OtherTaskActivity extends BaseActivity {
    private Button jump;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_other_task);

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

日誌輸出

onCreateMainActivity TaskId: 5 hasCode:1249321980
onCreateSingleTaskActivity TaskId: 5 hasCode:1249515136
onCreateOtherTaskActivity TaskId: 6 hasCode:1249386172
onNewIntentSingleTaskActivity TaskId: 6 hasCode:1249513244

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

Running activities (most recent first):
      TaskRecord{3c727e #11 A=com.maweiqi.task U=0 sz=2}
        Run #1: ActivityRecord{5a00d1e u0 com.maweiqi.task/.SingleTaskActivity t11}
        Run #0: ActivityRecord{2dce0b u0 com.maweiqi.task/.MainActivity t11}

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

singleInstance-全域性唯一模式

該模式具備singleTask模式的所有特性外,與它的區別就是,這種模式下的Activity會單獨佔用一個Task棧,具有全域性唯一性,即整個系統中就這麼一個例項,由於棧內複用的特性,後續的請求均不會建立新的Activity例項,除非這個特殊的任務棧被銷燬了。以singleInstance模式啟動的Activity在整個系統中是單例的,如果在啟動這樣的Activiyt時,已經存在了一個例項,那麼會把它所在的任務排程到前臺,重用這個例項。

singleInstance示例一配置形式:

<activity android:name=".MainActivity" 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=".SingleInstanceActivity" android:launchMode="singleInstance"/>
public class MainActivity extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button btn_standard= (Button) findViewById(R.id.btn_standard);
        btn_standard.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, SingleInstanceActivity.class);
                startActivity(intent);
            }
        });

    }
}
public class SingleInstanceActivity extends BaseActivity  {
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_instance);
        Button button4 = (Button) findViewById(R.id.button4);
        button4.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(SingleInstanceActivity.this, MainActivity.class);
                startActivity(intent);
            }
        });
    }
}

MainActivity進入SingleInstanceActivity,在從SingleInstanceActivity 進入MainActivity,在從MainActivity進入SingleInstanceActivity

日誌輸出

onCreateMainActivity TaskId: 12 hasCode:201331476
onCreateSingleInstanceActivity TaskId: 13 hasCode:57987178
onCreateMainActivity TaskId: 12 hasCode:254253633
onNewIntentSingleInstanceActivity TaskId: 13 hasCode:57987178

測試結果

1)兩個SingleInstanceActivity是同一個例項。

2) 第一次進入的MainActivity和第一次進入的SingleInstanceActivity位於不同的task中。

3) 兩個MainActivity是位於同一個task中的不同例項。

4)這個結論與預期是相同的,即,singleInstance型別的Activity的例項只能有一個,而且它只允許存在於單獨的一個task中。

5)至於為什麼兩個MainActivity是位於同一個task中的不同例項,那是因為它是standard型別的,我們可以將ActivityTest修改為singleTop等其他型別進行測試。

singleInstance示例二配置形式:

MainActivity的模式改為”singleTop”,修改後的manifest如下:

<activity android:name=".MainActivity" android:launchMode="singleTop">
    <intent-filter>
       <action android:name="android.intent.action.MAIN"/>

      <category android:name="android.intent.category.LAUNCHER"/>
     </intent-filter>
</activity>
 <activity android:name=".SingleInstanceActivity" android:launchMode="singleInstance"/>

MainActivity進入SingleInstanceActivity,在從SingleInstanceActivity 進入MainActivity,在從MainActivity進入SingleInstanceActivity

日誌輸出

onCreateMainActivity TaskId: 15 hasCode:201331476
onCreateSingleInstanceActivity TaskId: 16 hasCode:57987178
onNewIntentMainActivity TaskId: 15 hasCode:201331476
onNewIntentSingleInstanceActivity TaskId: 16 hasCode:57987178

測試結果

1)兩個SingleInstanceActivity是同一個例項。

2)第一次進入的MainActivity和第一次進入的SingleInstanceActivity位於不同的task中。

3)兩個MainActivity是同一個例項。

launchMode模式總結

1. standard

在該模式下,Activity可以擁有多個例項,並且這些例項既可以位於同一個task,也可以位於不同的task。

2.singleTop

該模式下,在同一個task中,如果存在該Activity的例項,並且該Activity例項位於棧頂(即,該Activity位於前端),則呼叫startActivity()時,不再建立該Activity的示例;而僅僅只是呼叫Activity的onNewIntent()。否則的話,則新建該Activity的例項,並將其置於棧頂。

3. singleTask

只容許有一個包含該Activity例項的task存在!
總的來說:singleTask的結論與android:taskAffinity相關(下章在講),以A啟動B來說

1) 當A和B的taskAffinity相同時:第一次建立B的例項時,並不會啟動新的task,而是直接將B新增到A所在的task;否則,將B所在task中位於B之上的全部Activity都刪除,然後跳轉到B中。
2) 當A和B的taskAffinity不同時:第一次建立B的例項時,會啟動新的task,然後將B新增到新建的task中;否則,將B所在task中位於B之上的全部Activity都刪除,然後跳轉到B中。

4. singleInstance

顧名思義,是單一例項的意思,即任意時刻只允許存在唯一的Activity例項,而且該Activity所在的task不能容納除該Activity之外的其他Activity例項!
它與singleTask有相同之處,也有不同之處。
相同之處:任意時刻,最多隻允許存在一個例項。
不同之處:
1) singleTask受android:taskAffinity屬性的影響,而singleInstance不受android:taskAffinity的影響。
2) singleTask所在的task中能有其它的Activity,而singleInstance的task中不能有其他Activity。
3) 當跳轉到singleTask型別的Activity,並且該Activity例項已經存在時,會刪除該Activity所在task中位於該Activity之上的全部Activity例項;而跳轉到singleInstance型別的Activity,並且該Activity已經存在時,不需要刪除其他Activity,因為它所在的task只有該Activity唯一一個Activity例項。

  • 歡迎關注微信公眾號,長期推薦技術文章和技術視訊

  • 微信公眾號名稱:Android乾貨程式設計師