본문 바로가기
개발 이야기/안드로이드 개발

kotlin-android-extensions 를 걷어내자. 걷어내자!

by 정선한 2024. 3. 22.
728x90
반응형

이미 지원 종료된 플러그인이지만, 아직 제가 들고 있는 프로젝트에서는 살아 숨 쉬는 이 친구와 한번 절교를 해보려고 합니다.

먼저 gradle(:app)파일 내의 plugins에 적용되어 있는 'kotlin-android-extensions'을 삭제하고, buildFeature 내부에 viewBinding이 활성화되어있지 않으면 true로 활성화합니다. 제가 들고 있는 프로젝트에는 이미 viewBinding이 적용되어 있고, synthetic기능을 레거시로 혼용하여 쓰고 있던 형태이기 때문에 이미 활성화는 되어있었습니다.
kotlin-android-extensions을 통해 기 적용된 서비스를 보면, kotlin의 synthetic 기능을 사용하여 viewBinding을 간편하게 구현한 모습들을 볼 수가 있는데, 이는 kotlin-android-extension에서 제공하는 기능으로 해당 플러그인이 삭제되면 사용할 수 없게 됩니다. (빌드조차 돌아가지 않아서 오류 나는 포인트는 명확하게 android studio에서 확인할 수 있어요)
따라서 기 적용된 viewBinding 방식을 변경해야 하는 순간이 찾아오게 되는 것인데, 안드로이드에서 view를 객체화할 수 있는 방법은 총 3가지 정도였습니다. 이제는 2가지라고 볼 수 있겠네요.

  1. findViewById() 함수를 사용하여 View를 객체화 하는 방법
  2. kotlin-android-extension, synthetic 을 사용하여 View를 객체화하는 방법
  3. 마지막으로 viewBinding을 사용하는 방법

여기서 2번의 항목은 이제 지원하지 않는 내용입니다. 현재 서비스 중인 코드에 synthetic을 import하는 클래스가 있다면, 일단 확인해 보시길 바랍니다. 저도 알고 싶지 않았어요...ㅎ

  • 참고로 내용을 붙이자면, synthetic은 kotlin 코드 외부의 요소 (view, layout, ...etc)와의 조합을 통해 만들어진 property들을 칭하는 것으로 kotlin-android-extensions 내에는 Activity, Fragment, View 클래스 혹은 자식 클래스들에서 사용이 가능하고 XML 내에 정의된 뷰의 인스턴스에 바로 접근이 가능하도록 지원하는 기능입니다.
  • 따라서 binding처리 없이 id에 접근이 가능했었던 것이죠. 자체적으로 findViewById()를 생성해서 객체화 한 것처럼 사용할 수 있도록 만들어 줍니다.

이젠 findViewById(), viewBidning을 통해서 View를 객체화 하면 되는데, 사실 findViewById()는 view마다 작성해야 하기 때문에, 온 천지에 findViewById()를 사용해야 하는 단점이 있습니다. 이런 걸 boilerplate라고도 하는데,, 모르겠고,, 그냥 일단 덕지덕지 똑같은 문자열이 반복되어 있다? 눈에 보기 예쁘지 않다...라고 생각합니다. 그냥 못생겼잖아요.
그러니까 코드 개선을 viewBinding으로 해볼텐데, 기존에 kotlin synthetic을 사용하여 viewBinding을 구현했던 코드는 아래의 구현으로 되어있습니다.
사실 viewBinding을 쉽게 구현하게 해준 것이 synthetic의 기능이었기 때문에, 크게 달라지는 것은 없지만
주로 달라지는 것은 view객체 자체를 넘겨주던 것을 binding.getRoot()로 View를 객체화해서 넘겨주는 부분으로 수정되었습니다.
이 코드에서는 ViewHolder의 생성 부분이 크게 바뀌었다고 보시면 될 것 같아요.

import kotlinx.android.synthetic.main.[layoutName].view.*

class PagerRecyclerAdapter(private val itemList: MutableList<[DataModel]>) 
: RecyclerView.Adapter<PagerRecyclerAdapter.PagerViewHolder>() {

    inner class PagerViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

        fun bind(item: [View]) {
            Glide.with(itemView.context)
                .load([item.URL])
                .into(itemView.[id])
            }
        }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PagerViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(
            R.layout.[layoutName],
            parent,
            false
        )
        return PagerViewHolder(view)
    }

    override fun onBindViewHolder(holder: PagerViewHolder, position: Int) {
        holder.bind(itemList[position])
    }

    override fun getItemCount(): Int = itemList.size
}

이 아래가 viewBinding을 통한 RecyclerViewAdapter의 구현인데요. 위 코드에서 바뀐 점들을 한번 보시면 감이 오실 것 같습니다.

import [PackageName].databinding.[ClassName]Binding

class PagerRecyclerAdapter(private val itemList: MutableList<[DataModel]>)
    : RecyclerView.Adapter<PagerRecyclerAdapter.PagerViewHolder>() {

    inner class PagerViewHolder(private val binding: [ClassName]Binding)
        : RecyclerView.ViewHolder(binding) {

        fun bind(item: [View]) {
            Glide.with(binding)
                .load([item.URL])
                .into(binding.[id])
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PagerViewHolder {
        val binding = [ClassName]Binding.inflate(
            LayoutInflater.from(parent.context),
            parent,
            false
        )
        return PagerViewHolder(binding.root)
    }
    override fun onBindViewHolder(holder: PagerViewHolder, position: Int) {
        holder.bind(itemList[position])
    }

    override fun getItemCount(): Int = itemList.size
}

binding.root의 함수 내용입니다. View를 return 해 주는 것을 확인할 수가 있어요.

/**
    * Returns the outermost View in the layout file associated with the Binding. If this
    * binding is for a merge layout file, this will return the first root in the merge tag.
    *
    * @return the outermost View in the layout file associated with the Binding.
    */
@NonNull
@Override
public View getRoot() {
    return mRoot;
}

이 함수를 통해, PagerViewHolder innerClass에 binding 변수로 View 객체를 넘겨주면, 그 ViewBinding을 통해 id를 사용할 수 있는 그런 내용입니다.
자잘 자잘하게 코드 전반에 들어가 있는 내용들이고 또 이 부분이 리스트를 그리고, 화면을 그리는 내용들이라 사이드 이펙트도 적지 않게 발생하는 상황이 생기곤 했는데요. 어찌어찌 테스트 코드도 넣어가며 대응을 하기는 했네요 ㅎㅎ
이 포스팅의 목적은 android-kotlin-extensions을 걷어내는 과정을 다루는 것이 목적이었기 때문에 추가적인 기술적인 내용들은 다른 포스팅에서 다루어 보도록 하겠습니다.

 

참고문서

Kotlin 합성에서 Jetpack 뷰 결합으로 이전  |  Android 개발자  |  Android Developers

이 페이지는 Cloud Translation API를 통해 번역되었습니다. Kotlin 합성에서 Jetpack 뷰 결합으로 이전 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Kotlin Android 확

developer.android.com

 

728x90
반응형