跳转至

协程加载文件

ASK

我的这个下面的协程加载读取文件的jetpack compose不能应用,你能帮我看看为什么吗

package com.linkdom.readbook

import android.annotation.SuppressLint
import android.content.Context
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import kotlinx.coroutines.*
import java.io.BufferedReader
import java.io.File
import java.io.InputStreamReader


@OptIn(DelicateCoroutinesApi::class)
@Composable
fun Test() {
    val context = LocalContext.current
    val text = remember { mutableStateOf("") }
    Column (
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally
            ){
        Button(onClick = {
            GlobalScope.launch(Dispatchers.Main) {
                readFile(context){
                    text.value = it
                }
            }
        }) {
            Text("读取文件")
        }
        Text(text = text.value)
    }
}

@SuppressLint("CoroutineCreationDuringComposition")
@OptIn(DelicateCoroutinesApi::class)
private fun readFile(
    context: Context,
    onRead: (String) -> Unit
) {
    GlobalScope.launch(Dispatchers.IO) {
//        println("${Thread.currentThread().name}执行文件读取任务。\n")
        val inputStream = File("app/src/main/res/tmp.txt").inputStream()
        val bufferedReader = BufferedReader(InputStreamReader(inputStream))
        var line: String? = bufferedReader.readLine()
        while (line != null) {
            onRead(line)
            line = bufferedReader.readLine()
        }
    }
}

@Preview(showBackground = true)
@Composable
fun Test4() {
    Test()
}

报错如下:

E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-1
    Process: com.linkdom.readbook, PID: 31890
    java.io.FileNotFoundException: app/src/main/res/tmp.txt: open failed: ENOENT (No such file or directory)
        at libcore.io.IoBridge.open(IoBridge.java:496)
        at java.io.FileInputStream.<init>(FileInputStream.java:159)
        at com.linkdom.readbook.TestReadRes4Kt$readFile$1.invokeSuspend(TestReadRes4.kt:53)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
        at kotlinx.coroutines.internal.LimitedDispatcher.run(LimitedDispatcher.kt:42)
        at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:95)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
        Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@555505, Dispatchers.IO]
    Caused by: android.system.ErrnoException: open failed: ENOENT (No such file or directory)
        at libcore.io.Linux.open(Native Method)
        at libcore.io.ForwardingOs.open(ForwardingOs.java:167)
        at libcore.io.BlockGuardOs.open(BlockGuardOs.java:252)
        at libcore.io.ForwardingOs.open(ForwardingOs.java:167)
        at android.app.ActivityThread$AndroidOs.open(ActivityThread.java:7255)
        at libcore.io.IoBridge.open(IoBridge.java:482)
            ... 10 more
I/Process: Sending signal. PID: 31890 SIG: 9

请用中文回答我。

GPT(顺利解决,代码在完整demo中)

从您提供的错误日志来看,问题出在找不到文件app/src/main/res/tmp.txt,具体错误为java.io.FileNotFoundException

在Android应用中,您不能直接使用项目文件结构中的路径。您需要将文件放在应用的assets文件夹中,并使用AssetManager来读取文件。请按照以下步骤修改您的代码:

  1. tmp.txt文件移动到app/src/main/assets文件夹下(如果没有该文件夹,请创建一个)。
  2. 修改readFile函数如下:
private fun readFile(
    context: Context,
    onRead: (String) -> Unit
) {
    GlobalScope.launch(Dispatchers.IO) {
        val assetManager = context.assets
        val inputStream = assetManager.open("tmp.txt")
        val bufferedReader = BufferedReader(InputStreamReader(inputStream))
        var line: String? = bufferedReader.readLine()
        while (line != null) {
            onRead(line)
            line = bufferedReader.readLine()
        }
    }
}

这样就可以正确读取tmp.txt文件了。

另外,虽然在这个示例中使用GlobalScope可能不会产生问题,但是并不推荐在实际项目中使用GlobalScope,因为它可能导致内存泄漏和其他问题。建议使用rememberCoroutineScope取代GlobalScope。这里是使用rememberCoroutineScope的修改示例:

@Composable
fun Test() {
    val context = LocalContext.current
    val text = remember { mutableStateOf("") }
    val coroutineScope = rememberCoroutineScope()
    Column (
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally
            ){
        Button(onClick = {
            coroutineScope.launch {
                readFile(context) {
                    text.value = it
                }
            }
        }) {
            Text("读取文件")
        }
        Text(text = text.value)
    }
}

这样您的代码应该可以正常工作了。如果您还有其他问题,请随时向我提问。