一、Activity 生命周期
生命周期图示
生命周期方法
- onCreate() : Activity 首次创建时会触发此方法,如果 Activity 触发了 onDestory() 方法后如果再次打开这个 Activity 则又会重走此方法创建。它的参数 savedInstanceState在 Activity 完全新建时是个空的,但是当我们因为某些情况打断了 Activity 的显示,比如说突然来电进入到接电话的界面。如果接完电话我们的 Activity 没被销毁则直接回到接电话前的状态了,如果接完电话因为长时间处于后台,我们的 Activity 已经被系统回收了那我们回来就会重新触发 onCreate 重建,之前我们正在填写的内容就会被清除掉。为了解决这个问题所以引入了这个参数,我们可以重写 onSaveInstanceState 这个方法来在系统非用户退出销毁 Activity 时触发保存值,然后 onCreate 时就可以拿到对应数据进行恢复了
- onRestart(): 这个方法不是 Activity 生命周期主线方法,他在 ActivityB 返回 ActivityA时,如果ActivityA 在栈内未被回收,ActivityA 会被触发其 onRestart 然后再触发 onStart
- onStart() : Activity 在屏幕上对用户可见时调用,这个时候 Activity 是还没拿到焦点的
- onResume(): Activity 获得焦点时调用
- onPause(): Activity 失去焦点时调用,当打开新的 Activity 或者打开弹窗抢夺了焦点时当前 Activity 会先调用 onPause 触发失去焦点
- onStop(): Activity 跳转新的Activity后,对用户完全不可见。新的 Activity走完 onCreate、onStart 方法后
- onDestory: Activity 被销毁时触发,走了这个方法 Activity的生命周期就宣告结束了
打开一个Activity,再返回生命周期流程:
几个循环
Activity 的生命周期大致可以分为以下几个循环场景
可见循环
可见循环如下图所示,ActivityA 上面弹出 Dialog 然后,关闭 Dialog ActivityA 重新获得焦点回到可交互状态,这种情况 ActivityA 的生命周期将走 onPause -> onResume 的循环。
不可见循环
从 MainActivity 中打开 RedActivity,Main(onPause)-> Red(onCreate)->Red(onStart)->Red(onResume)->Main(onStop)
从 RedActivity 中返回 MainActivity,Red(onPause)-> Main(onResetart)->Main(onStart)->Main(onResume)->Red(onStop)->Red(onDestory)
当打开一个全屏非透明 Activity时,打开再关闭的日志打印如下
重走生命周期
当 Activity 处于停止状态是,系统会根据需要进行回收。当非栈顶的 Activity 被回收后,如果要再返回到那个 Activity,那么系统就会重新创建一个新的 Activity。这里又跟前面的 onCreate 中恢复状态进行了闭环。
二、Activity 启动模式
静态启动模式
Android 中的启动模式在 Android 12 之前有四种,standard、singleTop、singleInstance、singleTask,Android 12之后新增了 singleInstancePerTask 启动模式
standard
standard 启动模式是 Activity 的默认启动模式,每次都会创建一个 Activity 实例入栈
入栈动画
back 出栈动画
singleTop
singelTop 模式分为两种情况,如果 ActivityB 是 singleTop 模式,当要启动的 activity正好又是 ActivityB,那么不会创建新的实例而是会复用当前栈顶的实例。如果当前栈顶的实例不是ActivityB,则会穿件一个新的 ActivityB进行入栈,栈内就会有两个 ActivityB
入栈 Activity 不在栈顶时:
入栈 Activity 在栈顶时:
singleTask
栈内单例模式表示栈内只能存在一个当前 ActivityA 的实例,当栈内无 ActivityA 时,他跟 standard 一样,正常创建新实例并入栈。当栈内有 ActivityA 时,他会把栈内 ActivityA 上面的 Activity全部出栈然后复用 ActivityA
栈内单例模式的入栈逻辑如下:
动画示意图:
singleInstance
singleInstance 模式可以理解为全局的单例模式,当 Activity 被标记为 singleInstance 模式启动时系统会把它放入一个独立的栈内,全局维护一个唯一的 Activity。当需要打开这个 Activity 时会直接栈内复用这个 Activity。
singleInstancePerTask
改模式为 Android 12 增加,具有以下特点: 1、当目标栈的底部为当前Activity则其栈与生命周期的变化与singleTask模式一样。 2、当目标栈不存在此 Activity 时其栈与生命周期的变化与 singleInstance模式一样,创建一个新栈把 Activity 作为根 Activity。
taskAffinity 属性
用于指定 Activity 的任务栈,多个应用程序使用同样的 taskAffinity + singleInstance 模式可以实现多个应用共享 Activity页面。singleInstance 模式启动,不管是否指定 taskAffinity 都会创建新的 task。
动态 FLAG
通过在启动 Activity 时给 intent 添加 Flag 方式动态指定启动模式
FLAG_ACTIVITY_CLEAR_TOP
同于 mainfest 中配置的 singleTask,如果栈内存在,会清空上面的 Activity。
FLAG_ACTIVITY_SINGLE_TOP
同于manifest 中配置的 singleTop
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
等同于 AndroidManifest 中设置了 excludeFromRecents="true",Task 不会出现在最近任务列表中
FLAG_ACTIVITY_NO_HISTORY
等同于 AndroidManifest 中设置了noHistory="true",这个Activity 不会放入栈中,从这个页面离开了就销毁了
FLAG_ACTIVITY_NEW_TASK
这个属性,需要 taskAffinity 与 APP 的包名不一样才能生效。会启动一个新的 Task 来放入 Activity
Activity 的最佳实践
封装 BaseActivity
我们在开发应用时一般会创建一个继承自AndroidSDK 提供的 Activity、FragmentActivity、AppCompatActivity 等的 BaseActivity类,在这个 Base类中我们可以定义一些公共的操作和管理
abstract class BaseActivity<P : BasePresenter<*>, VB : ViewBinding> : BaseSkinActivity(), BaseView {
protected lateinit var mPresenter: P
protected lateinit var viewBinding: VB
private var clazzVB: Class<VB>
init {
val type = javaClass.genericSuperclass as java.lang.reflect.ParameterizedType
clazzVB = type.actualTypeArguments[1] as Class<VB>
}
override fun onCreate(savedInstanceState: Bundle?) {
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O && isTranslucentOrFloating(this)) {
fixOrientation()
}
super.onCreate(savedInstanceState)
setWindowSkinBackgroundDrawable(R.drawable.color_bg_img_1)
viewBinding = inflateViewBinding()
setContentView(viewBinding.root)
AutoUtils.auto(window.decorView)
bindViews()
mPresenter = createPresenter()
mPresenter.attachView(this)
initEventAndData()
}
private fun isTranslucentOrFloating(context: Context): Boolean {
var isTranslucentOrFloating = false
try {
val styleableRes = Class.forName("com.android.internal.R$styleable").getField("Window")[null] as IntArray
val ta: TypedArray = context.obtainStyledAttributes(styleableRes)
val m: Method = ActivityInfo::class.java.getMethod("isTranslucentOrFloating", TypedArray::class.java)
m.isAccessible = true
isTranslucentOrFloating = m.invoke(null, ta) as Boolean
m.isAccessible = false
} catch (e: Exception) {
e.printStackTrace()
}
return isTranslucentOrFloating
}
private fun fixOrientation(): Boolean {
try {
val field: Field = Activity::class.java.getDeclaredField("mActivityInfo")
field.isAccessible = true
val o: ActivityInfo = field[this] as ActivityInfo
o.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
field.isAccessible = false
return true
} catch (e: Exception) {
e.printStackTrace()
}
return false
}
override fun onResume() {
super.onResume()
DataReporter.appop.onResume(this)
}
override fun onStart() {
super.onStart()
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
override fun onStop() {
super.onStop()
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
DataReporter.appop.onStop(this)
}
override fun onDestroy() {
super.onDestroy()
mPresenter.detachView()
}
protected abstract fun initEventAndData()
protected abstract fun createPresenter(): P
/**
* 原有 ButterKnife结构,有一些自定义的注解因此不能直接替换,保留原来的 View 属性定义和自定义注解
* 在此方法统一使用 ViewBinding 的方式进行 View 的绑定
*/
protected abstract fun bindViews()
private fun inflateViewBinding(): VB {
val inflateMethod = clazzVB.getMethod("inflate", LayoutInflater::class.java)
return inflateMethod.invoke(null, layoutInflater) as VB
}
}
也可以在这个 Activity 的各种生命周期方法中去实现公用的日志打印
维护 Activity 栈
如果目前你手机的界面还停留在ThirdActivity,你会发现当前想退出程序是非常不方便的,需 要连按3次Back键才行。按Home键只是把程序挂起,并没有退出程序。如果我们的程序需要 注销或者退出的功能该怎么办呢?看来要有一个随时随地都能退出程序的方案才行。上面已经说了可以在 BaseActivity 的生命周期中添加公共方法,那么我们也可以在 BaseActivity 的生命周期中进行入栈、出栈操作来完成自己 Activity 栈的维护
object ActivityCollector {
private val activities = ArrayList<Activity>()
fun addActivity(activity: Activity) {
activities.add(activity)
}
fun removeActivity(activity: Activity) {
activities.remove(activity)
}
fun finishAll() {
for (activity in activities) {
if (!activity.isFinishing) {
activity.finish()
}
}
activities.clear()
}
}
open class BaseActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("BaseActivity", javaClass.simpleName)
ActivityCollector.addActivity(this)
}
override fun onDestroy() {
super.onDestroy()
ActivityCollector.removeActivity(this)
}
}
当然我们也可以直接定义一个父 Activity 来标识这些 Activity 都是某某业务的,在完成之后直接循环判断是这个父 Activity 的子类全部出栈,方法多种多样。
启动实践
写 Activity 时对外提供静态的启动方法,把自己 Activity 启动需要的参数条件暴露出去,这样的好处是不需要关心外部启动时约定的 key 对不对得上。别人只需要调用你提供的方法来启动 Activity 就行。