Android # Androidx (2/2)
안드로이드/Issue

Android # Androidx (2/2)

AndroidX : Android Extension Libraries

Support Library를 사용하지 않고, 앱을 개발하는 것은 불가능할 정도로 Support Library는 안드로이드 개발 생태계의 큰 축이었다. 이전 버전의 안드로이드에 대한 하위 호환성 유지, 안드로이드 프레임워크가 제공하지 않는 위젯 및 유틸리티 기능 지원, TV, 차량, 웨어러블과 같은 다양한 폼 팩터에 대응하기 위한 기능을 제공한다. 하지만 이런 기능적인 측면과 달리 몇 가지 설계적인 한계도 갖고 있었다.


Support Library의 큰 집합 중 하나이자 2011년 최초로 배포된 com.android.support:support-v4를 예로 들면, 첫째로 메이븐 아티팩트와 패키지명이 주는 모호함이다.
support-v4, support-v13이라는 이름에서 v4, v13을 리비전 정보로 착각할 여지가 있다. v13이 v4를 포함하는 최신 버전이라고 말이다. 이 네이밍은 API 레벨 4 이상에서 사용할 수 있는 지원 라이브러리라는 의미이다. 하지만 minSdkVersion이 19 이상만 되어도 95% 이상을 지원하는 시대 상황을 놓고 보면 support-v4, support-v13, appcompat-v7, cardview-v7과 같은 API 레벨을 의미하는 이름은 시간이 지날수록 퇴색될 수 밖에 없었다.
놀라운 것은 support-v4가 더 이상 API 레벨 4를 지원하지 않는다는 것이다. 원래의 의도와 달리 v4는 24.2.0 배포에서 API 8 이하 지원을 중단했으며(현재는 API 레벨 14 이상만 지원) 동시에 이름이 가지는 규칙성을 상실했다.
AndroidX 1.0.0은 서포트 라이브러리의 마지막 버전인 28.0.0을 기반으로 새로운 이름과 버전 규칙으로 재정의되었다.


두 번째는 모놀리식(Monolithic) 형태의 라이브러리 배포이다. 안드로이드 5.0이 메이저 버전의 OS가 되기 이전에 안드로이드 앱을 개발한 경험이 있다면 DEX 파일의 “64K reference limit” 명세로 인한 적잖은 어려움을 겪었을 것이라고 들었다. 이는 하나의 DEX 파일에 담을 수 있는 메서드 레퍼런스는 총 65,536개라는 제약에서 비록되었는데, Dalvik 런타임은 기본적으로 APK당 하나의 DEX 파일만 로드를 하기 때문에 앱의 규모가 커짐에 따라, 혹은 사용하는 라이브러리 내의 메서드가 많을수록 이런 문제에 더 빨리 직면하게 되는 것이었다.

당시에 많이 사용되던 서드파티 라이브러리의 수

 

이런 문제를 일으키는 주범은 Google Play Services였다고 한다. 6.5 버전부터는 라이브러리가 기능 별로 모듈화되었지만, 그 이전에는 3만 개에 가까운 함수를 제공하는 단일 라이브러리였다. 여기에 support-v4, appcompat-v7를 추가로 사용하면(위의 사진에서 확인할 수 있다시피) 약 8천 개와 1만 2천 개가 더해져서 이 세 라이브러리만으로도 DEX의 한계치인 6.5만의 75% 이상을 차지하는 일이 발생했다다. ART 런타임 및 멀티덱스 지원 라이브러리가 나오면서 이 문제는 기술적으로 완전히 해결됐지만, support-v4는 이보다 2년 뒤인 24.2.0 버전이 출시되기 전까지 단일 라이브러리로 제공되었다고 한다.




AndroidX 장점 중 하나는 위젯 단위의 세분화된 형태로 라이브러리를 제공하는 것이다. 덕분에 메이븐 그룹 ID와 아티팩트 ID는 독립적이고, 직관적으로 정의되었다. 예를 들어, Supoort Library에서 ViewPager는 com.android.support:support-core-ui:28.0.0 아티팩트에 포함되어 있다. support-core-ui에는 ViewPager뿐만 아니라 DrawerLayout, CursorAdapter, SwipeRefreshLayout, SlidingPaneLayout 등도 포함하고 있다. 이런 구조로 인해 실제로 사용하지 않는 위젯 꾸러미들을 Gradle 빌드 시스템으로 내려받고, APK에 포함해야만 하는 비효율이 존재했다.


반면 AndroidX에서는 androidx.viewpager:viewpager:1.0.0으로 분리되어 원하는 기능만 독립적으로 사용하는 게 가능해졌다. 이로 인해 프로가드, 멀티덱스를 사용하지 않는 빌드에서(예를 들면 테스트 빌드) 이전보다 APK를 더 경량화 할 수 있게 됐으며 빌드 속도 면에서도 이득을 볼 수 있다.

Support Libarary의 다른 문제점은 바이너리 호환성에 대한 제약이다. 만약 다음과 같이 27.1.1을 사용하다가 cardview-v7에서 문제가 발생해 27.1.0으로 다운그레이드 하는 경우 appcompat-v7, design도 27.1.0으로 일괄 변경해야만 했다. 메이저가 아닌 마이너 버전 업데이트인 경우에도 마찬가지이다.
서포트 라이브러리는 패키지 별로 의존성이 높기 때문에 서포트 라이브러리 개발팀은 새로운 버전을 배포할 때마다 의존하는 모든 패키지를 함께 배포하는 형태를 취한다. 이건 라이브러리를 배포하는 입장에서도 꽤나 성가시고 손이 많이 가는 일일 것이다. 결론적으로 항상 같은 리비전의 서포트 라이브러리를 사용하는 것이 안드로이드 개발자의 규칙이었다. 동시에 서포트 라이브러리 버전 업그레이드가 부담스러운 이유이기도 했고, 혹시나 문제가 발생하게 되면 모든 서포트 라이브러리의 버전을 다운그레이드 해야만 했다.


AndroidX는 라이브러리를 사용할 때 개발자가 보편적으로 기대하는 바이너리 호환을 지원한다. 서포트 라이브러리만의 독자적 버저닝에서 벗어나 X.Y.Z 형태의 시메틱 버저닝을 사용한다. 따라서 어떠한 코드가 버전 1.4.0에 의존하고 있다면 주 버전(=X)이 같은 1.5.0, 1.8.0과의 호환을 보장한다.
AndroidX의 바이너리 호환성으로 인해 만일 RecyclerView가 1.0.0에서 1.1.0으로 업데이트됐다면 해당 라이브러리의 버전을 올리는 것만으로 새 버전을 사용할 모든 준비가 끝난다.

implementation 'androidx.recyclerview:recyclerview:1.1.0'

 

마지막으로

서포트 라이브러리는 28.0.0을 끝으로 더 이상 업데이트되지 않는다고 한다. Android Q에서는 서포트 라이브러리 29.0.0을 기대할 수 없다. 따라서 현존하는 모든 안드로이드 프로젝트는 필연적으로 AndroidX로 마이그레이션 할 수밖에 없는 상황에 놓여있다. 코틀린으로의 변화까지는 아니겠지만, 안드로이드 생태계에서의 큰 변화를 가져오는 것이라고 생각한다.