cài đặt trên thiết bị di động bazel

Phát triển lặp nhanh cho Android

Trang này mô tả cách bazel mobile-install giúp phát triển lặp cho Android nhanh hơn nhiều. Trang này mô tả những lợi ích của phương pháp này so với những nhược điểm của các bước xây dựng và cài đặt riêng biệt.

Tóm tắt

Để cài đặt các thay đổi nhỏ cho ứng dụng Android rất nhanh, hãy làm như sau:

  1. Tìm quy tắc android_binary của ứng dụng mà bạn muốn cài đặt.
  2. Kết nối thiết bị của bạn với adb.
  3. Chạy bazel mobile-install :your_target. Quá trình khởi động ứng dụng sẽ chậm hơn một chút so với bình thường.
  4. Chỉnh sửa mã hoặc tài nguyên Android.
  5. Chạy bazel mobile-install :your_target.
  6. Tận hưởng quá trình cài đặt gia tăng nhanh chóng và tối thiểu!

Một số tuỳ chọn dòng lệnh cho Bazel có thể hữu ích:

  • --adb cho Bazel biết tệp nhị phân adb nào cần sử dụng
  • Bạn có thể dùng --adb_arg để thêm các đối số bổ sung vào dòng lệnh của adb. Một ứng dụng hữu ích của việc này là chọn thiết bị mà bạn muốn cài đặt nếu bạn có nhiều thiết bị kết nối với máy trạm: bazel mobile-install :your_target -- --adb_arg=-s --adb_arg=<SERIAL>

Khi không chắc chắn, hãy xem ví dụ, liên hệ với chúng tôi trên Nhóm Google, hoặc báo cáo vấn đề trên GitHub

Giới thiệu

Một trong những thuộc tính quan trọng nhất của chuỗi công cụ dành cho nhà phát triển là tốc độ: có sự khác biệt rất lớn giữa việc thay đổi mã và xem mã chạy trong vòng một giây so với việc phải đợi vài phút, đôi khi là vài giờ, trước khi bạn nhận được ý kiến phản hồi về việc các thay đổi của bạn có thực hiện đúng như mong đợi hay không.

Rất tiếc, chuỗi công cụ Android truyền thống để xây dựng tệp .apk bao gồm nhiều bước nguyên khối, tuần tự và bạn phải thực hiện tất cả các bước này để xây dựng ứng dụng Android. Tại Google, việc phải đợi 5 phút để xây dựng một thay đổi một dòng không phải là điều hiếm gặp đối với các dự án lớn hơn như Google Maps.

bazel mobile-install giúp phát triển lặp cho Android nhanh hơn nhiều bằng cách kết hợp tính năng cắt tỉa thay đổi, phân chia công việc và thao tác thông minh với các thành phần bên trong của Android, tất cả đều không thay đổi bất kỳ mã nào của ứng dụng.

Các vấn đề khi cài đặt ứng dụng truyền thống

Việc xây dựng ứng dụng Android có một số vấn đề, bao gồm:

  • Tạo tệp dex. Theo mặc định, công cụ Dexer (trước đây là dx, hiện là d8 hoặc r8) được gọi chính xác một lần trong bản dựng và không biết cách sử dụng lại công việc từ các bản dựng trước: công cụ này sẽ tạo tệp dex cho mọi phương thức một lần nữa, ngay cả khi chỉ có một phương thức được thay đổi.

  • Tải dữ liệu lên thiết bị. adb không sử dụng toàn bộ băng thông của kết nối USB 2.0 và các ứng dụng lớn hơn có thể mất nhiều thời gian để tải lên. Toàn bộ ứng dụng được tải lên, ngay cả khi chỉ có các phần nhỏ thay đổi, chẳng hạn như một tài nguyên hoặc một phương thức duy nhất, vì vậy, đây có thể là một nút thắt cổ chai lớn.

  • Biên dịch sang mã gốc. Android L đã giới thiệu ART, một thời gian chạy Android mới, biên dịch ứng dụng trước thời gian thay vì biên dịch chúng ngay lập tức như Dalvik. Điều này giúp ứng dụng nhanh hơn nhiều nhưng phải trả giá bằng thời gian cài đặt lâu hơn. Đây là một sự đánh đổi tốt cho người dùng vì họ thường cài đặt ứng dụng một lần và sử dụng nhiều lần, nhưng dẫn đến quá trình phát triển chậm hơn khi ứng dụng được cài đặt nhiều lần và mỗi phiên bản chạy tối đa vài lần.

Phương pháp của bazel mobile-install

bazel mobile-install thực hiện những cải tiến sau:

  • Phân chia việc khử đường và tạo tệp dex. Sau khi xây dựng mã Java của ứng dụng, Bazel sẽ phân chia các tệp lớp thành các phần có kích thước gần bằng nhau và gọi d8 riêng trên các tệp đó. d8 không được gọi trên các phân mảnh không thay đổi kể từ bản dựng cuối cùng. Sau đó, các phân mảnh này được biên dịch thành các APK phân mảnh riêng biệt.

  • Truyền tệp gia tăng. Tài nguyên Android, tệp .dex và thư viện gốc bị xoá khỏi tệp .apk chính và được lưu trữ trong một thư mục mobile-install riêng biệt. Điều này giúp bạn có thể cập nhật mã và tài nguyên Android một cách độc lập mà không cần cài đặt lại toàn bộ ứng dụng. Do đó, việc truyền tệp mất ít thời gian hơn và chỉ các tệp .dex đã thay đổi mới được biên dịch lại trên thiết bị.

  • Cài đặt phân mảnh. Mobile-install sử dụng công cụ apkdeployer của Android Studio để kết hợp các APK phân mảnh trên thiết bị được kết nối và mang lại trải nghiệm gắn kết.

Phân chia việc tạo tệp dex

Việc phân chia việc tạo tệp dex khá đơn giản: sau khi các tệp .jar được xây dựng, một công cụ sẽ phân chia chúng thành các tệp .jar riêng biệt có kích thước gần bằng nhau, sau đó gọi d8 trên những tệp đã thay đổi kể từ bản dựng trước. Logic xác định phân mảnh nào cần tạo tệp dex không dành riêng cho Android: logic này chỉ sử dụng thuật toán cắt tỉa thay đổi chung của Bazel.

Phiên bản đầu tiên của thuật toán phân mảnh chỉ đơn giản là sắp xếp các tệp .class theo bảng chữ cái, sau đó cắt danh sách thành các phần có kích thước bằng nhau, nhưng điều này chứng tỏ là không tối ưu: nếu một lớp được thêm hoặc xoá (ngay cả lớp lồng nhau hoặc lớp ẩn danh một), thì tất cả các lớp theo bảng chữ cái sau đó sẽ bị dịch chuyển đi một, dẫn đến việc tạo tệp dex cho các phân mảnh đó một lần nữa. Do đó, người ta quyết định phân chia các gói Java thay vì các lớp riêng lẻ. Tất nhiên, điều này vẫn dẫn đến việc tạo tệp dex cho nhiều phân mảnh nếu một gói mới được thêm hoặc xoá, nhưng điều đó ít xảy ra hơn nhiều so với việc thêm hoặc xoá một lớp duy nhất.

Số lượng phân mảnh được kiểm soát bằng cấu hình dòng lệnh, sử dụng cờ --define=num_dex_shards=N. Trong một thế giới lý tưởng, Bazel sẽ tự động xác định số lượng phân mảnh tốt nhất, nhưng hiện tại, Bazel phải biết tập hợp các hành động (ví dụ: các lệnh sẽ được thực thi trong quá trình xây dựng) trước khi thực thi bất kỳ hành động nào trong số đó, vì vậy, Bazel không thể xác định số lượng phân mảnh tối ưu vì không biết cuối cùng sẽ có bao nhiêu lớp Java trong ứng dụng. Nói chung, càng nhiều phân mảnh thì bản dựng và quá trình cài đặt sẽ càng nhanh, nhưng quá trình khởi động ứng dụng sẽ càng chậm vì trình liên kết động phải thực hiện nhiều công việc hơn. Điểm tối ưu thường nằm trong khoảng từ 10 đến 50 phân mảnh.

Triển khai gia tăng

Việc truyền và cài đặt phân mảnh APK gia tăng hiện được tiện ích apkdeployer xử lý như mô tả trong "Phương pháp cài đặt trên thiết bị di động". Trong khi các phiên bản (gốc) trước đó của tính năng cài đặt trên thiết bị di động yêu cầu theo dõi thủ công các lượt cài đặt lần đầu và chọn lọc áp dụng cờ --incremental cho các lượt cài đặt tiếp theo, thì phiên bản gần đây nhất trong rules_android đã được đơn giản hoá đáng kể. Bạn có thể sử dụng cùng một lệnh gọi cài đặt trên thiết bị di động bất kể số lần ứng dụng đã được cài đặt hoặc cài đặt lại.

Ở cấp độ cao, công cụ apkdeployer là một trình bao bọc xung quanh nhiều adb lệnh con. Bạn có thể tìm thấy logic điểm truy cập chính trong com.android.tools.deployer.Deployer lớp, với các lớp tiện ích khác được đặt cùng gói. Lớp Deployer tiếp nhận, trong số những thứ khác, một danh sách các đường dẫn để phân tách APK và một protobuf có thông tin về quá trình cài đặt, đồng thời tận dụng các tính năng triển khai cho gói ứng dụng Android để tạo phiên cài đặt và triển khai gia tăng các phân tách ứng dụng. Hãy xem các lớp ApkPreInstallerApkInstaller để biết thông tin chi tiết về cách triển khai.

Kết quả

Hiệu suất

Nhìn chung, bazel mobile-install giúp tăng tốc độ xây dựng và cài đặt các ứng dụng lớn từ 4 đến 10 lần sau một thay đổi nhỏ.

Các số sau đây được tính toán cho một số sản phẩm của Google:

Tất nhiên, điều này phụ thuộc vào bản chất của thay đổi: việc biên dịch lại sau khi thay đổi một thư viện cơ sở sẽ mất nhiều thời gian hơn.

Các điểm hạn chế

Các thủ thuật mà ứng dụng gốc thực hiện không hoạt động trong mọi trường hợp. Các trường hợp sau đây nêu bật những trường hợp mà ứng dụng không hoạt động như mong đợi:

  • Mobile-install chỉ được hỗ trợ thông qua các quy tắc Starlark của rules_android. Hãy xem "lịch sử tóm tắt về mobile-install" để biết thêm thông tin chi tiết.

  • Chỉ các thiết bị chạy ART mới được hỗ trợ. Mobile-install sử dụng các tính năng API và thời gian chạy chỉ tồn tại trên các thiết bị chạy ART, không phải Dalvik. Mọi thời gian chạy Android gần đây hơn Android L (API 21 trở lên) đều phải tương thích.

  • Bản thân Bazel phải được chạy bằng thời gian chạy Java của công cụ phiên bản ngôn ngữ là 17 trở lên.

  • Các phiên bản Bazel trước 8.4.0 phải chỉ định một số cờ bổ sung cho mobile-install. Hãy xem hướng dẫn về Bazel Android. Các cờ này thông báo cho Bazel biết khía cạnh cài đặt trên thiết bị di động Starlark nằm ở đâu và những quy tắc nào được hỗ trợ.

Lịch sử tóm tắt về mobile-install

Các phiên bản Bazel trước đây gốc bao gồm các quy tắc kiểm thử và xây dựng tích hợp cho các ngôn ngữ và hệ sinh thái phổ biến như C++, Java và Android. Do đó, các quy tắc này được gọi là quy tắc gốc. Bazel 8 (phát hành năm 2024) đã ngừng hỗ trợ các quy tắc này vì nhiều quy tắc trong số đó đã được di chuyển sang ngôn ngữ Starlark. Hãy xem "bài đăng trên blog về Bazel 8.0 LTS" để biết thêm thông tin chi tiết.

Các quy tắc Android gốc cũ cũng hỗ trợ phiên bản gốc cũ của chức năng cài đặt trên thiết bị di động. Chức năng này hiện được gọi là "mobile-install v1" hoặc "native mobile-install". Chức năng này đã bị xoá trong Bazel 8, cùng với các quy tắc Android tích hợp.

Hiện tại, tất cả chức năng cài đặt trên thiết bị di động, cũng như tất cả các quy tắc kiểm thử và xây dựng Android, đều được triển khai trong Starlark và nằm trong kho lưu trữ GitHub rules_android. Phiên bản mới nhất được gọi là "mobile-install v3" hoặc "MIv3".

Lưu ý về tên: Có một phiên bản "mobile-install v2" chỉ có sẵn nội bộ tại Google tại một thời điểm, nhưng phiên bản này chưa bao giờ được phát hành ra bên ngoài và chỉ có phiên bản 3 tiếp tục được sử dụng cho cả việc triển khai rules_android nội bộ của Google và OSS.