티스토리 뷰

Android

LayoutContainer는 캐시가 되고 있을까?

갈치참치꽁치멸치 2020. 1. 18. 18:04

지금까지는 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/

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함