Fragment中API请求时按返回键致App崩溃问题排查
你的崩溃完全是生命周期相关的问题。当你按下返回键时,Fragment会被销毁并与Activity解除绑定,此时activity属性会变为null。但Retrofit的enqueue是异步请求——请求发起后会在后台执行,当请求完成回调onResponse/onFailure时,Fragment已经没有依附的Activity了,而你代码中使用了(activity as AppCompatActivity)这种非空强制类型转换,试图把null转成非空的AppCompatActivity,这就直接触发了kotlin.TypeCastException。
我们需要从两个方面修复这个问题:一是避免在回调中访问已失效的Activity/Fragment组件,二是在Fragment销毁时取消未完成的请求,避免无用的回调执行。
1. 保存Retrofit Call实例,在Fragment销毁时取消请求
首先在Fragment中添加一个成员变量保存Retrofit的Call对象,这样可以在Fragment生命周期结束时取消请求,防止回调继续执行:
class ProductStockOutletListFragment : Fragment() { private var binding: FragmentProductStockOutletListBinding? = null // 保存Retrofit请求的Call实例 private var outletListCall: Call<OutletListPOJODataClasses>? = null override fun onCreateView(...) { // ... 其他代码 ... // 给Call实例赋值 outletListCall = NetworkConfig().getOutletListService() .getOutlets() .enqueue(object : Callback<OutletListPOJODataClasses> { // ... 回调实现 ... }) } override fun onDestroyView() { super.onDestroyView() // 取消未完成的请求,避免回调执行 outletListCall?.cancel() // 清空binding,防止内存泄漏 binding = null } }
2. 在回调中安全访问Activity和View组件
在onResponse和onFailure回调中,不要直接强制转换activity,而是先检查Fragment是否还处于活跃状态(依附于Activity),再使用安全调用处理:
修改后的回调代码:
override fun onFailure(call: Call<OutletListPOJODataClasses>, t: Throwable) { // 先检查请求是否是主动取消的,再判断Activity是否存在 if (call.isCanceled) { activity?.let { Toast.makeText(it, "Request Aborted", Toast.LENGTH_SHORT).show() } } else { activity?.let { Toast.makeText(it, t.localizedMessage ?: "Unknown error", Toast.LENGTH_SHORT).show() } } } override fun onResponse( call: Call<OutletListPOJODataClasses>, response: Response<OutletListPOJODataClasses> ) { // 先检查Fragment是否还依附于Activity,以及binding是否有效 if (!isAdded || binding == null) return binding?.progressBar?.visibility = View.GONE response.body()?.let { outletList -> binding?.rvOutletList?.adapter = OutletListAdapter(outletList, this@ProductStockOutletListFragment) } // 安全显示Toast activity?.let { Toast.makeText(it, "Data retrieved!", Toast.LENGTH_SHORT).show() } }
额外优化:避免重复强制转换Activity
你代码中多次出现(activity as AppCompatActivity),可以用requireActivity()替代,但注意requireActivity()在Activity为null时会抛出异常,所以必须先确保Fragment处于活跃状态(用isAdded判断),或者直接用activity的安全调用。
- 取消未完成的请求:当Fragment销毁时,调用
call.cancel()会终止Retrofit的请求,回调不会再执行,从根源上避免了访问失效组件的可能。 - 安全访问组件:通过
isAdded检查Fragment状态,结合activity?.let和binding?.的安全调用,避免了null值的强制转换,彻底消除了TypeCastException的触发条件。
内容的提问来源于stack exchange,提问作者Foxtrot 1-5

