眾所周知當我們多次啟動同一個Activity時,系統會創建多個實例,并把它們按照先進后出的原則一一放入任務棧中,當我們按back鍵時,就會有一個activity從任務棧頂移除,重復下去,直到任務棧為空,系統就會回收這個任務棧。但是這樣以來,系統多次啟動同一個Activity時就會重復創建多個實例,這種做法顯然不合理,為了能夠優化這個問題,Android提供四種啟動模式來修改系統這一默認行為。
standard、singleTop、singleTask、singleInstance
接下來,我們一邊講理論一邊結合案例來全面學習這四種啟動模式。 為了方便打印,定義一個BaseActivity,在其onCreate方法和onNewIntent方法中打印出當前Activity的日志信息,主要包括所屬的task,當前類的hashcode,以及taskAffinity的值。之后我們進行測試的Activity都直接繼承該BaseActivity
public class BaseActivity extends Activity { @Override PRotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.i("owen", "onCreate method " + getClass().getSimpleName() + " taskId: " + getTaskId() + " hasCode: " + this.hashCode()); dumpTaskAffinity(); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); Log.i("owen", "onCreate method " + getClass().getSimpleName() + " taskId: " + getTaskId() + " hasCode: " + this.hashCode()); dumpTaskAffinity(); } protected void dumpTaskAffinity() { try { ActivityInfo info = this.getPackageManager().getActivityInfo(getComponentName(), PackageManager.GET_META_DATA); Log.i("owen", "taskAffinity " + info.taskAffinity); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } }}這個模式是默認的啟動模式,即標準模式,在不指定啟動模式的前提下,系統默認使用該模式啟動Activity,每次啟動一個Activity都會重寫創建一個新的實例,不管這個實例存不存在,這種模式下,誰啟動了該模式的Activity,該Activity就屬于啟動它的Activity的任務棧中。這個Activity它的onCreate(),onStart(),onResume()方法都會被調用。
對于standard模式,android:launchMode可以不進行聲明,因為默認就是standard。 StandardActivity 的代碼如下,入口MainActivity啟動會直接啟動該Activity,這個Activity中又有一個按鈕啟動StandardActivity。
public class ActivityStandard extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_standard); TextView jumpButton = (TextView) findViewById(R.id.standard); jumpButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(ActivityStandard.this, ActivityStandard.class); startActivity(intent); } }); }}我們首先進入StandardActivity,進入后再點擊進入Standard的按鈕,再按四次返回鍵不斷返回。
打印日志如下:
02-08 11:11:58.192 11356-11356/com.ownchan.activitystartmodel I/owen: onCreate method MainActivity taskId: 358 hasCode: 53114957402-08 11:11:58.193 11356-11356/com.ownchan.activitystartmodel I/owen: taskAffinitycom.ownchan.activitystartmodel02-08 11:11:58.217 11356-11356/com.ownchan.activitystartmodel I/owen: onCreate method ActivityStandard taskId: 358 hasCode: 66355035402-08 11:11:58.218 11356-11356/com.ownchan.activitystartmodel I/owen: taskAffinitycom.ownchan.activitystartmodel02-08 11:12:04.402 11356-11356/com.ownchan.activitystartmodel I/owen: onCreate method ActivityStandard taskId: 358 hasCode: 24776541602-08 11:12:04.402 11356-11356/com.ownchan.activitystartmodel I/owen: taskAffinitycom.ownchan.activitystartmodel02-08 11:12:05.026 11356-11356/com.ownchan.activitystartmodel I/owen: onCreate method ActivityStandard taskId: 358 hasCode: 102087387902-08 11:12:05.027 11356-11356/com.ownchan.activitystartmodel I/owen: taskAffinitycom.ownchan.activitystartmodel02-08 11:12:08.113 11356-11356/com.ownchan.activitystartmodel I/owen: onCreate method ActivityStandard taskId: 358 hasCode: 52216021002-08 11:12:08.113 11356-11356/com.ownchan.activitystartmodel I/owen: taskAffinitycom.ownchan.activitystartmodel02-08 11:12:11.323 11356-11356/com.ownchan.activitystartmodel I/owen: onCreate method ActivityStandard taskId: 358 hasCode: 21376698502-08 11:12:11.324 11356-11356/com.ownchan.activitystartmodel I/owen: taskAffinitycom.ownchan.activitystartmodel可以看到日志輸出了四次StandardActivity的和一次MainActivity的,從MainActivity進入StandardActivity一次,后來我們又按了三次按鈕,總共四次StandardActivity的日志,并且所屬的任務棧的id都是358,這也驗證了誰啟動了該模式的Activity,該Activity就屬于啟動它的Activity的任務棧中這句話,因為啟動StandardActivity的是MainActivity,而MainActivity的taskId是358,因此啟動的StandardActivity也應該屬于id為358的這個task,后續的3個StandardActivity是被StandardActivity這個對象啟動的,因此也應該還是358,所以taskId都是2087。并且每一個Activity的hashcode都是不一樣的,說明他們是不同的實例,即“每次啟動一個Activity都會重寫創建一個新的實例”
這個模式下,如果新的activity已經位于棧頂,那么這個Activity不會被重寫創建,同時它的onNewIntent方法會被調用,通過此方法的參數我們可以去除當前請求的信息。如果棧頂不存在該Activity的實例,則情況與standard模式相同。需要注意的是這個Activity它的onCreate(),onStart()方法不會被調用,因為它并沒有發生改變。
我們看到從MainActivity進入到SingleTopActivity時,新建了一個SingleTopActivity對象,并且task id與MainActivity是一樣的,然后從SingleTopActivity跳到OtherActivity時,新建了一個OtherActivity,此時task中存在三個Activity,從棧底到棧頂依次是MainActivity,SingleTopActivity,OtherActivity,此時如果再跳到SingleTopActivity,即使棧中已經有SingleTopActivity實例了,但是依然會創建一個新的SingleTopActivity實例,這一點從上面的日志的hashCode可以看出,此時棧頂是SingleTopActivity,如果再跳到SingleTopActivity,就會復用棧頂的SingleTopActivity,即會調用SingleTopActivity的onNewIntent方法。這就是上述日志的全過程。
當前棧中已有該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(如果不指定即默認值,即包名)
配置形式:
<activity android:name=".singleTask.SingleTaskActivity" android:launchMode="singleTask" >public class SingleTaskActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_single_top); TextView singleTop = (TextView) findViewById(R.id.single_top); TextView other = (TextView) findViewById(R.id.single_other); singleTop.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(SingleTaskActivity.this, SingleTaskActivity.class); startActivity(intent); } }); other.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(SingleTaskActivity.this, OtherTaskActivity.class); startActivity(intent); } }); }}public class OtherTaskActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_other_top); TextView jump = (TextView) findViewById(R.id.jump); jump.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(OtherTaskActivity.this, SingleTaskActivity.class); startActivity(intent); } }); }}現在我們先不指定任何taskAffinity屬性,對它做類似singleTop的操作,即從入口MainActivity進入SingleTaskActivity,然后跳到OtherActivity,再跳回到SingleTaskActivity。看看整個過程的日志。
02-08 15:56:07.446 4787-4787/com.ownchan.activitystartmodel I/owen: onCreate method MainActivity taskId: 397 hasCode: 53114957402-08 15:56:07.446 4787-4787/com.ownchan.activitystartmodel I/owen: taskAffinity com.ownchan.activitystartmodel02-08 15:56:07.568 4787-4787/com.ownchan.activitystartmodel I/owen: onCreate method SingleTaskActivity taskId: 397 hasCode: 98506543502-08 15:56:07.568 4787-4787/com.ownchan.activitystartmodel I/owen: taskAffinity com.ownchan.activitystartmodel02-08 15:56:22.997 4787-4787/com.ownchan.activitystartmodel I/owen: onNewIntent method SingleTaskActivity taskId: 397 hasCode: 98506543502-08 15:56:22.997 4787-4787/com.ownchan.activitystartmodel I/owen: taskAffinity com.ownchan.activitystartmodel02-08 15:56:28.876 4787-4787/com.ownchan.activitystartmodel I/owen: onCreate method OtherTaskActivity taskId: 397 hasCode: 6244982302-08 15:56:28.876 4787-4787/com.ownchan.activitystartmodel I/owen: taskAffinity com.ownchan.activitystartmodel02-08 15:56:32.439 4787-4787/com.ownchan.activitystartmodel I/owen: onNewIntent method SingleTaskActivity taskId: 397 hasCode: 98506543502-08 15:56:32.439 4787-4787/com.ownchan.activitystartmodel I/owen: taskAffinity com.ownchan.activitystartmodel當我們從MainActiviyty進入到SingleTaskActivity,再進入到OtherActivity后,此時棧中有3個Activity實例,并且SingleTaskActivity不在棧頂,而在OtherActivity跳到SingleTaskActivity時,并沒有創建一個新的SingleTaskActivity,而是復用了該實例,并且回調了onNewIntent方法。并且原來的OtherActivity出棧了,具體見下面的信息,使用命令adb shell dumpsys activity activities可進行查看
如果不存在,則會創建一個新的Task,并創建新的Activity實例入棧到新創建的Task中去 如果存在,則得到該任務棧,查找該任務棧中是否存在該Activity實例 如果存在實例,則將它上面的Activity實例都出棧,然后回調啟動的Activity實例的onNewIntent方法 如果不存在該實例,則新建Activity,并入棧
使用下面的方式分別在兩個應用中啟動它
Intent intent = new Intent();intent.setAction("com.castiel.demo.singleinstance");startActivity(intent);我們看到,第一個應用啟動SingleInstanceActivity時,由于系統中不存在該實例,所以新建了一個Task,按home鍵后,使用另一個App進入該Activity,由于系統中已經存在了一個實例,不會再創建新的Task,直接復用該實例,并且回調onNewIntent方法。可以從他們的hashcode中可以看出這是同一個實例。因此我們可以理解為:SingleInstance模式啟動的Activity在系統中具有全局唯一性。
新聞熱點
疑難解答