持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情
故事背景
在日常开发中多次遇到有与AC
和Fr
相关开发问题,例如资源释放时机经常会放在AC
的onDestory
方法或是Stop
中,这就是问题所在。同一个资源在原有AC
持有状态下当新AC
也需要持有该资源时,该资源是在原有AC
关闭时是先释放还是等待新AC
创建后再释放,不同逻辑判断就会有不同结果。因此理解好生命周期才能正确使用好一个单例资源。
Activity和Fragment生命周期
启动Activity和Fragment
当一个Actvity
嵌套Fragment
它们初始化时日志如下:
<UI> 我是第一个AC: onCreate
<UI> 我是第一个AC的Fragment: onCreate
<UI> 我是第一个AC的Fragment: onCreateView
<UI> 我是第一个AC的Fragment: onStart
<UI> 我是第一个AC: onStart
<UI> 我是第一个AC: onResume
关闭Activity和Fragment
关闭Actvity
和Fragment
时会发现内部Fragment
先执行,Actvity
是后执行
<UI> 我是第一个AC的Fragment: onPause
<UI> 我是第一个AC: onPause
<UI> 我是第一个AC的Fragment: onStop
<UI> 我是第一个AC: onStop
<UI> 我是第一个AC的Fragment: onDestroy
<UI> 我是第一个AC: onDestroy
因此需要注意一些资源释放放置位置需要注意。
当前页面启动新Activity和Fragment
在当前Actvity
启动新的Actvity
,过程是先finish
掉自己在启动新页面。代码逻辑如下:
requireActivity().finish()
startActivity(Intent(context, UILifeCycleAc::class.java).also {
it.putExtra("title", "${requireActivity().title}")
it.putExtra("name", "我是新的AC")
})
PS:同时试过先执行finish再启动新页面还是先启动新页面后执行finish,日志打印结果是一样的。
<UI> 我是第一个AC的Fragment: onPause
<UI> 我是第一个AC: onPause
<UI> 我是第二个AC: onCreate
<UI> 我是第二个Fragment: onCreate
<UI> 我是第二个Fragment: onCreateView
<UI> 我是第二个Fragment: onStart
<UI> 我是第二个AC: onStart
<UI> 我是第二个AC: onResume
<UI> 我是第一个AC的Fragment: onStop
<UI> 我是第一个AC: onStop
<UI> 我是第一个AC的Fragment: onDestroy
<UI> 我是第一个AC: onDestroy
Actvity
是先启动第二个新页面直到生命周期到了onResume
第一个Actvity
才会去执行onStop
和onDestroy
方法。
实际场景应用
实践日志打印结果上可知,当使用单例模型资源时切记不能在两个Actvity
结束生命周期中做释放操作。同时也不建议单例模型去做释放操作(既然是全局使用的工具类就不应该轻易释放)
在开发IM功能时新需求中有在当前会话页面进入到H5页面接着进行跳转到新会话页面。IM所有状态机是由一个单例结构管理(老逻辑没有多会话场景),因此上个会话页面结束后再启动新会话页面IM状态是离线了(结合当前页面启动新Activity和Fragment
可知)单例先执行创建又被上一个页面销毁了。
解决方式1
第一种解决方式是在单例中增加采用HashMap
计数方式来判断多次进入同一个会话时enter
方法会记录下次数,等到离开时leave
判断次数是否只有一个对象持有了来决定是否释放单例。
private val mSessionCountCache : ConcurrentHashMap<String, Int> = ConcurrentHashMap()
fun enter(){
if(mSessionCountCache.containsKey(groupId)){
mSessionCountCache[groupId] = 2
}else{
mSessionCountCache[groupId] = 1
}
return enterPage(groupId);
}
fun leave(){
if (mSessionCountCache.containsKey(groupId) && (mSessionCountCache[groupId] ?:0) > 1){
mSessionCountCache[groupId] = 1
return
}
}
解决方式2
将单例释放时机不在和Actvity
绑定,而是增加一个封装对象流程。启动一个新流程时单例对象实例化,经过多个流程当所有流程结束后再最后结束方法中释放单例。通过一个统一状态机管理来决定单例对象的情况从而保证在流程当中单例对象是实时有效不为空态。
interface progress{
}
class idle implement progress{ //初始阶段
}
class start implement progress{ //开始阶段
// 初始化单例
}
class stop implement progress{ //结束阶段
//再次释放单例
}
class mainProgress{
clas // 对象集合
idle,start,stop // 状态机
}
小结
关于Activity和Fragment生命周期所思主要是对单例在和生命周期共同作用下应该如何正确使用的问题,也是最近开发过程中经常会遇到的也是踩坑最多的问题。希望之后开发中多注意生命周期和功能性代码如何正确相结合非必要情况下不是万物皆可单例。