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

Kotlin故事书App开发遇FileNotFoundException求助

问题描述

我是编程新手,正在使用Kotlin开发一款故事书App,计划在Home Fragment中通过Assets加载本地contents.json文件,并搭配Glide实现功能。但运行时触发了InvocationTargetException,其根源为FileNotFoundException,具体错误日志、相关代码如下,请求帮忙修复该问题:

报错信息

FATAL EXCEPTION: main
Process: com.example.mykotlinapp, PID: 19988
java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:558)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:957)
Caused by: java.lang.reflect.InvocationTargetException                                                                                                      at java.lang.reflect.Method.invoke(Native Method
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)                                                                                                        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:957) 
Caused by: java.io.FileNotFoundException: contents.json

Home Fragment代码

private lateinit var bookAdapter: BookAdapter
private lateinit var recyclerView: RecyclerView
private lateinit var dataList: ArrayList<BooksData>

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?

): View {
    val view = inflater.inflate(R.layout.fragment_home, container, false)

    // Initialize the RecyclerView
    dataList = ArrayList()
    recyclerView = view.findViewById(R.id.bookRecyclerView)
    recyclerView.layoutManager = GridLayoutManager(requireContext(), 2)
    bookAdapter = BookAdapter(this@HomeFragment, dataList)
    recyclerView.adapter = bookAdapter
    recyclerView.setHasFixedSize(true)

    loadData()
   // loadJson()
    return view
}

private fun loadData() {
    val jsonString = loadJson("contents.json")
    val items = parseJson(jsonString)
    bookAdapter.setData(items)

}

private fun loadJson(contents: String): String {
    val inputStream = this.requireContext().assets.open(contents)
    val size = inputStream.available()
    val buffer = ByteArray(size)
    inputStream.read(buffer)
    inputStream.close()
    return String(buffer)
}

private fun parseJson(jsonString: String): ArrayList<BooksData> {
    val dataList = ArrayList<BooksData>()
    val jsonArray = JSONArray(jsonString)
    for (i in 0 until jsonArray.length()) {
        val jsonObject = jsonArray.getJSONObject(i)
        val bookName = jsonObject.getString("bookName")
        val imageUrl = jsonObject.getString("imageUrl")
        dataList.add(BooksData(imageUrl, bookName))
    }; return dataList
}
}

数据类代码

data class BooksData(val bookName:String, val imageUrl:String){
}

Adapter代码

class BookAdapter(var context: HomeFragment, private var dataList: ArrayList<BooksData>) :
    RecyclerView.Adapter<BookAdapter.BookViewHolder>() {

    inner class BookViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        var bookNameTextView: TextView = itemView.findViewById(R.id.bookName)
        var bookImageView: ImageView = itemView.findViewById(R.id.imageUrl)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BookViewHolder {
        val view =
            LayoutInflater.from(parent.context).inflate(R.layout.book_category_item, parent, false)
        return BookViewHolder(view)
    }

    override fun getItemCount(): Int = dataList.size
    fun setData(data: ArrayList<BooksData>) {
        dataList.clear()
        dataList.addAll(data)
        notifyDataSetChanged()
    }

    override fun onBindViewHolder(holder: BookViewHolder, position: Int) {
        val item = dataList[position]
        holder.bookNameTextView.text = item.bookName
        Glide.with(context)
            .load(item.imageUrl)
            .placeholder(R.drawable.ic_launcher_background)
            .error(R.drawable.vec_quiz)
            .into(holder.bookImageView)
    }
}

JSON文件内容

{
  "items": [
    {
      "bookName": "Basic Grammar",
      "imageUrl": "https://drive.google.com/uc?id=1_eVoTO4IeTW1gAbNBb_0-GWASecBNGgS"
    } ,
    {
      "bookName": "Basic Grammar",
      "imageUrl": "https://drive.google.com/uc?id=1_eVoTO4IeTW1gAbNBb_0-GWASecBNGgS"
    }
  ]
}

解决方案

1. 解决FileNotFoundException

  • 检查assets目录结构:确保在src/main目录下创建了assets文件夹(不是res目录下),并将contents.json文件放在该文件夹根目录。Android文件名区分大小写,确认文件名拼写完全一致(包括大小写)。
  • 确认文件路径:如果JSON文件在assets的子文件夹中,需要传入完整路径(如"books/contents.json"),当前代码使用的是根目录路径,确保文件位置正确。

2. 修复JSON解析错误

当前parseJson方法错误地将整个JSON对象当成JSONArray解析,实际JSON外层是包含items数组的JSONObject。修改方法如下:

private fun parseJson(jsonString: String): ArrayList<BooksData> {
    val dataList = ArrayList<BooksData>()
    // 先解析外层JSONObject
    val rootObject = JSONObject(jsonString)
    // 获取items数组
    val jsonArray = rootObject.getJSONArray("items")
    
    for (i in 0 until jsonArray.length()) {
        val itemObj = jsonArray.getJSONObject(i)
        val bookName = itemObj.getString("bookName")
        val imageUrl = itemObj.getString("imageUrl")
        // 修正数据类参数顺序(原代码传反了)
        dataList.add(BooksData(bookName, imageUrl))
    }
    return dataList
}

3. 修正数据类参数传递顺序

原代码中BooksData(imageUrl, bookName)的参数顺序与数据类定义(bookName:String, imageUrl:String)相反,会导致数据错位,已经在上面的解析方法中修正。

4. 优化Adapter避免内存泄漏

Adapter中传入HomeFragment作为Context存在内存泄漏风险,改为传入Context:

// 修改Adapter构造函数
class BookAdapter(private val context: Context, private var dataList: ArrayList<BooksData>) :
    RecyclerView.Adapter<BookAdapter.BookViewHolder>() {
    // 其他代码不变,Glide使用context即可
    override fun onBindViewHolder(holder: BookViewHolder, position: Int) {
        val item = dataList[position]
        holder.bookNameTextView.text = item.bookName
        Glide.with(context)
            .load(item.imageUrl)
            .placeholder(R.drawable.ic_launcher_background)
            .error(R.drawable.vec_quiz)
            .into(holder.bookImageView)
    }
}

// 在HomeFragment中初始化Adapter时修改为:
bookAdapter = BookAdapter(requireContext(), dataList)

5. 添加异常处理增强稳定性

在loadJson方法中添加异常捕获,避免崩溃并便于调试:

private fun loadJson(filename: String): String? {
    return try {
        val inputStream = requireContext().assets.open(filename)
        val size = inputStream.available()
        val buffer = ByteArray(size)
        inputStream.read(buffer)
        inputStream.close()
        String(buffer)
    } catch (e: IOException) {
        e.printStackTrace()
        null
    }
}

// 对应的loadData方法增加空值判断:
private fun loadData() {
    val jsonString = loadJson("contents.json") ?: run {
        Toast.makeText(requireContext(), "数据加载失败", Toast.LENGTH_SHORT).show()
        return
    }
    val items = parseJson(jsonString)
    bookAdapter.setData(items)
}

内容的提问来源于stack exchange,提问作者Anoop Moji

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 02:20:00