Kotlin lateinit变量未初始化报错,如何修正JSON数据获取后的变量更新?
问题根源分析
你遇到的kotlin.UninitializedPropertyAccessException本质是异步操作的时序问题:
fetchJson里使用OkHttp的enqueue()发起的是异步网络请求——这个请求会在后台线程执行,而你的fetchJson函数会立刻返回name2,这时候name2还没被onResponse里的代码初始化,自然就抛出异常了。lateinit只是告诉编译器"我会在使用前初始化",但异步场景下你没法保证这一点。
解决方案:用Kotlin协程(推荐)
现在Android开发中最简洁的异步处理方式是Kotlin协程,它能让异步代码看起来像同步代码,完美解决这个时序问题:
步骤1:修改fetchJson为挂起函数
我们用OkHttp的await()扩展函数(需要依赖kotlinx-coroutines-okhttp)来同步等待网络响应,确保拿到数据后再返回:
// 添加suspend标记,将函数转为挂起函数 suspend fun fetchJson(): Array<Articles>? { println("Attempting to fetch JSON") val url1 = "https://go-api-api.herokuapp.com/" val request = Request.Builder().url(url1).build() val client = OkHttpClient() return try { // await()会挂起协程,直到网络请求完成返回响应 val response = client.newCall(request).await() val body = response.body()?.string() ?: return null println(body) val gson = GsonBuilder().create() gson.fromJson(body, Array<Articles>::class.java) } catch (e: IOException) { println("Failed to execute request: ${e.message}") null } catch (e: Exception) { println("Error parsing JSON: ${e.message}") null } }
步骤2:在Fragment中用生命周期协程调用
Fragment可以使用viewLifecycleOwner.lifecycleScope来启动协程,它会自动和Fragment的视图生命周期绑定,避免内存泄漏:
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { super.onCreateView(inflater, container, savedInstanceState) val view1 = inflater?.inflate(R.layout.fragment_home, container, false) ?: return null val recyclerview = view1.findViewById<RecyclerView>(R.id.recycler_view) recyclerview.layoutManager = LinearLayoutManager(activity) // 启动协程获取数据,拿到数据后再设置适配器 viewLifecycleOwner.lifecycleScope.launch { val articles = fetchJson() articles?.let { recyclerview.adapter = CustomAdaptor(it) } } return view1 }
备选方案:使用回调接口
如果暂时不想用协程,也可以通过回调接口来确保数据获取完成后再更新UI:
步骤1:定义回调接口
interface JsonFetchCallback { fun onSuccess(articles: Array<Articles>) fun onFailure(errorMsg: String) }
步骤2:修改fetchJson接受回调
fun fetchJson(callback: JsonFetchCallback) { println("Attempting to fetch JSON") val url1 = "https://go-api-api.herokuapp.com/" val request = Request.Builder().url(url1).build() val client = OkHttpClient() client.newCall(request).enqueue(object : Callback { override fun onResponse(call: Call?, response: Response?) { val body = response?.body()?.string() println(body) val gson = GsonBuilder().create() try { val articles = gson.fromJson(body, Array<Articles>::class.java) callback.onSuccess(articles) } catch (e: Exception) { callback.onFailure("JSON解析错误: ${e.message}") } } override fun onFailure(call: Call?, e: IOException?) { callback.onFailure("请求失败: ${e?.message}") } }) }
步骤3:在Fragment中调用并更新UI
注意:OkHttp的回调在后台线程,更新UI必须切回主线程:
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { super.onCreateView(inflater, container, savedInstanceState) val view1 = inflater?.inflate(R.layout.fragment_home, container, false) ?: return null val recyclerview = view1.findViewById<RecyclerView>(R.id.recycler_view) recyclerview.layoutManager = LinearLayoutManager(activity) fetchJson(object : JsonFetchCallback { override fun onSuccess(articles: Array<Articles>) { // 切回主线程更新RecyclerView requireActivity().runOnUiThread { recyclerview.adapter = CustomAdaptor(articles) } } override fun onFailure(errorMsg: String) { println(errorMsg) } }) return view1 }
关键总结
不管用哪种方案,核心都是确保在网络请求完成、数据解析完成后,再去初始化RecyclerView的适配器——不能在异步操作还没完成时就返回未初始化的变量。协程方案更简洁易读,是当前Android开发的首选方式。
内容的提问来源于stack exchange,提问作者subhra jyoti das
相关产品推荐
相关产品推荐

