一、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方法。