Android Fragment Volley回调强转Context触发空指针崩溃问题
问题根因
日志里的Unknown bits set in runtime_flags: 0x8000是系统镜像的通用警告,和你的崩溃无关,可以直接忽略。你的崩溃和功能异常来自三个核心代码错误:
- 空指针触发原因:Volley网络请求是异步执行的,你把
activity as Context的强转写在请求回调里,等网络返回结果触发回调时,Fragment很可能已经和宿主Activity解绑,此时activity属性值为null,你把null强转为非空类型的Context直接触发空指针崩溃。你把adapter设置代码挪到onCreateView开头不崩,是因为onCreateView执行时Fragment刚完成视图创建,必然和Activity绑定,activity不为空,但此时网络请求还没返回,列表自然是空的。 - 布局警告原因:你一开始没有给RecyclerView设置adapter,所以系统会抛出
No adapter attached; skipping layout的警告,RecyclerView首帧加载时没有可渲染的适配器。 - 逻辑写法错误:你把adapter初始化、绑定adapter、设置layoutManager的代码写在了JSON解析的for循环内部,每遍历一条书籍数据就会重新创建一次适配器、重新绑定一次RecyclerView,既浪费性能,也会导致列表数据反复被覆盖。
修复方案
按以下步骤调整代码即可:
- 在onCreateView里完成视图加载、找到RecyclerView实例后,立刻设置LayoutManager,同时初始化空数据的适配器并绑定给RecyclerView,不要等网络回调再做这两步。
- 网络请求回调触发时,先判断Fragment是否还附加在Activity上(用
isAdded校验),避免访问null的activity。 - 等所有JSON数据遍历解析完成、全部加入bookInfoList之后,再通知适配器数据更新,不要在循环里反复创建适配器。
修复后的核心代码示例:
class Dashboard : Fragment() { lateinit var recycle: RecyclerView lateinit var reLayout: RecyclerView.LayoutManager lateinit var RecObj: AdapterData lateinit var bookInfoList: ArrayList<Book> override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View? { val view = inflater.inflate(R.layout.fragment_dashboard, container, false) recycle = view.findViewById(R.id.recyle) // 提前设置布局管理器,此阶段Fragment必然和Activity绑定,上下文非空 reLayout = LinearLayoutManager(requireContext()) recycle.layoutManager = reLayout // 提前初始化空列表适配器并绑定,解决No adapter警告 bookInfoList = arrayListOf() RecObj = AdapterData(requireContext(), bookInfoList) recycle.adapter = RecObj val queue = Volley.newRequestQueue(requireContext()) val url = "http://13.235.250.119/v1/book/fetch_books" val JsonRequest = object : JsonObjectRequest( Request.Method.GET, url, null, Response.Listener{ // 回调先校验Fragment附着状态,避免空指针 if (!isAdded) return@Listener val success = it.getBoolean("success") if(success){ val jsonarray = it.getJSONArray("data") // 循环内仅做数据解析添加,不重复创建适配器 for( i in 0 until jsonarray.length()){ val jsonobj = jsonarray.getJSONObject(i) val bok:Book = Book( jsonobj.getString("book_id"), jsonobj.getString("name"), jsonobj.getString("author"), jsonobj.getString("rating"), jsonobj.getString("price"), jsonobj.getString("image") ) bookInfoList.add(bok) } // 所有数据解析完成后统一通知列表刷新 RecObj.notifyDataSetChanged() } else{ Toast.makeText(requireContext(),"Error",Toast.LENGTH_LONG).show() } }, Response.ErrorListener{ println("Error is $it") } ) { override fun getHeaders(): MutableMap<String, String> { val headers = HashMap<String,String>() headers["Content-type"] = "application/json" headers["token"] = "9bf534118365f1" return headers } } queue.add(JsonRequest) return view } }
额外说明:Fragment里尽量用
requireContext()/requireActivity()获取上下文,不要直接用activity as Context的强转写法,前者在Fragment未绑定时报的是更明确的状态异常,方便排查问题,配合isAdded校验可以完全避免这类异步回调的空指针。
内容的提问来源于stack exchange,提问作者Resfe
相关产品推荐
相关产品推荐

