android 如何使用Kotlin协程写入文件,是否可以创建FIFO?

0dxa2lsx  于 2023-02-27  发布在  Android
关注(0)|答案(1)|浏览(161)

我正在尝试在应用程序中创建日志系统。我正在考虑收集日志并将其保存在一个文件中。我的想法是使用Kotlin协程来做这件事。所以我做了这个:

object LoggingModule {
    private val sdfTime = SimpleDateFormat("HH:mm:ss.SSS", Locale.US)

    fun saveLog(tag: String, event: String, t: Throwable?) {
        if (t == null) {
            Log.v(tag, event)
        } else {
            Log.e(tag, event, t)
        }

        val fileName = tag + ".txt"
        writeLogToFile(fileName, Date(System.currentTimeMillis()), event, t)
    }

    private fun writeLogToFile(fileName: String, time: Date, note: String, throwable: Throwable?) {
        val directory = File(Environment.getExternalStorageDirectory(), "APP_NAME")
        if (createDirIfNotExist(directory)) {
            val fileLogsDirectory = File(directory, fileName)
            val logToSave = sdfTime.format(time) + " " + note

            CoroutineScope(Dispatchers.IO).launch {
                saveLogInFile(logToSave, directory, throwable)
            }
        }
    }

    private suspend fun saveLogInFile(logToSave: String, directory: File, throwable: Throwable?) {
        withContext(Dispatchers.IO) {
            try {
                val pw = PrintWriter(FileWriter(directory, true))
                pw.println(logToSave)
                throwable?.printStackTrace(pw)
                pw.close()
            } catch (e: IOException) {
                Log.e("LogSystem", "Error writing to the log file", e)
            }
        }
    }
}

我想请你帮助我理解我做错了什么。我以为当我使用暂停方法时,它会被阻止,然后会以正确的顺序执行。当我需要同步顺序时,使用协程保存到文件是个好主意吗?
使用示例:

LoggingModule.saveLog("ACCOUNT", "user logged successful")
83qze16e

83qze16e1#

Suspend函数调用在同一个协程中按顺序执行,writeLogToFile函数每次被调用都会创建一个新的协程,因此saveLogInFile调用之间没有协调。
这可以通过一个参与者协程通道来实现,它允许你发送消息给它,让它以有序的方式处理。参与者计划在未来被弃用或删除,所以会有一个编译器警告使用“过时的API”。然而,参与者的替代品不会很快到来。
首先为要发布的数据创建一个 Package 器类:

data class LogMessage(val logToSave: String, val directory: File, val throwable: Throwable?)

此外,创建一个CoroutineScope供整个对象使用,而不是重复创建新的对象。

private val scope = CoroutineScope(Dispatchers.IO)

然后创建一个Actor,它将一次处理一条消息,在调用suspend函数saveLogInFile或等待另一条消息时挂起。

private val logMessageActor = scope.actor<LogMessage> {
    for (msg in channel)
        saveLogInFile(msg.logToSave, msg.directory, msg.throwable)
}

然后修改writeLogToFile函数,将消息发送给参与者,而不是直接调用saveLogInFile

private fun writeLogToFile(fileName: String, time: Date, note: String, throwable: Throwable?) {
    scope.launch {
        val directory = File(Environment.getExternalStorageDirectory(), "APP_NAME")
        if (createDirIfNotExist(directory)) {
            val fileLogsDirectory = File(directory, fileName)
            val logToSave = sdfTime.format(time) + " " + note
            logMessageActor.send(LogMessage(logToSave, directory, throwable))
        }
    }
}

下面是我认为不使用actor()也能做到的方法:

private val logMessageChannel = Channel<LogMessage>(capacity = Channel.UNLIMITED).also {
    scope.launch {
        for (msg in channel)
            saveLogInFile(msg.logToSave, msg.directory, msg.throwable)
    }
}

然后发送到这个频道而不是演员。

相关问题