bazel 모바일 설치

Android의 빠른 반복 개발

이 페이지에서는 bazel mobile-install이 Android의 반복 개발을 훨씬 더 빠르게 만드는 방법을 설명합니다. 이 접근 방식의 이점과 별도의 빌드 및 설치 단계의 단점을 설명합니다.

요약

Android 앱에 작은 변경사항을 매우 빠르게 설치하려면 다음 단계를 따르세요.

  1. 설치하려는 앱의 android_binary 규칙을 찾습니다.
  2. 기기를 adb에 연결합니다.
  3. bazel mobile-install :your_target을 실행합니다. 앱 시작이 평소보다 약간 느립니다.
  4. 코드 또는 Android 리소스를 수정합니다.
  5. bazel mobile-install :your_target을 실행합니다.
  6. 빠르고 최소한의 증분 설치를 즐기세요.

Bazel에 유용할 수 있는 몇 가지 명령줄 옵션은 다음과 같습니다.

  • --adb는 Bazel에 사용할 adb 바이너리를 알려줍니다.
  • --adb_argadb의 명령줄에 추가 인수를 추가하는 데 사용할 수 있습니다. 이 방법의 유용한 애플리케이션 중 하나는 워크스테이션에 여러 기기가 연결되어 있는 경우 설치할 기기를 선택하는 것입니다. bazel mobile-install :your_target -- --adb_arg=-s --adb_arg=<SERIAL>

잘 모르겠다면 를 살펴보고 Google 그룹에서 문의하거나 GitHub 문제를 제출하세요.

소개

개발자 도구 모음의 가장 중요한 속성 중 하나는 속도입니다. 코드를 변경하고 1초 이내에 실행되는 것을 보는 것과 변경사항이 예상대로 작동하는지 여부에 관한 의견을 받기 전에 몇 분 또는 몇 시간 동안 기다려야 하는 것에는 큰 차이가 있습니다.

안타깝게도 .apk를 빌드하기 위한 기존 Android 도구 모음에는 많은 모놀리식 순차적 단계가 포함되어 있으며 Android 앱을 빌드하려면 이러한 단계를 모두 실행해야 합니다. Google에서는 Google 지도와 같은 대규모 프로젝트에서 한 줄 변경사항을 빌드하는 데 5분을 기다리는 것이 드문 일이 아니었습니다.

bazel mobile-install 은 앱의 코드를 변경하지 않고도 변경사항 가지치기, 작업 샤딩, Android 내부의 스마트 조작을 조합하여 Android의 반복 개발을 훨씬 더 빠르게 만듭니다.

기존 앱 설치 관련 문제

Android 앱 빌드에는 다음과 같은 몇 가지 문제가 있습니다.

  • 덱싱. 기본적으로 Dexer 도구 (이전에는 dx, 현재는 d8 또는 r8)는 빌드에서 정확히 한 번 호출되며 이전 빌드의 작업을 재사용하는 방법을 알지 못합니다. 메서드가 하나만 변경되었더라도 모든 메서드를 다시 덱싱합니다.

  • 기기에 데이터 업로드. adb는 USB 2.0 연결의 전체 대역폭을 사용하지 않으며 대규모 앱은 업로드하는 데 시간이 오래 걸릴 수 있습니다. 리소스 또는 단일 메서드와 같이 작은 부분만 변경되었더라도 전체 앱이 업로드되므로 주요 병목 현상이 될 수 있습니다.

  • 네이티브 코드로 컴파일. Android L에서는 Dalvik과 같이 JIT 컴파일하는 대신 AOT 컴파일하는 새로운 Android 런타임인 ART를 도입했습니다. 이렇게 하면 설치 시간이 길어지는 대신 앱이 훨씬 더 빨라집니다. 사용자는 일반적으로 앱을 한 번 설치하고 여러 번 사용하므로 이는 사용자에게 좋은 절충안이지만 앱이 여러 번 설치되고 각 버전이 최대 몇 번 실행되는 느린 개발로 이어집니다.

bazel mobile-install의 접근 방식

bazel mobile-install은 다음과 같은 개선사항을 제공합니다.

  • 샤딩된 desugaring 및 덱싱. 앱의 자바 코드를 빌드한 후 Bazel은 클래스 파일을 대략 동일한 크기의 부분으로 샤딩하고 d8을 별도로 호출합니다. 마지막 빌드 이후 변경되지 않은 샤드에서는 d8이 호출되지 않습니다. 그런 다음 이러한 샤드는 별도의 샤딩된 APK로 컴파일됩니다.

  • 증분 파일 전송. Android 리소스, .dex 파일, 네이티브 라이브러리는 기본 .apk에서 삭제되고 별도의 mobile-install 디렉터리 아래에 저장됩니다. 이렇게 하면 전체 앱을 다시 설치하지 않고도 코드와 Android 리소스를 독립적으로 업데이트할 수 있습니다. 따라서 파일을 전송하는 데 시간이 덜 걸리고 변경된 .dex 파일만 기기에서 다시 컴파일됩니다.

  • 샤딩된 설치. Mobile-install은 Android 스튜디오의 apkdeployer 도구를 사용하여 연결된 기기의 샤딩된 APK를 결합하고 일관된 환경을 제공합니다.

샤딩된 덱싱

샤딩된 덱싱은 비교적 간단합니다. .jar 파일이 빌드되면 a 도구 가 이를 대략 동일한 크기의 별도 .jar 파일로 샤딩한 다음 이전 빌드 이후 변경된 파일에서 d8를 호출합니다. 덱싱할 샤드를 결정하는 로직은 Android에만 국한되지 않습니다. Bazel의 일반적인 변경사항 가지치기 알고리즘을 사용합니다.

샤딩 알고리즘의 첫 번째 버전은 단순히 .class 파일을 알파벳순으로 정렬한 다음 목록을 동일한 크기의 부분으로 잘랐지만 이는 최적이 아닌 것으로 밝혀졌습니다. 클래스가 추가되거나 삭제되면 (중첩되거나 익명인 경우에도) 알파벳순으로 그 뒤에 있는 모든 클래스가 하나씩 이동하여 해당 샤드를 다시 덱싱하게 됩니다. 따라서 개별 클래스 대신 자바 패키지를 샤딩하기로 결정했습니다. 물론 새 패키지가 추가되거나 삭제되면 여전히 많은 샤드가 덱싱되지만 이는 단일 클래스를 추가하거나 삭제하는 것보다 훨씬 덜 자주 발생합니다.

샤드 수는 --define=num_dex_shards=N 플래그를 사용하여 명령줄 구성으로 제어됩니다. 이상적인 세계에서 Bazel은 최적의 샤드 수를 자동으로 결정하지만 Bazel은 현재 작업을 실행하기 전에 작업 집합 (예: 빌드 중에 실행할 명령어)을 알아야 하므로 앱에 최종적으로 포함될 자바 클래스 수를 알 수 없으므로 최적의 샤드 수를 결정할 수 없습니다. 일반적으로 샤드가 많을수록 빌드 및 설치가 빨라지지만 동적 링커가 더 많은 작업을 해야 하므로 앱 시작이 느려집니다. 일반적으로 10~50개의 샤드가 적절합니다.

증분 배포

증분 APK 샤드 전송 및 설치는 이제 apkdeployer 유틸리티에서 "Mobile-install의 접근 방식"에 설명된 대로 처리합니다. 이전 (네이티브) 버전의 mobile-install에서는 최초 설치를 수동으로 추적하고 후속 설치에 --incremental 플래그를 선택적으로 적용해야 했지만 rules_android 의 최신 버전은 크게 간소화되었습니다. 앱이 설치 또는 재설치된 횟수와 관계없이 동일한 mobile-install 호출을 사용할 수 있습니다.

높은 수준에서 apkdeployer 도구는 다양한 adb 하위 명령어의 래퍼입니다. 기본 진입점 로직은 com.android.tools.deployer.Deployer 클래스에서 찾을 수 있으며 다른 유틸리티 클래스는 동일한 패키지에 함께 배치됩니다. Deployer 클래스는 분할 APK의 경로 목록과 설치에 관한 정보가 포함된 프로토콜 버퍼를 비롯한 여러 항목을 수집하고 Android App Bundle의 배포 기능을 활용하여 설치 세션을 만들고 앱 분할을 증분 방식으로 배포합니다. 구현에 관한 자세한 내용은 ApkPreInstallerApkInstaller 클래스를 참고하세요.

결과

성능

일반적으로 bazel mobile-install을 사용하면 작은 변경사항 후 대규모 앱을 빌드하고 설치하는 속도가 4~10배 빨라집니다.

다음 숫자는 몇 가지 Google 제품에 대해 계산되었습니다.

물론 이는 변경사항의 특성에 따라 다릅니다. 기본 라이브러리를 변경한 후 다시 컴파일하는 데 시간이 더 오래 걸립니다.

제한사항

스텁 애플리케이션이 실행하는 트릭은 모든 경우에 작동하지 않습니다. 다음 사례에서는 예상대로 작동하지 않는 경우를 강조합니다.

  • Mobile-install은 rules_android의 Starlark 규칙을 통해서만 지원됩니다. 자세한 내용은 "mobile-install의 간단한 기록"을 참고하세요.

  • ART를 실행하는 기기만 지원됩니다. Mobile-install은 Dalvik이 아닌 ART를 실행하는 기기에만 있는 API 및 런타임 기능을 사용합니다. Android L (API 21 이상)보다 최신 Android 런타임은 호환되어야 합니다.

  • Bazel 자체는 도구 자바 런타임 언어 버전 17 이상으로 실행해야 합니다.

  • 8.4.0 이전 버전의 Bazel은 mobile-install에 몇 가지 추가 플래그를 지정해야 합니다. Bazel Android 튜토리얼을 참고하세요. 이러한 플래그는 Bazel에 Starlark mobile-install 측면이 있는 위치와 지원되는 규칙을 알려줍니다.

mobile-install의 간단한 기록

이전 Bazel 버전에는 C++, 자바, Android와 같은 인기 있는 언어 및 생태계를 위한 기본 제공 빌드 및 테스트 규칙이 기본적으로 포함되어 있었습니다. 따라서 이러한 규칙을 네이티브 규칙이라고 했습니다. Bazel 8 (2024년 출시)에서는 이러한 규칙의 지원 기능이 삭제되었습니다. 이러한 규칙 중 다수가 Starlark 언어로 이전되었기 때문입니다. 자세한 내용은 "Bazel 8.0 LTS 블로그 게시물" 을 참고하세요.

기존 네이티브 Android 규칙은 기존 네이티브 버전의 mobile-install 기능도 지원했습니다. 이제 이를 'mobile-install v1' 또는 '네이티브 mobile-install'이라고 합니다. 이 기능은 Bazel 8에서 기본 제공 Android 규칙과 함께 삭제되었습니다.

이제 모든 mobile-install 기능과 모든 Android 빌드 및 테스트 규칙이 Starlark로 구현되고 rules_android GitHub 저장소에 있습니다. 최신 버전은 'mobile-install v3' 또는 'MIv3'라고 합니다.

이름 지정 참고사항: 한때 Google 내부에서만 사용할 수 있는 "mobile-install v2"가 있었지만 외부에는 게시되지 않았으며 v3 만 Google 내부 및 OSS rules_android 배포에 계속 사용됩니다.