Fix Android RAM overlay instant crash

Use getMainLooper() instead of myLooper() in Handler initialization
and add view attachment checks to prevent crashes.

Reported-by: Shadai (theonlyshadai) on Discord
Signed-off-by: Zephyron <zephyron@citron-emu.org>
This commit is contained in:
Zephyron
2025-10-08 16:59:38 +10:00
parent 4c5f12ec69
commit 2a7e6c74bd
2 changed files with 57 additions and 22 deletions

View File

@@ -526,12 +526,17 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
if (emulationViewModel.emulationStarted.value &&
!emulationViewModel.isEmulationStopping.value
) {
if (_binding != null && binding.fpsIndicatorView.isAttachedToWindow) {
val perfStats = NativeLibrary.getPerfStats()
if (_binding != null) {
binding.fpsIndicatorView.updateFps(perfStats[FPS].toFloat())
}
perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800)
}
} else {
// Stop the updater if emulation is stopping
if (perfStatsUpdater != null) {
perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
}
}
}
perfStatsUpdateHandler.post(perfStatsUpdater!!)
} else {
@@ -549,12 +554,17 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
if (emulationViewModel.emulationStarted.value &&
!emulationViewModel.isEmulationStopping.value
) {
if (_binding != null) {
if (_binding != null && binding.thermalIndicatorView.isAttachedToWindow) {
val temperature = getBatteryTemperature(requireContext())
binding.thermalIndicatorView.updateTemperature(temperature)
}
thermalStatsUpdateHandler.postDelayed(thermalStatsUpdater!!, 2000)
}
} else {
// Stop the updater if emulation is stopping
if (thermalStatsUpdater != null) {
thermalStatsUpdateHandler.removeCallbacks(thermalStatsUpdater!!)
}
}
}
thermalStatsUpdateHandler.post(thermalStatsUpdater!!)
} else {
@@ -572,11 +582,16 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
if (emulationViewModel.emulationStarted.value &&
!emulationViewModel.isEmulationStopping.value
) {
if (_binding != null) {
if (_binding != null && binding.ramMeterView.isAttachedToWindow) {
binding.ramMeterView.updateRamUsage()
}
ramStatsUpdateHandler.postDelayed(ramStatsUpdater!!, 1500)
}
} else {
// Stop the updater if emulation is stopping
if (ramStatsUpdater != null) {
ramStatsUpdateHandler.removeCallbacks(ramStatsUpdater!!)
}
}
}
ramStatsUpdateHandler.post(ramStatsUpdater!!)
} else {
@@ -1161,9 +1176,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
companion object {
private val perfStatsUpdateHandler = Handler(Looper.myLooper()!!)
private val thermalStatsUpdateHandler = Handler(Looper.myLooper()!!)
private val ramStatsUpdateHandler = Handler(Looper.myLooper()!!)
private val shaderStatsUpdateHandler = Handler(Looper.myLooper()!!)
private val perfStatsUpdateHandler = Handler(Looper.getMainLooper())
private val thermalStatsUpdateHandler = Handler(Looper.getMainLooper())
private val ramStatsUpdateHandler = Handler(Looper.getMainLooper())
private val shaderStatsUpdateHandler = Handler(Looper.getMainLooper())
}
}

View File

@@ -35,7 +35,18 @@ class RamMeterView @JvmOverloads constructor(
fun updateRamUsage() {
try {
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
// Safety check: ensure view is attached and has valid dimensions
if (!isAttachedToWindow || width <= 0 || height <= 0) {
Log.w("RamMeter", "View not ready for update (attached: $isAttachedToWindow, width: $width, height: $height)")
return
}
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as? ActivityManager
if (activityManager == null) {
Log.e("RamMeter", "ActivityManager service not available")
return
}
val memoryInfo = ActivityManager.MemoryInfo()
activityManager.getMemoryInfo(memoryInfo)
@@ -61,7 +72,6 @@ class RamMeterView @JvmOverloads constructor(
ramUsagePercent = 0f
usedRamMB = 0L
totalRamMB = 0L
invalidate()
}
}
@@ -92,6 +102,13 @@ class RamMeterView @JvmOverloads constructor(
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// Safety check: ensure view has valid dimensions
if (width <= 0 || height <= 0) {
Log.w("RamMeter", "onDraw called with invalid dimensions (width: $width, height: $height)")
return
}
try {
// Draw simple text-based RAM display
val usedGB = usedRamMB / 1024f
val totalGB = totalRamMB / 1024f
@@ -103,5 +120,8 @@ class RamMeterView @JvmOverloads constructor(
canvas.drawText(ramText, 8f, height - 8f, textPaint)
Log.d("RamMeter", "onDraw called - RAM: $ramText")
} catch (e: Exception) {
Log.e("RamMeter", "Error in onDraw", e)
}
}
}