Android

[Android] findViewById 동작 원리 파헤치기

yujinius 2024. 11. 9. 20:46
안드로이드 findViewById 동작 원리 파헤치기

https://velog.io/@ows3090/Android-findViewById-원리

 

[Android] findViewById 원리

findViewById란? 안드로이드 개발에서 findViewById 메소드는 레이아웃에 있는 뷰를 리소스 id를 통해서 원하는 뷰 객체를 가져올 수 있는 메소드입니다. 그 전에 setContentView와 같은 메소드로 xml에 있는

velog.io

 

이 글은 findViewById의 내부 동작 원리를 파악하기 위해 작성된 것이다. 위의 링크를 참고하여 직접 코드를 살펴보며 확인하고자 한다.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    enableEdgeToEdge()
    setContentView(R.layout.activity_main)
    ViewCompat.setOnApplyWindowInsetsListener(**findViewById(R.id.main)**) { v: View, insets: WindowInsetsCompat ->
        // ...
    }
    // ...
}

findViewById 파고들어 가 보기

findViewById는 Activity에서 호출 시 Window의 findViewById를 호출하게 되고, Window에서는 View의 findViewById를 호출한다.

[Activity.java] findViewById

//Activity.java
@Nullable
public <T extends View> T findViewById(@IdRes int id) {
    return getWindow().findViewById(id);
}

public Window getWindow() {
    return mWindow;
}

@UnsupportedAppUsage
private Window mWindow;

▼ 파고들기

[Window.java] findViewById

// Window.java
@Nullable
public <T extends View> T findViewById(@IdRes int id) {
    return getDecorView().findViewById(id);
}

public abstract @NonNull View getDecorView();

▼ 파고들기

[View.java] findViewById ⇒ findViewTraversal

// View.java
@Nullable
public final <T extends View> T findViewById(@IdRes int id) {
    if (id == NO_ID) {
        return null;
    }
    return findViewTraversal(id);
}

protected <T extends View> T findViewTraversal(@IdRes int id) {
    if (id == mID) {
        return (T) this;
    }
    return null;
}

  • Activity의 findViewById는 Window에 있는 것을 호출하고, Window의 findViewById는 View에 있는 것을 호출하여 최종적으로 findViewTraversal 메서드까지 호출하게 된다.
  • findViewTraversal은 호출된 View의 ID와 매개변수의 ID를 비교하여 동일할 때 View 자체를 리턴하는 구조다.

[ViewGroup.java] ViewGroup의 findViewTraversal

// ViewGroup.java
@Override
protected <T extends View> T findViewTraversal(@IdRes int id) {
    if (id == mID) {
        return (T) this;
    }

    final View[] where = mChildren;
    final int len = mChildrenCount;

    for (int i = 0; i < len; i++) {
        View v = where[i];

        if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
            v = v.findViewById(id);

            if (v != null) {
                return (T) v;
            }
        }
    }

    return null;
}

  • ViewGroup의 findViewTraversal은 자식 뷰 배열인 mChildren을 순회하며 매칭되는 ID를 찾는다. 일치하는 뷰가 발견되면 반환하고 탐색을 종료한다.
  • 만약 자식 뷰 중에 또 다른 ViewGroup이 존재한다면, 해당 ViewGroup의 findViewById와 findViewTraversal이 재귀적으로 호출된다. 결과적으로 트리 구조에서 깊이 우선 탐색(DFS) 방식으로 각 뷰의 ID가 동일한지 하나씩 확인하면서 찾는 방식으로 동작한다.
  • 결과적으로, findViewById는 여러 ViewGroup을 순회하며 필요한 View를 찾는 방식으로 동작한다. 이러한 구조 때문에 findViewById를 여러 번 호출하면 시간 복잡도가 증가하는 이유도 여기에 있다. 또한 특히, 순회 중에 null이 반환되면 런타임 오류가 발생할 위험이 있다.

이러한 구조를 이해함으로써 컴파일 타임에 바인딩을 지원하는 ViewBinding이 왜 필요한지 그 이유를 알게 되었다.

 


다음으로 읽으면 좋은 글 추천

https://yujinius45.tistory.com/124

 

[Android] 안드로이드에서 findViewById 완벽 가이드: 사용법, 동적 뷰 조작, 문제점과 해결 방법

안드로이드에서 findViewById 완벽 가이드: 사용법, 동적 뷰 조작, 문제점과 해결 방법목차findViewById 개요findViewById 기본 사용법: MainActivity에서 뷰에 접근하기동적 뷰 삽입 및 삭제 실습Layout Inspector

yujinius45.tistory.com