티스토리 뷰
지금까지는 ViewHolder 안에 itemView를 넘기고 findByViewId()를 이용하여 필요한 View를 찾아 사용하는 방식을 사용했었다.
하지만 Kotlin-Extension을 이용하면 findByViewId() 함수를 소스코드에 직접 사용하지 않고 간단히 View에 접근 할 수 있다.
class BoxViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(item: Box) {
itemView.txt_name.text = item.name
}
}
얼핏 보기에는 간단히 해결! 처럼 보일 수 있지만 사실은 그렇지 않다.
Android Studio-Tools-Kotlin-Kotlin Byte Code에 들어가 decomplie 해보자.
public static final class BoxViewHolder extends ViewHolder {
public final void bind(@NotNull Box item) {
Intrinsics.checkParameterIsNotNull(item, "item");
View var10000 = this.itemView;
Intrinsics.checkExpressionValueIsNotNull(var10000, "itemView");
TextView var2 = (TextView)var10000.findViewById(id.txt_name);
Intrinsics.checkExpressionValueIsNotNull(var2, "itemView.txt_name");
var2.setText((CharSequence)item.getName());
}
public BoxViewHolder(@NotNull View itemView) {
Intrinsics.checkParameterIsNotNull(itemView, "itemView");
super(itemView);
}
}
bind 함수 안에 4번 라인을 보고 이상한 점을 발견 할 수 있었다.
Kotlin-Extension을 이용하여 간단하게 변경한 View에 대한 접근은 당연하게도 매번 View를 그릴 때 마다 findViewById가 호출되고 있었고 이는 findByViewId 같은 함수의 호출을 줄여 성능을 향상시키기 위한 ViewHolder 패턴의 의미가 없어지고 만다.
그렇다면 어떻게 해야할까?
문제는 생각보다 간단하게 해결할 수 있다.
LayoutContainer를 ViewHolder에 implements 해주면 된다. (변수에 저장하여 findByViewId를 한 번만 호출하게 하여 사용할 수도 있지만 해당 글의 주요 내용이 아니므로 넘어가겠다.)
LayoutContainer를 implements 해주게되면 containerView라는 변수가 새로 생성자에 생기게 되고 이는 view를 캐시해두고 보여준다고 한다.
/**
* A base interface for all view holders supporting Android Extensions-style view access.
*/
public interface LayoutContainer {
/** Returns the root holder view. */
public val containerView: View?
}
LayoutContainer에는 containerView가 들어있다.
class BoxViewHolder(override val containerView: View) : RecyclerView.ViewHolder(containerView), LayoutContainer {
fun bind(item: Box) {
containerView.txt_name.text = item.name
}
}
itemView를 containerView로 변경하였다.
interface안에 변수 한개밖에 없었는데 과연 캐시가 될까..?
확인을 위해 Android Studio-Tools-Kotlin-Kotlin Byte Code에 들어가 decomplie를 실행해보자.
public View _$_findCachedViewById(int var1) {
if (this._$_findViewCache == null) {
this._$_findViewCache = new HashMap();
}
View var2 = (View)this._$_findViewCache.get(var1);
if (var2 == null) {
View var10000 = this.getContainerView();
if (var10000 == null) {
return null;
}
var2 = var10000.findViewById(var1);
this._$_findViewCache.put(var1, var2);
}
return var2;
}
신기하게도 _$_findCachedViewById라는 함수가 생성된 것을 확인할 수 있다.
그렇다면 마찬가지로 view에도 _$_findCachedViewById()가 적용되어있을까?
public final void bind(@NotNull Box item) {
Intrinsics.checkParameterIsNotNull(item, "item");
TextView var10000 = (TextView)this.getContainerView().findViewById(id.txt_name);
Intrinsics.checkExpressionValueIsNotNull(var10000, "containerView.txt_name");
var10000.setText((CharSequence)item.getName());
}
띠용?! 이럴 수가 findViewById()을 그대로 사용하고 있다. 무엇이 문제일까?
문제는 아래 두 가지의 차이에서 확인할 수 있다.
(X)
import kotlinx.android.synthetic.main.item_box.view.*
class BoxViewHolder(override val containerView: View) : RecyclerView.ViewHolder(containerView), LayoutContainer {
fun bind(item: Box) {
containerView.txt_name.text = item.name
}
}
(O)
import kotlinx.android.synthetic.main.item_box.*
class BoxViewHolder(override val containerView: View) : RecyclerView.ViewHolder(containerView), LayoutContainer {
fun bind(item: Box) {
txt_name.text = item.name
}
}
어떤 차이로 인해서 containerView.뷰와 그냥 뷰에 접근하는거에 대해 다른 결과가 나오는 것일까.. (이 것은 더 알아보도록 하자... 아직 잘 모르겠다..)
다시 한 번 decompile해서 보면?!
public final void bind(@NotNull Box item) {
Intrinsics.checkParameterIsNotNull(item, "item");
TextView var10000 = (TextView)this._$_findCachedViewById(id.txt_name);
Intrinsics.checkExpressionValueIsNotNull(var10000, "txt_name");
var10000.setText((CharSequence)item.getName());
}
이제는 정상적으로 _$_findCachedViewById를 사용하고 있는 것을 확인할 수 있다.
주의해서 사용하도록 하자..(내가 잘못 사용했었다..)
참고
https://www.androidhuman.com/lecture/kotlin/2017/11/26/kotlin_android_extensions_on_viewholder/
https://thdev.tech/kotlin/2018/05/22/Android-Studio-Kotlin-Decompile/
'Android' 카테고리의 다른 글
Inconsistency detected 에러에 대한 의심.. (0) | 2020.01.11 |
---|---|
Single 과 Observable의 차이 (0) | 2019.12.29 |
Kakao REST API 사용하기 (2) | 2019.06.12 |
네이버 아이디 로그인 API를 사용해보자 (1) | 2018.08.08 |
기상청에서 제공하는 Json 파일로 지역코드와 지역명 파싱하기 (0) | 2018.05.19 |