bazel 모바일 설치

문제 신고 소스 보기 Nightly · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

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_arg를 사용하여 adb의 명령줄에 추가 인수를 추가할 수 있습니다. 이 기능의 유용한 적용 사례 중 하나는 워크스테이션에 연결된 기기가 여러 대인 경우 설치할 기기를 선택하는 것입니다. 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)는 빌드에서 정확히 한 번 호출되며 이전 빌드의 작업을 재사용하는 방법을 알지 못합니다. 메서드가 하나만 변경되었더라도 모든 메서드를 다시 dex합니다.

  • 기기에 데이터를 업로드합니다. adb는 USB 2.0 연결의 전체 대역폭을 사용하지 않으며 큰 앱은 업로드하는 데 시간이 오래 걸릴 수 있습니다. 리소스나 단일 메서드와 같이 작은 부분만 변경된 경우에도 전체 앱이 업로드되므로 심각한 병목 현상이 발생할 수 있습니다.

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

bazel mobile-install의 접근 방식

bazel mobile-install는 다음과 같이 개선되었습니다.

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

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

  • 샤딩된 설치입니다. 모바일 설치는 Android 스튜디오의 apkdeployer 도구를 사용하여 연결된 기기에서 샤딩된 APK를 결합하고 일관된 환경을 제공합니다.

샤딩된 Dexing

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

샤딩 알고리즘의 첫 번째 버전에서는 .class 파일을 알파벳순으로 정렬한 다음 목록을 동일한 크기의 부분으로 잘랐지만 이는 최적이 아닌 것으로 드러났습니다. 클래스가 추가되거나 삭제되면 (중첩된 클래스나 익명 클래스 포함) 알파벳순으로 그 뒤에 있는 모든 클래스가 하나씩 이동하여 해당 샤드가 다시 dexing됩니다. 따라서 개별 클래스가 아닌 Java 패키지를 샤딩하기로 결정했습니다. 물론 새 패키지를 추가하거나 삭제하면 여전히 많은 샤드가 색인화되지만 이는 단일 클래스를 추가하거나 삭제하는 것보다 훨씬 덜 빈번합니다.

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

점진적 배포

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

개략적으로 apkdeployer 도구는 다양한 adb 하위 명령어의 래퍼입니다. 기본 진입점 로직은 com.android.tools.deployer.Deployer 클래스에 있으며 다른 유틸리티 클래스는 동일한 패키지에 있습니다. Deployer 클래스는 무엇보다도 분할 APK 경로 목록과 설치에 관한 정보가 포함된 protobuf를 수집하고 Android App Bundle의 배포 기능을 활용하여 설치 세션을 만들고 앱 분할을 점진적으로 배포합니다. 구현 세부정보는 ApkPreInstallerApkInstaller 클래스를 참고하세요.

결과

성능

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

다음은 몇 가지 Google 제품에 대해 계산된 수치입니다.

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

제한사항

스텁 애플리케이션이 사용하는 트릭이 모든 경우에 작동하는 것은 아닙니다. 다음 사례는 예상대로 작동하지 않는 경우를 보여줍니다.

  • 모바일 설치는 rules_android의 Starlark 규칙을 통해서만 지원됩니다. 자세한 내용은 '모바일 설치의 간략한 역사'를 참고하세요.

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

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

  • 8.4.0 이전 Bazel 버전에서는 모바일 설치를 위해 몇 가지 추가 플래그를 지정해야 합니다. Bazel Android 튜토리얼을 참고하세요. 이러한 플래그는 Starlark 모바일 설치 측면이 어디에 있는지, 어떤 규칙이 지원되는지 Bazel에 알려줍니다.

모바일 설치의 간략한 역사

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

기존 네이티브 Android 규칙은 기존 네이티브 버전의 모바일 설치 기능도 지원했습니다. 이를 현재 '모바일 설치 v1' 또는 '네이티브 모바일 설치'라고 합니다. 이 기능은 Bazel 8에서 내장 Android 규칙과 함께 삭제되었습니다.

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

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