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

View File

@@ -35,7 +35,18 @@ class RamMeterView @JvmOverloads constructor(
fun updateRamUsage() { fun updateRamUsage() {
try { 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() val memoryInfo = ActivityManager.MemoryInfo()
activityManager.getMemoryInfo(memoryInfo) activityManager.getMemoryInfo(memoryInfo)
@@ -61,7 +72,6 @@ class RamMeterView @JvmOverloads constructor(
ramUsagePercent = 0f ramUsagePercent = 0f
usedRamMB = 0L usedRamMB = 0L
totalRamMB = 0L totalRamMB = 0L
invalidate()
} }
} }
@@ -92,16 +102,26 @@ class RamMeterView @JvmOverloads constructor(
override fun onDraw(canvas: Canvas) { override fun onDraw(canvas: Canvas) {
super.onDraw(canvas) super.onDraw(canvas)
// Draw simple text-based RAM display // Safety check: ensure view has valid dimensions
val usedGB = usedRamMB / 1024f if (width <= 0 || height <= 0) {
val totalGB = totalRamMB / 1024f Log.w("RamMeter", "onDraw called with invalid dimensions (width: $width, height: $height)")
val ramText = if (totalGB >= 1.0f) { return
"RAM: ${ramUsagePercent.roundToInt()}% (%.1fGB/%.1fGB)".format(usedGB, totalGB)
} else {
"RAM: ${ramUsagePercent.roundToInt()}% (${usedRamMB}MB/${totalRamMB}MB)"
} }
canvas.drawText(ramText, 8f, height - 8f, textPaint)
Log.d("RamMeter", "onDraw called - RAM: $ramText") try {
// Draw simple text-based RAM display
val usedGB = usedRamMB / 1024f
val totalGB = totalRamMB / 1024f
val ramText = if (totalGB >= 1.0f) {
"RAM: ${ramUsagePercent.roundToInt()}% (%.1fGB/%.1fGB)".format(usedGB, totalGB)
} else {
"RAM: ${ramUsagePercent.roundToInt()}% (${usedRamMB}MB/${totalRamMB}MB)"
}
canvas.drawText(ramText, 8f, height - 8f, textPaint)
Log.d("RamMeter", "onDraw called - RAM: $ramText")
} catch (e: Exception) {
Log.e("RamMeter", "Error in onDraw", e)
}
} }
} }