Android ViewBinding偶现binding对象为空 访问控件时抛空指针异常
问题根因
从崩溃栈可以看出,空指针发生在Retrofit异步请求的失败回调中调用showErrorMessage的环节,binding为空的核心原因是异步请求的生命周期和Activity生命周期没有绑定:
- 请求发出后、回调返回前,Activity已经被销毁(用户主动关闭页面、系统内存不足回收页面、旋转屏幕触发页面重建等场景),此时回调持有的是已经销毁的Activity实例,对应的binding要么已经被置空,要么属于废弃对象,访问就会触发空指针。
解决方案
方案1:页面销毁时取消未完成的请求(推荐)
异步请求和页面生命周期绑定,页面销毁后直接取消请求,避免销毁后执行UI更新逻辑:
// 新增成员变量存储登录请求实例 private Call<LoginResponse> loginCall; private void submitRemoteLogin() { // 发起请求时保存Call实例 loginCall = yourApiService.loginRequest(email, password); loginCall.enqueue(new Callback<LoginResponse>() { @Override public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) { if(call.isCanceled()) return; // 原有请求成功逻辑 } @Override public void onFailure(Call<LoginResponse> call, Throwable t) { if(call.isCanceled()) return; showErrorMessage(t.getLocalizedMessage()); } }); } @Override protected void onDestroy() { super.onDestroy(); // 页面销毁时取消未完成的请求 if(loginCall != null && !loginCall.isCanceled()) { loginCall.cancel(); } // 可选:置空binding避免内存泄漏 binding = null; }
方案2:访问binding前增加状态校验
在所有访问binding的UI方法前先校验页面状态和binding是否为空,异常状态直接返回:
private void showErrorMessage(final String message) { // 校验页面是否已销毁、binding是否为空 if (isFinishing() || isDestroyed() || binding == null) { return; } binding.errorMessageTV.setVisibility(View.VISIBLE); binding.errorMessageTV.setText(message); }
方案3:配合ViewModel+LiveData实现生命周期感知
将登录请求逻辑放到ViewModel中,用LiveData通知UI层更新,LiveData会自动感知Activity生命周期,只有页面处于活跃状态时才会触发UI回调,从根源避免销毁后更新UI的问题。
内容的提问来源于stack exchange,提问作者Swapon
相关产品推荐
相关产品推荐

