为何Kotlin+Gradle文件读取逻辑在Android Studio失效但IntelliJ IDEA可用?
问题描述
我使用Windows操作系统,在Android Studio中把${project_name}\app\src\main\res标记为resources root,两个类使用同一包名,代码如下:
import java.nio.charset.Charset import java.nio.file.Files import java.nio.file.Paths class Parser(fileName:String){ val fileName=fileName val fileContent=getContent() fun getContent():String{ val encoded= Files.readAllBytes(Paths.get(".",fileName)) // 预期相对路径指向Android Studio里的res文件夹 return String(encoded, Charset.defaultCharset()) } }
import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.widget.TextView class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val parser=Parser("default.vert") findViewById<TextView>(R.id.tv_test).also { it.text=parser.fileContent } } }
运行时报错:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.test/com.example.test.MainActivity}: java.nio.file.NoSuchFileException: ./default.vert
但在IntelliJ IDEA社区版中,使用几乎相同的代码(仅路径不同,将某文件夹标记为默认resources root)可正常运行:
import java.nio.charset.Charset import java.nio.file.Files import java.nio.file.Paths class Parser(fileName:String){ val fileName=fileName val fileContent=getContent() fun getContent():String{ val encoded= Files.readAllBytes(Paths.get(".","src","main","resources",fileName)) // 此处使用项目最顶层目录(项目名为Test) return String(encoded, Charset.defaultCharset()) } } fun main(){ val parser=Parser("test.vert") print(parser.fileContent) }
正常输出文件内容:
layout (location=0) in vec4 vPosition; void main(){ gl_Position=vPosition; }
如何让该代码在Android Studio中也能正常运行?(使用Kotlin实现)
我尝试过使用绝对路径如c:\users\${user_name}\..until_the_file\default.vert,但仍抛出fileNotFoundException异常。
解决方案
Android项目的资源访问逻辑和Java SE项目完全不同,不能直接用java.nio.file读取本地路径,需使用Android官方提供的资源读取API,以下是两种可行方案:
方案1:使用assets目录读取
- 在
app/src/main下创建assets文件夹(无则新建),将default.vert放入该目录。 - 修改
Parser类,通过AssetManager读取文件:
import android.content.Context import java.nio.charset.Charset class Parser(private val context: Context, fileName: String) { val fileName = fileName val fileContent = getContent() private fun getContent(): String { return context.assets.open(fileName).use { inputStream -> val bytes = inputStream.readBytes() String(bytes, Charset.defaultCharset()) } } }
- 在
MainActivity中传入上下文初始化Parser:
import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.widget.TextView class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val parser = Parser(this, "default.vert") findViewById<TextView>(R.id.tv_test).also { it.text = parser.fileContent } } }
方案2:使用res/raw目录读取
- 在
app/src/main/res下创建raw文件夹(无则新建),将default.vert放入此处(注意文件名只能用小写字母、数字、下划线,不能有大写或特殊字符)。 - 修改
Parser类,通过资源ID读取文件:
import android.content.Context import java.nio.charset.Charset class Parser(private val context: Context, private val resId: Int) { val fileContent = getContent() private fun getContent(): String { return context.resources.openRawResource(resId).use { inputStream -> val bytes = inputStream.readBytes() String(bytes, Charset.defaultCharset()) } } }
- 在
MainActivity中使用资源ID初始化Parser:
import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.widget.TextView class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val parser = Parser(this, R.raw.default_vert) findViewById<TextView>(R.id.tv_test).also { it.text = parser.fileContent } } }
原方法失效原因
- Android项目打包成APK后,res目录下的文件会被编译或重命名,不存在本地开发时的相对路径。
- APP运行时的工作目录是设备上的沙盒目录,不是电脑上的项目根目录,
./default.vert这类路径无法定位到文件。 - 绝对路径不可行是因为APP没有访问电脑本地文件系统的权限,且打包到设备后路径完全不同。
内容的提问来源于stack exchange,提问作者beginner
相关产品推荐
相关产品推荐

