You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Kotlin重复创建传感器数据文件时EExit错误的解决方法

问题修复方案

错误原因分析

你遇到的EExit错误,本质是Android文件系统缓存/媒体库同步延迟导致的误判:手动删除文件后,系统MediaStore或文件系统缓存未及时更新,当程序尝试创建同名文件时,系统误以为文件仍存在;另外依赖saveCounter自增生成文件名的逻辑,若未结合目录现有文件计数,也可能导致重复文件名冲突。

修复方案

以下是几种针对性的修复方法,按推荐优先级排序:

  • 使用绝对唯一的文件名(最推荐):放弃依赖saveCounter,改用时间戳生成文件名,确保每次创建的文件绝对唯一,彻底避免文件名冲突。
  • 手动同步文件系统状态:创建文件前,强制检查文件实际存在状态,若系统缓存误判则手动刷新媒体库。
  • 动态初始化saveCounter:启动时扫描目标目录,自动计算当前最大文件编号,确保saveCounter从正确值开始自增。

完整修改代码(方案1实现)

import android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import android.os.Environment
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.io.File
import java.text.SimpleDateFormat
import java.util.*

class MainActivity : AppCompatActivity() {
    private var isSaving = false
    private var currentFileName: String? = null
    private val coroutineScope = CoroutineScope(Dispatchers.IO)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        setChart()
        checkFunction()

        val saveButton: Button = findViewById(R.id.btn_save)
        saveButton.setOnClickListener {
            if (isSaving) {
                isSaving = false
                saveButton.text = "Save Start"
                Toast.makeText(this, "已停止数据保存", Toast.LENGTH_SHORT).show()
            } else {
                isSaving = true
                saveButton.text = "Save Stop"

                if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                    != PackageManager.PERMISSION_GRANTED
                ) {
                    ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 1)
                } else {
                    Toast.makeText(this, "已开始数据保存", Toast.LENGTH_SHORT).show()
                    // 生成绝对唯一的文件名:时间戳+随机数
                    currentFileName = "Sensor_data_${System.currentTimeMillis()}_${Random().nextInt(1000)}.txt"
                    coroutineScope.launch {
                        while (isSaving) {
                            saveSensorDataToFile()
                            delay(1000)
                        }
                    }
                }
            }
        }
    }

    private fun saveSensorDataToFile() {
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
            == PackageManager.PERMISSION_GRANTED && currentFileName != null
        ) {
            val root = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
            val dir = File(root, "SensorData3")
            dir.mkdirs()
            val file = File(dir, currentFileName!!)
            // ThreadLocal解决SimpleDateFormat线程不安全问题
            val sdf = ThreadLocal.withInitial {
                SimpleDateFormat("MM-dd HH:mm:ss", Locale.getDefault())
            }.get()
            val currentDate = sdf.format(Date())
            val data = "$currentDate, $pre_x, $acc_x, $acc_y, $acc_z\n"
            
            // 显式确认文件状态,避免缓存误判
            if (!file.exists()) {
                file.createNewFile()
            }
            file.appendText(data)
        } else {
            ActivityCompat.requestPermissions(
                this,
                arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
                0
            )
        }
    }

    private fun checkFunction() {
        val permissions = arrayOf(
            Manifest.permission.ACCESS_FINE_LOCATION,
            Manifest.permission.ACCESS_COARSE_LOCATION,
            Manifest.permission.WRITE_EXTERNAL_STORAGE
        )
        if (permissions.any {
                ActivityCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED
            }) {
            ActivityCompat.requestPermissions(this, permissions, 101)
        }
    }

    // 保留你的图表初始化逻辑
    private fun setChart() {
        // 原图表代码实现
    }

    // 保留你的传感器数据变量
    private var pre_x = 0.0
    private var acc_x = 0.0
    private var acc_y = 0.0
    private var acc_z = 0.0
}

额外优化建议

  • 替换GlobalScope为lifecycleScope(需依赖AndroidX lifecycle-runtime-ktx),避免内存泄漏
  • 使用ThreadLocal封装SimpleDateFormat,解决多线程下的线程安全问题
  • 简化权限请求逻辑,去除冗余的条件判断

内容的提问来源于stack exchange,提问作者이건웅

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 02:16:14