[Android/Java] findViewById가 Null safety하지 않은 이유
안드로이드에서는 레이아웃(*.xml)에 작성한 View를 참조할 때 사용하는 가장 기초적인 방법으로 findViewById 메서드를 제공한다. 안드로이드 초기부터 있었던 방법이지만 여러 가지 문제점이 거론되기 시작하면서 다른 방법들로 대체되어 왔고 현재는 DataBinding과 ViewBidning을 가장 많이 사용한다. findViewById를 사용되지 않는 이유 중 하나로 "Null Safety 하지 않다"는 것이 있는데 앞서 말한 두 방식은 이 문제점을 보완하고 있다. Null safety는 무엇인지, findViewById에서 이 문제가 생기는 이유는 무엇인지, 바인딩 방식은 이 문제를 어떻게 보완했는지에 대해서 알아보자.
Null Safety(널 안전성)란 참조하려는 객체가 null일 경우 발생할 수 있는 Null Pointer Exception(이하 NPE)으로부터 안전한 코드라는 의미를 가진다. Null Safety의 의미와 앞서 말한 "findViewById가 Null safety하지 않다"는 말의 의미를 다시 해석해보면 "findViewById의 수행 결과로 얻은 객체는 Null로부터 안전하지 않다"고 바꿔말할 수 있을 것이다.
findViewById는 "R.id."과 같은 방식으로 현재 프로젝트에 있는 거의 모든 id에 접근할 수 있으며 파라미터로 전달한 id에 대한 view 객체를 반환한다. 이 방식은 말 그대로 모든 id에 접근할 수 있기 때문에 현재 레이아웃에 존재하지 않는, 즉 다른 레이아웃의 view에도 접근할 수 있다.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
Activity를 생성하면 기본적으로 콜백 메서드인 onCreate가 주어지는데 두 번째 라인에서 setContentView를 호출하며 레이아웃 id를 파라미터로 전달한다. 이 과정은 Activity의 레이아웃을 객체화하는 과정으로서 이 과정을 정상적으로 거쳐야 화면이 그려짐은 물론, findViewById로 객체에 대한 참조를 얻어와 사용할 때 NPE가 발생하지 않는다. 하지만 아직 생성되지 않은 레이아웃의 View를 호출하면 어떻게 될까?
TextView tv = findViewById(R.id.sub_activity_text_view);
MainActivity에서 SubActivity에 있는 textView에 접근해보자. SubActivity는 아직 메모리에 올라가있지 않기 때문에 NPE가 발생해야 할 것이다. 하지만 초기화만 해서는 빌드 단계에서 에러가 잡히지 않는다.
TextView tv = findViewById(R.id.sub_activity_text_view);
tv.setText("Hello");
문제는 위와 같이 참조한 객체를 사용할 때 발생한다. setText를 하기 위해서 TextView 객체를 참조했지만 해당 View는 아직 생성되지 않았기 때문에 NPE가 발생한다. 이 문제가 빌드 단계에서 잡히거나 테스트 단계에서 발견될 경우 에러를 초기에 해결할 수 있겠지만 만약 테스트 과정에서도 발견되지 않고 넘어갈 경우 사용자에게 있어서 어플이 강제 종료될 수도 있는 잠재적인 위협이 된다.
이것이 findViewById가 Null safety하지 못한 이유다. 반면 데이터 바인딩은 setContentView로 생성한 레이아웃에 있는 View들에 밖에 접근할 수 없다. findViewById와 다르게 접근 범위를 setContentView를 호출하며 전달한 레이아웃(id)으로 제한한 것이다.