[Android]안드로이드 RecyclerView에 View Binding 사용시 오류 해결 방법: java.lang.NullPointerException: Attempt to read from field ‘androidx.recyclerview.widget.RecyclerView com.test.databinding.FragmentHomeBinding.recyclerView’ on a null object reference in method
리사이클러뷰를 뷰 바인딩 아키텍쳐를 사용하여 코딩하였는데, 간할적 혹은 주기적으로 발생한 오류이다.
java.lang.NullPointerException: Attempt to read from field ‘androidx.recyclerview.widget.RecyclerView com.test.databinding.FragmentHomeBinding.recyclerView’ on a null object reference in method
오류가 발생한 코드는 다음과 같다.
public class HomeFragment extends Fragment implements RequestApi.OnResultListener {
private FragmentHomeBinding binding;
protected RequestApi requestApi;
AlertDialog.Builder alertDialogBuilder = null;
public RecyclerView.LayoutManager layoutManager;
private HomeStockRecyclerViewAdapter adapter;
private ArrayList<UserStockVo> stockItemList;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
HomeViewModel homeViewModel =
new ViewModelProvider(this).get(HomeViewModel.class);
binding = FragmentHomeBinding.inflate(inflater, container, false);
View root = binding.getRoot();
final TextView baseDate = binding.baseDateText;
baseDate.setText("주가 기준일: 2024.12.20");
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getActivity());
binding.recyclerView.setLayoutManager(layoutManager);
// binding.recyclerView.setHasFixedSize(true);
requestApi = ((StockProfitManageApplication) getContext().getApplicationContext()).getRequestApiManager();
requestApi.addListener(HomeFragment.this); //(RequestApi.OnResultListener)
searchUserStockInfo("", "");
// final Button addbtn = binding.addBtn;
// homeViewModel.getText().observe(getViewLifecycleOwner(), addbtn::setText);
return root;
}
.....이상 생략
Log.d(BaseSettings.TAG, "#### stockItemList.size :" + stockItemList.size());
if(stockItemList!=null && stockItemList.size() > 0){
adapter = new HomeStockRecyclerViewAdapter(stockItemList, getActivity(), false);
binding.recyclerView.setAdapter(adapter);
}
데이터는 정상적으로 API서버에서 받아왔지만
“binding.recyclerView.setAdapter(adapter);” 코드에서 오류가 발생되는게 아닌가??
오류원인
이 오류는 binding
객체가 null
인데 binding.recyclerView
를 접근하려고 했기 때문에 발생한다. 해당 오류는 주로 다음 두 가지 상황에서 발생할 수 있다:
binding
객체 초기화 실패:FragmentHomeBinding.inflate(inflater, container, false)
가 제대로 실행되지 않았거나 반환된binding
객체가null
이다.onDestroyView()
호출 후onSuccess()
실행:Fragment
의 생명주기에서onDestroyView()
호출 시binding = null;
로 설정되기 때문에 이후에onSuccess()
메서드가 호출되면binding
을 사용할 수 없다.
오류 해결방법
1. binding
초기화 확인
onCreateView()
메서드에서 FragmentHomeBinding
객체를 초기화하는 부분을 확인한다.
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
binding = FragmentHomeBinding.inflate(inflater, container, false);
return binding.getRoot();
}
binding
이 null
이라면, XML 파일 이름과 데이터 바인딩 클래스를 다시 확인한다.
예: XML 파일 이름이 fragment_home.xml
이라면 데이터 바인딩 클래스는 FragmentHomeBinding
이다.
2. binding
사용 시점 확인
onDestroyView()
에서 binding = null;
을 설정한 이후, onSuccess()
에서 binding.recyclerView
를 사용하려 하면 문제가 발생한다. Fragment
의 생명주기와 비동기 요청 처리 타이밍을 조정해야 한다.
수정 방법:
onSuccess()
호출 전에binding
이null
인지 확인한다.
@Override
public void onSuccess(ResponseObject vo) {
if (binding == null) {
Log.d(BaseSettings.TAG, "Binding is null. Skipping onSuccess.");
return;
}
if (vo != null) {
ULog.d(BaseSettings.TAG, ">>>>>>>>>> onSuccess jsonStr: " + vo.getResponseData());
if (vo.getResponseCode() == ResponseObject.SUCCESS) {
switch (vo.getRequestIdentyCode()) {
case HttpInterface.REQUEST_USER_STOCK:
if (vo.getResponseData() != null) {
CustomToast.callToastFrag(getActivity(), vo.getResponseMessage());
Type listType = new TypeToken<List<UserStockVo>>() {}.getType();
stockItemList = new Gson().fromJson(vo.getResponseData().toString(), listType);
if (stockItemList != null && stockItemList.size() > 0) {
adapter = new HomeStockRecyclerViewAdapter(stockItemList, getActivity(), false);
} else {
stockItemList = new ArrayList<>();
stockItemList.add(new UserStockVo("", "", "", "", "", "", "", "", "", "", ""));
adapter = new HomeStockRecyclerViewAdapter(stockItemList, getActivity(), true);
}
binding.recyclerView.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
break;
}
} else {
alertMsgDialog(vo.getResponseMessage());
}
}
}
3. 비동기 요청 취소 처리
비동기 요청이 완료되기 전에 Fragment
가 제거되거나 onDestroyView()
가 호출될 수 있다. 이런 경우 요청을 안전하게 취소하거나 onSuccess()
호출을 막아야 한다.
onDestroyView()
에서 리스너를 제거한다!
@Override
public void onDestroyView() {
super.onDestroyView();
if (requestApi != null) {
requestApi.removeListener(this);
}
binding = null;
}
4. onDestroyView
와 onSuccess
타이밍 처리
안전한 리스너 등록 및 제거를 위해, onCreateView
에서 리스너를 등록하고 onDestroyView
에서 제거한다.
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
binding = FragmentHomeBinding.inflate(inflater, container, false);
requestApi = ((StockProfitManageApplication) requireContext().getApplicationContext()).getRequestApiManager();
requestApi.addListener(this); // 리스너 등록
searchUserStockInfo("", "");
return binding.getRoot();
}
@Override
public void onDestroyView() {
super.onDestroyView();
if (requestApi != null) {
requestApi.removeListener(this); // 리스너 제거
}
binding = null; // binding 해제
}
@Override
public void onSuccess(ResponseObject vo) {
if (binding == null) {
Log.d(BaseSettings.TAG, "Binding is null. Skipping onSuccess.");
return;
}
// onSuccess 코드...
}
요약
binding
초기화 상태를 확인하고,null
검사 후 사용한다.onDestroyView()
에서binding
해제 후 비동기 요청에 대한 리스너를 제거한다.- 비동기 요청 처리 시점과
Fragment
생명주기를 조율한다.
오류 해결을 위해 ChatGPT를 이용하였는데, 너무나 좋구나!! 웹 검색에서 낭비하는 시간에 하나라도 더 코딩할 수 있게 되었다. 오류 해결을 위한 스트레스도 그만큼 줄어든것이다!! 개발하기 정말 편한 세상이 되었네. 고맙다 쳇GPT