fix(base): 修复多处内存泄漏及生命周期安全问题
BaseVMBFragment: - 修复 onDestroyView 中 mBinding == null(比较运算)为正确的 _mBinding = null(赋值), 引入 backing field 模式彻底释放 View 引用,防止 Fragment 回退栈内存泄漏 - 修复 onDestroy 中 EventBus.unregister 判断条件取反(!isRegistered → isRegistered), 确保 Fragment 销毁时正确注销,避免 EventBus 长期持有 Fragment 引用 BaseVMBActivity: - 删除无效的 getResources() 重写(createConfigurationContext 返回值未使用), 字体防缩放逻辑已由 attachBaseContext 正确实现 MainActivity: - 删除 onSaveInstanceState(Bundle()) 错误重写, 传入空 Bundle 导致 Fragment 状态无法保存,低内存恢复场景必现异常 EmergencyFragment: - onDestroy 中补充主动停止并销毁 AMapLocationClient, 防止 Fragment 销毁后 GPS 定位服务持续运行 HomeFragment: - homeNoticeStartRepeat 新增 Job 引用,每次启动前取消旧协程, 防止多次调用时多个轮询协程并发叠加导致接口重复请求 WatchItemFragment: - newInstance 迁移至 companion object,符合 Android Fragment 工厂方法规范 - position 类型由 Int? 改为 Int,移除无用的 Fragment import Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
dcb53f344a
commit
9c91839520
@@ -272,9 +272,6 @@ class MainActivity : BaseVMBActivity<MainViewModel, ActivityMainBinding>(R.layou
|
|||||||
super.onPause()
|
super.onPause()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSaveInstanceState(outState: Bundle) {
|
|
||||||
super.onSaveInstanceState(Bundle())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getFragmentByTag(tabTag: String): Fragment? {
|
override fun getFragmentByTag(tabTag: String): Fragment? {
|
||||||
when (tabTag) {
|
when (tabTag) {
|
||||||
|
|||||||
@@ -70,15 +70,6 @@ abstract class BaseVMBActivity<VM : BaseViewModel, B : ViewDataBinding>(private
|
|||||||
var mEmpty: View?=null
|
var mEmpty: View?=null
|
||||||
var mRootView:RelativeLayout?=null
|
var mRootView:RelativeLayout?=null
|
||||||
|
|
||||||
/**
|
|
||||||
* 重写getResources()方法,让APP的字体不受系统设置字体大小影响
|
|
||||||
*/
|
|
||||||
override fun getResources(): Resources? {
|
|
||||||
val config = Configuration()
|
|
||||||
config.setToDefaults()
|
|
||||||
createConfigurationContext(config)
|
|
||||||
return super.getResources()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|||||||
@@ -56,15 +56,21 @@ abstract class BaseVMBFragment<VM : BaseViewModel, B : ViewDataBinding>(private
|
|||||||
private var mIsFirstLoading = true
|
private var mIsFirstLoading = true
|
||||||
var dialog: LoadingDialog? = null
|
var dialog: LoadingDialog? = null
|
||||||
protected lateinit var mViewModel: VM
|
protected lateinit var mViewModel: VM
|
||||||
lateinit var mBinding: B
|
|
||||||
|
/** ViewDataBinding 可空 backing field,onDestroyView 时置 null 防止内存泄漏 */
|
||||||
|
private var _mBinding: B? = null
|
||||||
|
|
||||||
|
/** 对外暴露的非空 getter,仅在 onCreateView ~ onDestroyView 生命周期内有效 */
|
||||||
|
val mBinding: B get() = _mBinding!!
|
||||||
|
|
||||||
var mEmpty: View? = null
|
var mEmpty: View? = null
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View? {
|
||||||
mBinding = DataBindingUtil.inflate(inflater, contentViewResId, container, false)
|
_mBinding = DataBindingUtil.inflate(inflater, contentViewResId, container, false)
|
||||||
return mBinding.root
|
return _mBinding!!.root
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
@@ -306,13 +312,15 @@ abstract class BaseVMBFragment<VM : BaseViewModel, B : ViewDataBinding>(private
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
mBinding == null
|
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
|
// 置 null 释放 View 引用,防止 Fragment 回退栈期间内存泄漏
|
||||||
|
_mBinding = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
if (!EventBus.getDefault().isRegistered(this)) {
|
// 已注册才执行注销,避免 EventBus 持有 Fragment 引用
|
||||||
|
if (EventBus.getDefault().isRegistered(this)) {
|
||||||
EventBus.getDefault().unregister(this)
|
EventBus.getDefault().unregister(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -180,6 +180,10 @@ class EmergencyFragment :
|
|||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
|
// 主动停止并销毁定位客户端,防止 Fragment 销毁后 GPS 定位服务持续运行造成资源浪费
|
||||||
|
mlocationClient?.stopLocation()
|
||||||
|
mlocationClient?.onDestroy()
|
||||||
|
mlocationClient = null
|
||||||
mMapView?.onDestroy()
|
mMapView?.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import com.xjjk.healthyclients.ui.viewmodel.HomeViewModel
|
|||||||
import com.xjjk.healthyclients.utils.ConstantUtils
|
import com.xjjk.healthyclients.utils.ConstantUtils
|
||||||
import com.xjjk.healthyclients.utils.IMInputActionSettingUtils
|
import com.xjjk.healthyclients.utils.IMInputActionSettingUtils
|
||||||
import com.xjjk.healthyclients.utils.TUIUtils
|
import com.xjjk.healthyclients.utils.TUIUtils
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -49,9 +50,11 @@ class HomeFragment :
|
|||||||
var mGuidanceFragmentDoctorAdapter: GuidanceFragmentDoctorAdapter? = null
|
var mGuidanceFragmentDoctorAdapter: GuidanceFragmentDoctorAdapter? = null
|
||||||
|
|
||||||
var REQUEST_DURATION = 1000 * 120L
|
var REQUEST_DURATION = 1000 * 120L
|
||||||
|
|
||||||
var noticeTime = REQUEST_DURATION
|
var noticeTime = REQUEST_DURATION
|
||||||
|
|
||||||
|
/** 通知轮播协程 Job,重新启动前先取消旧任务,防止多协程并发叠加 */
|
||||||
|
private var noticeJob: Job? = null
|
||||||
|
|
||||||
|
|
||||||
override fun initView(root: View?, savedInstanceState: Bundle?) {
|
override fun initView(root: View?, savedInstanceState: Bundle?) {
|
||||||
mBinding.swipeRefresh.initColors()
|
mBinding.swipeRefresh.initColors()
|
||||||
@@ -339,7 +342,9 @@ class HomeFragment :
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun homeNoticeStartRepeat() {
|
private fun homeNoticeStartRepeat() {
|
||||||
lifecycleScope.launch {
|
// 取消旧的轮询协程,防止多次调用时多个协程并发叠加
|
||||||
|
noticeJob?.cancel()
|
||||||
|
noticeJob = lifecycleScope.launch {
|
||||||
repeat(500) {
|
repeat(500) {
|
||||||
mViewModel.getHomeNoticeListRepeat()
|
mViewModel.getHomeNoticeListRepeat()
|
||||||
delay(noticeTime)
|
delay(noticeTime)
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package com.xjjk.healthyclients.fragment
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import com.sw.healthyclients.utils.DateUtil
|
import com.sw.healthyclients.utils.DateUtil
|
||||||
import com.xjjk.healthyclients.R
|
import com.xjjk.healthyclients.R
|
||||||
import com.xjjk.healthyclients.base.BaseVMBFragment
|
import com.xjjk.healthyclients.base.BaseVMBFragment
|
||||||
@@ -20,18 +19,25 @@ import com.xjjk.healthyclients.ui.viewmodel.HomeViewModel
|
|||||||
*/
|
*/
|
||||||
class WatchItemFragment :
|
class WatchItemFragment :
|
||||||
BaseVMBFragment<HomeViewModel, FragmentItemWatchBinding>(R.layout.fragment_item_watch) {
|
BaseVMBFragment<HomeViewModel, FragmentItemWatchBinding>(R.layout.fragment_item_watch) {
|
||||||
var position: Int? = -1
|
|
||||||
|
|
||||||
fun newInstance(position: Int): Fragment {
|
companion object {
|
||||||
|
/**
|
||||||
|
* 创建 WatchItemFragment 实例,通过 arguments 传入 Tab 位置
|
||||||
|
* @param position Tab 位置(0=心率 1=血氧 2=压力 3=体温 4=睡眠)
|
||||||
|
*/
|
||||||
|
fun newInstance(position: Int): WatchItemFragment {
|
||||||
val args = Bundle()
|
val args = Bundle()
|
||||||
args.putInt("position", position)
|
args.putInt("position", position)
|
||||||
val fragment = WatchItemFragment()
|
return WatchItemFragment().apply {
|
||||||
fragment.arguments = args
|
arguments = args
|
||||||
return fragment
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var position: Int = -1
|
||||||
|
|
||||||
override fun initView(root: View?, savedInstanceState: Bundle?) {
|
override fun initView(root: View?, savedInstanceState: Bundle?) {
|
||||||
position = arguments?.getInt("position")
|
position = arguments?.getInt("position") ?: -1
|
||||||
mBinding.watchTypeImg.setImageResource(getWatchTypeImg(position))
|
mBinding.watchTypeImg.setImageResource(getWatchTypeImg(position))
|
||||||
|
|
||||||
if (position != 4) {
|
if (position != 4) {
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ class HomeWatchView(context: Context?, attrs: AttributeSet?) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun createFragment(position: Int): Fragment {
|
override fun createFragment(position: Int): Fragment {
|
||||||
return WatchItemFragment().newInstance(position)
|
return WatchItemFragment.newInstance(position)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user