✅ ViewBinding 등장배경
-
기존에는 view를 가져와 사용할때 개발자가 직접 view의 타입을 작성해야했다(findViewById()를 사용하여 각 view component들을 객체로 만들어서 사용하기 위해).
그러다보니 타입을 잘못 적으면 일치하지 않는 타입이기 때문에 null 에러가 발생하기도 했다
-
이런 문제점을 해결한게 View Binding이다. ViewBinding은 binding class생성 시 개발자가 아닌 컴파일러가 뷰 객체의 참조값을 가져와 멤버변수에 저장한다.
-
따라서 binding class의 멤버 변수 타입이 뷰와 다른 타입으로 선언된다거나 ID값이 틀려 존재하지 않아 NULL을 저장한다거나같은 문제가 발생하지 않는다
등장배경: 기존의 findViewById를 호출해서 특정 뷰 객체의 참조값을 사용하는
반복적인 코드를 해결하기 위해View Binding 등장
목적: Fragment나 Activity에서 원하는 뷰 객체의 참조를 간편하게 바로 가져다 쓸 수 있도록 함
✅ ViewBinding 사용
- build.gradle 파일에 View Binding을 사용하겠다는 정의를 해두면 앱 빌드 시 컴파일러가 자동으로 해당 모듈 내의 모든 xml 파일에 대해, 뷰 객체들의 참조를 어딘가에 미리 가져와 놓는다. 여기서 ‘어딘가에’ 라는 곳이 그래서 어딜까?→View Binding은 모듈 별로 사용 설정한다. 설정이 되면 각 xml 파일에 대해 binding class 를 자동 생성 한다.
- 예를 들어, 만약 activity_main.xml 라는 파일이 App 모듈 내에 존재한다면 컴파일러는 ActivityMainBinding.java이라는 이름의 binding class를 자동으로 생성한다.
- 이 클래스의 멤버 변수(=properties)들은 activity_main.xml에 정의된 모든 뷰(단, ID가 정의되어 있지 않은 뷰는 제외) 객체들로 선언되고, 각 뷰 객체의 참조값이 멤버 변수에 저장된다.
- activity_main.xml 파일 뿐만 아니라 모든 xml 파일에 대응되는 binding class가 하나씩 생성
- 자동으로 생성되는 바인딩 클래스 이름은 규칙이 정해져 있다
build.gradle 파일에 viewBinding = true 해두면
컴파일러가 자동으로 해당 모듈에 binding class를 생성
✅ ViewBinding 특징
- viewBinding은 빌드 과정에서 생성된다.
- 끝에 Binding이란 단어가 붙는다.
- 카멜 표기법에 따라 네이밍이 된다.
- ID가 존재하지 않는 뷰에 대해선 클래스에 참조가 존재하지 않는다.
- getRoot()메서드가 자동 포함 된다. layout file의 루트 뷰에 관한 직접 참고 제공.
🔎 findViewById와의 차이?
- null safety - 뷰 직접 참조로 없는 아이디로 널포인트 익셉션 발생 안한다
- type safety - 뷰 타입이 일치함으로 Class Cast Exception 발생 안한다
- findViewById보다 빠르다 - findViewById는 ViewGroup 밑에 있는 모든 뷰들을 전부 한 번씩 순회하며 id 값을 비교한다. 관찰 대상인 ViewGroup, 즉 layout이 복잡하면 복잡해질 수록 순회해야 할 View가 많아지므로 작업량이 많아진다. ViewBinding은 Binding 클래스 생성 시에 id가 있는 모든 View를 참조하고 있기 때문에 매번 id값을 순회해서 비교해야하는 작업이 필요없어서 빠르다
🔎 View를 호출할 일이 없는 layout 파일도 Binding 클래스로 미리 변환해둔다면, 그것도 일종의 자원 낭비 아닌가?
binding 클래스를 생성하는 동안 특정 레이아웃 파일을 바인딩 클래스로 변환하지 않고 싶다면 해당 레이아웃 파일의 최상단에 tools:viewBindingIgnore 속성을 true로 설정하면 된다.
🔎 DataBinding과의 차이?
일단, 둘은 모두 뷰를 직접 참조하는데 사용할 수 있는 binding class 를 제공한다.
확실히 viewbinding은 보다 단순한 처리의 경우 적합하다
- 더 빠른 컴파일 = BindingAdapter 사용이 안되므로 annotation처리를 해줄 필요가 없어서 컴파일이 빠르다.
- 사용 편의 - tag처리된 xml 불 필요로 사용 용이. (모듈에서 binding 사용 설정만을 통해 자동으로 모든 레이아웃의 binding class 생성)
✅binding class
// activity_main.xml 예시
<ConstraintLayout><TextView android:id="@+id/hello" />
<Button android:id="@+id/button1" />
</ConstraintLayout>
⬇️
// ActivityMainBinding.java 클래스 예시
public final class ActivityMainBinding implements ViewBinding {
@NonNull
private final ConstraintLayout rootView; // rootView 라는 변수는 항상 자동으로 선언됨
@NonNull
private final TextView hello;
@NonNull
private final Button button1;
}
- ID가 존재하는 모든 뷰의 참조값이 멤버변수로: binding class에는 xml 파일에 정의되어 있고 ID가 존재하는 모든 뷰들의 참조값을 저장할 멤버 변수들이 선언된다. 따라서 개발자는 Activity나 Fragment 같은 클래스에서 binding class의 인스턴스를 생성한 후, 해당 binding class 내의 멤버 변수를 그냥 가져다 사용하기만 하면 된다
- rootView: rootView라는 이름의 멤버 변수는 자동으로 항상 선언되는데 이 변수에는 해당 xml 파일에 존재하는 모든 뷰들 중 가장 top에 존재하는 뷰 객체의 참조값이 저장된다. 위에서 예시로 든 activity_main.xml을 예로 들면 ConstraintLayout이 root view이다
- getRoot() 라는 public 멤버 메소드: binding class 안에는 getRoot() 라는 public 멤버 메소드도 자동으로 정의되는데, 이 메소드는 해당 xml 파일의 root view 객체의 참조값을 반환한다. (root view는 xml 파일에 존재하는 모든 뷰들 중 가장 top에 존재하는 뷰이다.)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(layoutInflater)
binding.hello.text = "안녕"
...
val view = binding.root
setContentView(view)
}
ID가 존재하는 모든 뷰의 참조값이 binding class의 멤버변수가 됨
top view가 되는 rootView를 얻을 수 있음
✅ DataBinding 등장배경
textView.text = "안녕"
binding.textView.text = "안녕"
- 원래 뷰에 데이터 바꾸려면 위처럼 코드상으로 했었다.
- 그런데, Activity, Fragment에는 뷰 이동같은 로직을 위한 코드만 남기고 뷰와 관련된 작업은 XML파일에서 해결하고 싶다! 즉 데이터와 뷰 연결 작업을 레이아웃에서 처리하기 위해 등장한게 DataBinding이다
viewbinding의 역할을 하면서 레이아웃에서 데이터와 뷰 연결작업을 하고 싶어 Databinding등장
✅ DataBinding vs ViewBinding
- View Binding: xml 파일에 정의해놓은 뷰들을 실제 메모리에 올리고 코드로 조작할 수 있게 하기 위해 해야하는 몇 가지 작업 을 이전보다 쉽게 할 수 있도록 해주는 기능이 View Binding 이였다.
->메모리에 얹혀놓고 타입,null안전성을 가지면서 쉽게 view참조
- Data Binding:이러한 View Binding 과 비슷하게 Data Binding 은 어떠한 뷰에 보여질 데이터들을 연결하는 작업을 이전보다 쉽게 할 수 있도록 지원하는 기능
-->엑티비티에서 연결해줘야하는 데이터 작업을 xml에서 한방에 하니까 엑티비티 코드가 깔끔
—>layout 파일인 xml 파일 내에서 데이터를 연결할 수 있기 때문에 UI 뷰 객체를 선언하거나 연결하는 코드 자체가 필요 없게 된다
databinding은 null안전성을 가지며 뷰를 참조할 수 있는 viewBinding역할을 함
동시에 activity, fragment에서 데이터 연결 작업을 하지 않아도 되어서 역할 분리 확실해짐
✅ 데이터바인딩, 뷰바인딩 공통점/차이점
- 공통점: Binding 클래스는 해당 레이아웃에서 ID가 존재하는 모든 View를 직접 참조하고 View 타입이 일치하기 때문에 Null Pointer Exception, Class Cast Exception이 발생하지 않는다
- 차이점
- 데이터 바인딩은 뷰 바인딩의 역할도 할 수 있을 뿐더러 Binding Adapter을 제공해 동적 UI 콘텐츠 선언, 양방향 데이터 결합도 지원 → null 안전성을 가지며 쉽게 view를 참조할 수 있는 viewBinding의 역할도 한다
- 데이터 바인딩이 기능은 다양하지만 뷰 바인딩이 상대적으로 간단하며 퍼포먼스 효율이 좋고 용량이 절약된다는 장점이 있다.
- 실제로 구글 공식문서에서는 단순히 findViewById를 대체하기 위한 거라면, 뷰 바인딩을 사용할 것을 권장하고 있다.
- 용량-> Databinding = 2670 kb / ViewBinding = 2620 kb
위의 결과는 같은 구조의 프로젝트에 각각 Data Binding과 View Binding으로 바인딩하여 구현한 프로젝트의 APK 크기들이다. 위의 결과에서 APK 크기가 50kb 차이나듯이 바인딩 방법에 의해 영향을 받게 되고, 충분히 View Binding 사용을 고려할만한 가치가 있다.
ViewBinding이 컴파일 속도가 빠른 이유
ViewBinding은 BindingAdapter등에서 annotation processor를 사용하지 않아 애노테이션에 대한 코드베이스를 검사, 수정 또는 생성하는 부가시간이 들지 않아서 DataBinding 보다 빠르다
*Annotation?
애노테이션은 자바 소스 코드에 추가 할 수있는 메타 데이터의 한 형태입니다. 클래스, 인터페이스, 메소드, 변수, 매개 변수 등에 추가 할 수 있다
*왜 애노테이션을 써야할까요?
Annotation Processor는 실제로 javac 컴파일러의 일부이므로 모든 처리가 런타임보다는 컴파일시간에 발생해서 런타임 시간을 줄일 수 있다
*Annotation Processor란?
일반적으로 애노테이션에 대한 코드베이스를 검사, 수정 또는 생성하는데 사용된다. 본질적으로 애노테이션 프로세서는 java 컴파일러의 플러그인의 일종이다
어노테이션 프로세싱이란 @명령어처럼 사용하는 주석 형태의 문자열을 실제 코드로 생성해주는 것.
@으로 시작하는 명령어를 어노테이션이라고 하는데 어노테이션이 컴파일 시에 코드로 생성되기 때문에 실행시 발생할 수 있는 성능 문제 개선됨
Room을 사용하면 클래스명이나 변수명 위에 @어노테이션을 사용해서 코드로 자동변환되주니까 편함
dabinding의 장점은 null안전성을 가지며 쉽게 view를 참조할 수 있는 databinding의 역할을하며
레이아웃과 activity,fragment의 데이터 연결 작업을 레이아웃에서 처리할 수 있게함
하지만 databinding보다 viewbinding이 컴파일 속도가 빠르고(어노테이션) 용량 측면에서 이점
✅ DataBinding 사용법
1. xml 파일 구성하기
- data binding을 사용하여 xml 파일 내에서 바로 데이터를 연결시키기 위해서는 기존의 xml 파일을 작성하던 방식과 조금 다르게 작성 필요
- 레이아웃의 루트태그를 < layout >으로 감싸주고 그 안에 < data >태그를 추가해야한다
- 이 태그 안에 xml 파일 내에서 연결한 데이터를 표시하는 태그와 다른 뷰 태그들이 위치하게 된다.
- 기존 xml 파일을 작성할 때는 루트 태그에 바로 같은 태그를 사용했었는데 data binding 을 사용할 때는 루트 태그를 태그로 먼저 크게 감싼 후, 그 안에 데이터와 뷰들을 작성하는 방식이라는 것을 이해할 수 있을 것이다.
- 하지만 태그는 데이터와 뷰를 감싸주는 역할만 할 뿐 ConstraintLayout이나 LinearLayout 처럼 Android 앱 개발에 직접 사용되는 Layout은 아니다. 따라서 어떤 xml 파일에 태그만 존재한다면 컴파일러는 에러를 발생시킬 것이다.
- 반드시 태그는 와 같은 실제 레이아웃을 포함해야 한다.태그는 해당 xml 파일에서 데이터 연결을 위해 변수처럼 사용할 것들을 정의하는 태그이다.
- 태그 안에 이라는 “변수” 태그가 존재하고, 이 태그의 속성으로 name과 type을 지정해주면 된다.
• xml 안에서 사용되는 여러 속성들의 속성값으로 태그 안에서 정의해준 변수들을 사용하려면 @{} 구문을 사용
layout / data 루트태그 추가 안에 변수를 정의해야함. 또한 실제 레이아웃 포함해야함
2. xml 파일 inflate시키기
- 위 단계를 거쳐 레이아웃 파일을 완성했으면 레이아웃과 엑티비티를 연결해줘야함
- data binding도 view binding과 마찬가지고 각 xml파일마다 binding class가 자동으로 생성됨
- 다만, view binding에서의 binding class와 다른 점은 data binding에서의 binding class에는 뷰에 대한 binding 정보 뿐만 아니라 태그에서 정의된 데이터 변수들에 대한 binding 정보도 포함되어 있음
- MainActivity.kt 파일의 onCreate 콜백 메소드에 activity_main.xml 파일을 inflate 시켜보자
- binding class를 인스턴스화 하는 과정에서 DataBindingUtil 이라는 클래스 안의 setContentView() 메소드를 통해 활성 뷰를 activity_main.xml 로 설정하면 된다.그 다음, activity_main.xml 파일에서 데이터 변수로 사용되었던 user이 실제로 어떤 데이터인지 명시해주면 된다!
- 이건 view binding에서 사용하던 방식인데 setContentView의 인자로 연결하고자하는 레이아웃을 지정해주는군
✅Binding Adapter?
- 뷰의 속성을 설정하는 메서드는 여러 가지가 있다.당장 텍스트 뷰만 해도 텍스트 크기, 텍스트 컬러, 높이, 여백 등등 무수히 많은 옵션들이 있으니 말이다.그럼에도 불구하고 내가 원하는 기능의 메서드가 없다면 어떻게 해야 할까?액티비티에서 내가 원하는 메서드를 만들어 사용하듯이 레이아웃에서도 내가 원하는 메서드를 만들어 사용할 수는 없을까?. Binding Adapter를 사용하면 된다.
- image view에 이미지를 바인딩 시킬때는 text데이터를 연결한것처럼 하면 안된다 이때 Binding Adapter를 사용해야함
종속성 추가
- Adapter를 만들기에 앞서 이미지를 Glide라이브러리를 이용해 넣어줄거고, 어노테이션을 붙여야해서 Glide종속성과 어노테이션을 위한 종속성을 추가한다(app단위)
Adapter만들기
- 역할설명: 어노테이션 참고
- object: Binding Adapter는 메모리상에 올려서 사용해야 하기 때문에 Object로 생성한다.
- @JvmStatic: 전역 변수의 Getter Setter를 정적 함수로 설정하는 어노테이션이다.@BindingAdapter: 괄호 안에 원하는 메서드 이름을 지어주면 된다. 위 예제에서는 일부러 이상한 이름으로 지었다. (아무 이름으로 지어도 된다는 뜻에서..)< data >태그안에 변수 선언해주면 이제 바인딩 어댑터에서 정의해준 메서드를 레이아웃에서 사용할 수 있다. 텍스트뷰에서 아까 만든 setImage를 사용할 수 있다(이때 앞에 'android:' 나 'app:'을 붙이지 않아도 된다)이제 엑티비티 상에서 Databinding_image의 인스턴스 생성을 통해 데이터를 바인딩해서 넣어줌
data binding왜 쓸까
val url = "https://i.esdrop.com/d/i604ibqnl50b/OZ1dWvxKwh.jpg"
Glide.with(this@Library_Activity)
.load(url)
.into(glide)
- 하나의 엑티비티 안에서 이렇게 이미지를 로드하는 Glide 코드를 쓰는거보다 data binding을 통해서 이미지를 로드하면 view단의 코드와 로직 코드를 분리할 수 있기 때문 아닐까?
- 내가 여태 배웠던 클린 아키텍처, MVVM, View Model, Data Binding ···전부 궁극적으로는 역할의 분리를 위한 것들이었다.하나의 Activity에 모든 기능을 다 때려 넣는 게 아니라 각자 역할과 기능을 가지는 모듈로 잘게 쪼개자는 것이다. Binding Adapter도 이런 것들과 마찬가지로 바인딩이라는 작업을 액티비티로부터 떼어내기 위한 기술인 것이다.
✅표로 보기