一、Activity的四種啟動模式:
1、standard(標準模式):一個定義Activity在mainfest中不設置Android:launchMode=“standard”,也會默認為standard,standard就是新建一個Activity就在棧中新建一個Activity實例。
2、singleTop(棧頂復用模式):在mainfest中設置singleTop模式時,當前棧頂如果有一個相同的Activity,就不創建而復用棧頂的那個,只要創建新的和棧頂相同的Activity才會復用,復用的Activity就回調onNewIntent方法。
3、singleTask(棧內單例模式):當前棧內只有一個Activity實例,棧內已存activity實例,在其他Activity中開啟這個Activity,Android直接把這個實例棧上面的其他Activity實例踢出棧GC掉。
4、singleInstace(堆內單例):設置該模式的Activity實例存在一個單獨的任務棧中,整個系統獨立的。
設置了singleTop、singleTask、singleInstance這三種模式的Activity,如果開啟一個新的Activity頁面,棧頂存在相同的實例就復用,都不會重新創建一個新實例,Activity復用后都會調用onNewIntent(Intent intent)方法。
測試demo例子:
在mainfest清單文件中定義這些Activity
<activity android:name=".lauchmode.LaunchModeActivity"
android:launchMode="singleTask"/>
<activity
android:name=".lauchmode.StandardActivity"
android:launchMode="standard" />
<activity
android:name=".lauchmode.SingleTopActivity"
android:launchMode="singleTop" />
<activity
android:name=".lauchmode.SingleTaskActivity"
android:launchMode="singleTask"/>
<activity android:name=".lauchmode.SingleInstanceActivity"
android:launchMode="singleInstance"/>
class LaunchModeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityLaunchModeBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.btStandard.setOnClickListener {
startActivity(Intent(this, StandardActivity::class.JAVA))
}
binding.btSingleTop.setOnClickListener {
startActivity(Intent(this, SingleTopActivity::class.java))
}
binding.btSingleTask.setOnClickListener {
startActivity(Intent(this, SingleTaskActivity::class.java))
}
}
}
以下就是LaunchModeActivity頁面,下面按鈕分別跳轉到StandardActivity 、SingleTopActivity、SingleInstanceActivity、SingleTaskActivity頁面。
class StandardActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityStandardBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.btStandard.setOnClickListener {
startActivity(Intent(this, StandardActivity::class.java))
}
binding.btSingleTop.setOnClickListener {
startActivity(Intent(this, SingleTopActivity::class.java))
}
binding.btSingleTask.setOnClickListener {
startActivity(Intent(this, SingleTaskActivity::class.java))
}
binding.btSingleInstance.setOnClickListener {
startActivity(Intent(this, SingleInstanceActivity::class.java))
}
}
}
class SingleTopActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivitySingleTopBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.btStandard.setOnClickListener {
startActivity(Intent(this, StandardActivity::class.java))
}
binding.btSingleTop.setOnClickListener {
startActivity(Intent(this, SingleTopActivity::class.java))
}
binding.btSingleTask.setOnClickListener {
startActivity(Intent(this, SingleTaskActivity::class.java))
}
binding.btSingleInstance.setOnClickListener {
startActivity(Intent(this, SingleInstanceActivity::class.java))
}
}
}
class SingleTaskActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivitySingleTaskBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.btStandard.setOnClickListener {
startActivity(Intent(this, StandardActivity::class.java))
}
binding.btSingleTop.setOnClickListener {
startActivity(Intent(this, SingleTopActivity::class.java))
}
binding.btSingleTask.setOnClickListener {
startActivity(Intent(this, SingleTaskActivity::class.java))
}
binding.btSingleInstance.setOnClickListener {
startActivity(Intent(this, SingleInstanceActivity::class.java))
}
}
}
class SingleInstanceActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivitySingleInstanceBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.btStandard.setOnClickListener {
startActivity(Intent(this, StandardActivity::class.java))
}
binding.btSingleTop.setOnClickListener {
startActivity(Intent(this, SingleTopActivity::class.java))
}
binding.btSingleTask.setOnClickListener {
startActivity(Intent(this, SingleTaskActivity::class.java))
}
binding.btSingleInstance.setOnClickListener {
startActivity(Intent(this, SingleInstanceActivity::class.java))
}
}
}
StandardActivity 、SingleTopActivity、SingleInstanceActivity、SingleTaskActivity這些Activity的頁面都是下圖,方便測試
查看task棧情況指令: adb shell dumpsys activity
1、singleTop模式測試:
開啟Activity的順序是StandardActivity---->SingleTopActivity---->SingleTopActivity,查看任務棧可發現,本來是開啟兩個SingleTopActivity頁面的,但是棧頂只有一個實例。
2、singleTask模式測試:
開啟Activity的順序是SingleTaskActivity---->StandardActivity---->SingleTopActivity---->SingleTaskActivity,如下圖任務棧可發現,本來一共開啟了四個Activity,最后只剩下一個SingleTaskActivity,所以設置了singleTask模式的Activity,在一個任務棧中只能有一個實例,棧頂不管開多少個Activity,只要打開設置了singleTask的Activity后,該Activity上面的Activity都會銷毀回收掉。
3、singleInstance模式測試:
開啟Activity的順序是StandardActivity---->SingleInstanceActivity---->StandardActivity,然后關閉頁面順序是這樣的StandardActivity—>StandardActivity---->SingleInstanceActivity,最后關閉的頁面即然是SingleInstanceActivity,如果再關閉SingleInstanceActivity頁面就回到手機的桌面了。所有這個模式有點特別,設置了該模式Activity存在在一個單獨的任務棧中。如下圖:
4、taskAffinity屬性:
taskAffinity屬性和Activity的啟動模式息息相關,而且taskAffinity屬性比較特殊,在普通的開發中也是鮮有遇到,但是在有些特定場景下卻有著出其不意的效果。
taskAffinity是Activity在mainfest中配置的一個屬性,暫時可以理解為:taskAffinity為宿主Activity指定了存放的任務棧[不同于App中其他的Activity的棧],為activity設置taskAffinity屬性時不能和包名相同,因為Android團隊為taskAffinity默認設置為包名任務棧。
taskAffinity只有和SingleTask啟動模式匹配使用時,啟動的Activity才會運行在名字和taskAffinity相同的任務棧中。
5、Intent中標志位設置啟動模式:
四種模式可以使用代碼中設置,通過Intent.setFlags(int flags)設置啟動模式。
FLAG_ACTIVITY_CLEAR_TOP : 等同于mainfest中配置的singleTask。
FLAG_ACTIVITY_SINGLE_TOP: 同樣等同于mainfest中配置的singleTop。
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS: 其對應在AndroidManifest中的屬性為android:excludeFromRecents=“true”,當用戶按了“最近任務列表”時候,該Task不會出現在最近任務列表中,可達到隱藏應用的目的。
FLAG_ACTIVITY_NO_HISTORY: 對應在AndroidManifest中的屬性為:android:noHistory=“true”,這個FLAG啟動的Activity,一旦退出,它不會存在于棧中。
6、設置FLAG_ACTIVITY_NEW_TASK屬性:
這個屬性需要在被開啟的目標Activity在AndroidManifest.xml文件配置taskAffinity的值(必須和startActivity發其者Activity的包名不一樣,如果是跳轉另一個App的話可以taskAffinity可以省略),則會在新標記的Affinity所存在的taskAffinity中壓入這個Activity。
如下面跳轉到微信頁面的代碼中,設置FLAG_ACTIVITY_NEW_TASK后,跳轉到微信的時候會開啟一個新的任務棧存放微信的頁面,如果不設置就回加入到當前的任務棧中
try {
val intent = Intent(Intent.ACTION_MAIN)
val cmp = ComponentName("com.tencent.mm","com.tencent.mm.ui.LauncherUI")
intent.addCategory(Intent.CATEGORY_LAUNCHER)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.component = cmp
startActivity(intent)
} catch (e: ActivityNotFoundException) {
Toast.makeText(this,"檢查到您手機沒有安裝微信,請安裝后使用該功能", Toast.LENGTH_SHORT).show()
}
如果去掉上面intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)這條代碼,微信的頁面就回加入到自己app的任務棧中。
7、Intent屬性:
在Android中Intent是在四大組件之間進行交互與通訊,也可以在應用之間通訊。其底層的通信是以Binder機制實現的,在物理層則是通過共享內存的方式實現的。Intent的屬性有:component(組件)、action、category、data、type、extras、flags;所有的屬性也是各顯神通,滿足開發者的各種需要滿足不同場景。
component: 顯然就是設置四大組件的,將直接使用它指定的組件,借助這一屬性可以實現不同應用組件之間通訊。
action: 是一個可以指定目標組件行為的字符串,開發人員可以自定義action通過匹配action實現組件之間的隱士跳轉,當然Android系統也已經預定部分String作為系統應用Action,例如打開系統設置頁面等等。
data: 通常是URI類型或者MIME類型格式定義的操作數據;表示與動作要操縱的數據。
Category: 屬性用于指定當前動作(Action)被執行的環境。
type: 對于data范例的描寫。
extras:extras和flags 這兩個太熟悉了就不在重復。
8、Activity的onSaveInstanceState和onRestoreInstanceState方法:
在Android系統內存不足時,同時Activity失去焦點后被系統給回收后,Activity 再次被創建時,通過onSaveInstanceState 和onRestoreInstanceState使用Bundle來存儲恢復數據,例如屏幕的橫豎屏切換時,就會先調用onSaveInstanceState方法,切換屏幕后的頁面就會調用onRestoreInstanceState方法。