Databinding 사용한 예시를 만들기 전에 findViewByid의 작동 방식을 알아보고 가겠습니다.
목차
findViewByid 작동 방식과 문제점
우리가 뷰에 대한 참조를 얻기 위해서 findViewByid() 함수를 사용할 때마다 안드로이드 시스템은 런타임 중에 뷰 구조를(view hierarchy) 살펴보고 원하는 뷰를 찾아내는 작업을 합니다.
규모가 큰 앱의 경우 레이아웃이 많고 수 백개의 뷰가 있을 수 있습니다.
그럼 한 화면을 보여줄 때 안드로이드 시스템은 수 백번 뷰 구조를 보는 작업이 필요합니다.
-> 규모가 큰 앱의 경우 안드로이드 시스템에서 뷰를 참조하기 위한 많은 작업이 필요합니다.
적어도 오늘 날의 모바일 폰 시장에서 적어도 60Hz의 화면 재생 빈도(refresh frequency)를 가집니다.
화면 재생 빈도는 화면이 1초에 얼마큼 비추는지를 나타내는 값입니다.
즉 디스플레이가 초당 60회의 프레임을 나타낼 수 있습니다.
16밀리 초 마다 스크린을 다시 만듭니다..
안드로이드 시스템은 16밀리 초 안에 뷰의 구조를 살펴봐야 합니다.
몇몇 고급 사양의 폰들은 90Hz, 120Hz의 화면 재생 빈도(refresh frequency)를 가진다.
-> 기기 성능의 고도화로 인해 안드로이드 시스템은 뷰를 다시 만드는 데 더 적은 시간을 얻습니다.
결론 : findViewByid 함수를 통해서 런타임 중에 뷰를 반복해서 살펴보는 것은 좋지 않습니다. 규모가 큰 앱의 경우 앱의 성능을 감소시킵니다.
findViewByid 소스 코드 분석
findViewByid의 소스 코드를 분석해서 성능 상에 영향을 미칠 수 있는 부분을 찾아보겠습니다.
public final View findViewById(int id) {
if (id < 0) {
return null;
}
return findViewTraversal(id);
}
findViewByid 함수를 보시면 id가 0 이상인 경우 즉 유효한 경우에 `findViewTraversal` 을 호출 합니다.
@Override
protected View findViewTraversal(int id) {
if (id == mID) {
return 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 v;
}
}
}
return null;
}
findViewTraversal을 보시면 전달된 id가 ViewGroup의 ID와 같은지 여부를 확인하고 같은 경우에 자기 자신을 반환하고 아닌 경우 자기 자신이 가진 Child View에 대해 루프를 돌려 각 Child View에 findViewById를 호출하고 있습니다.
그렇다면 만약 ViewGroup안에 Child View가 100개가 있고 그 해당 뷰들을 찾아 넘길려면 최소 100번의 loop를 돌게됩니다.
이 부분에서 모든 뷰들을 불필요하게 확인하는 작업이 발생합니다.
DataBinding 작동방식
우리는 레이아웃에 속한 각각 뷰에 대한 참조를 가지는 binding 객체를 생성합니다.
binding 객체가 생성됐을 때 앱의 모든 컴포넌트들은 binding 객체를 통해서 뷰를 접근할 수 있습니다.
이 방법은 런타임 중에 뷰를 반복해서 살펴보지 않습니다.
findViewByid에 비해 Databinding이 가지는 이점
앱 성능을 향상시킴
findViewByid를 제거함으로써 코드를 더 간결하게 한다.
이는 코드를 읽고 유지 보수하는 게 더 수월해진다.
컴파일하는 도중에 에러를 발견할 수 있다.
findViewByid는 런타임 중에 발생하지만 Databinding은 컴파일 시 에러를 인지한다.
그러므로 에러가 있을 경우 사용자가 앱 사용 중에 에러를 마주치는 것을 방지할 수 있다.
사용법
gradle에 databinding 설정하기 (enable databinding)
Android Studio version 4.1 이상 버전의 경우 다음과 같이 추가합니다.
android {
...
buildFeatures {
dataBinding true
}
}
Android Studio version 4.1 이전 버전의 경우 다음과 같이 추가합니다.
android {
...
dataBinding{
enabled = true
}
}
레이아웃 xml 파일을 layout 태그로 감쌉니다.
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
...
binding 객체를 생성하고 사용합니다.
data binding 라이브러리는 레이아웃 파일 이름을 참고해 binding 객체를 생성합니다.
_(underscore)를 제거하고, 대문자를 추가하고 뒤에 Binding을 추가합니다.
예시) activity_main → ActivityMainBinding
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this,R.layout.activity_main)
}
객체를 사용한 Databinding
실제 앱은 로컬 디비나 API를 통해 가져온 데이터를 뷰에 보여주는 경우가 많습니다.
이런 경우 databinding을 통해 객체가 가진 데이터를 레이아웃 파일에서 넣을 수 있습니다.
데이터 클래스를 만듭니다.
data class Person (
var id:Int,
var name:String,
var email:String
)
layout 태그 안에 다음과 같이 variable 태그를 사용해 레이아웃 파일에서 사용할 데이터를 정의합니다.
값은 @{}를 통해 참조할 수 있습니다.
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="person"
type="com.example.databindingex.Person" />
</data>
<TextView
android:text="@{person.name}"
.../>
앱을 실행하면 다음과 같이 나옵니다.
코틀린의 apply scope 함수를 사용함으로써 중복된 코드를 줄일 수 있습니다.
binding.apply {
nameText.text = "Hello!"
}
참고 : developer.android.com/topic/libraries/data-binding/start?hl=en
전체 소스 : github.com/keepseung/Android-Blog-Source
'Android > Jetpack, Clean Architecture' 카테고리의 다른 글
[안드로이드/Android] LiveData 사용하기 (0) | 2021.01.25 |
---|---|
[안드로이드/Android] ViewModel 사용하기 (0) | 2021.01.20 |
모던 안드로이드 앱 만들기 (3) - Retrofit, RxJava를 이용한 네트워크 통신 (0) | 2021.01.18 |
모던 안드로이드 앱 만들기 (2) - MVVM 구조를 사용한 리스트 구현 (0) | 2021.01.17 |
모던 안드로이드 앱 만들기 (1) - 소개 (using Java, MVVM, RxJava) (0) | 2021.01.17 |