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

Báo cáo vấn đề Xem nguồn Nightly/3}

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

Trang này mô tả cách bazel mobile-install giúp việc phát triển lặp lại cho Android nhanh hơn nhiều. Tài liệu này mô tả lợi ích của phương pháp này so với những thách thức của phương thức cài đặt ứng dụng truyền thống.

Tóm tắt

Để cài đặt các thay đổi nhỏ đối với ứng dụng Android thật nhanh chóng, 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. Tắt Proguard bằng cách xoá thuộc tính proguard_specs.
  3. Đặt thuộc tính multidex thành native.
  4. Đặt thuộc tính dex_shards thành 10.
  5. Kết nối thiết bị đang chạy ART (không phải Dalvik) qua USB và cho phép gỡ lỗi USB trên thiết bị đó.
  6. 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.
  7. Chỉnh sửa mã hoặc tài nguyên Android.
  8. Chạy bazel mobile-install --incremental :your_target.
  9. Bạn có thể thoải mái khám phá mà không cần phải chờ đợi lâ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 tính năng này là chọn thiết bị bạn muốn cài đặt nếu có nhiều thiết bị được kết nối với máy trạm: bazel mobile-install --adb_arg=-s --adb_arg=<SERIAL> :your_target
  • --start_app sẽ tự động khởi động ứng dụng

Nếu nghi ngờ, hãy xem ví dụ hoặc liên hệ với chúng tôi.

Giới thiệu

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

Thật không may là chuỗi công cụ Android truyền thống để tạo một tệp .apk đòi hỏi nhiều bước nguyên khối, tuần tự và tất cả những bước này phải được thực hiện để tạo một ứng dụng Android. Tại Google, việc đợi 5 phút để tạo một thay đổi trên một dòng là điều bình thường đối với các dự án lớn hơn như Google Maps.

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

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

Khi 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, "dx" đượ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 của các bản dựng trước: phương thức này sẽ lặp lại mọi phương thức, ngay cả khi chỉ có một phương thức được thay đổi.

  • Đang 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 thành mã gốc. Android L giới thiệu ART, một môi trường thời gian chạy Android mới, giúp biên dịch trước ứng dụng thay vì biên dịch ngay lập tức như Dalvik. Điều này giúp ứng dụng nhanh hơn nhiều nhưng lại cần 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 một ứng dụng một lần và sử dụng ứng dụng đó nhiều lần, nhưng dẫn đến việc phát triển chậm hơn, trong đó một ứng dụng được cài đặt nhiều lần và mỗi phiên bản chỉ chạy tối đa một vài lần.

Phương thức của bazel mobile-install

bazel mobile-installcó các điểm cải tiến sau:

  • Tạo tệp dex phân đoạn. Sau khi tạo mã Java của ứng dụng, Bazel phân đoạn tệp lớp thành các phần có kích thước gần bằng nhau và gọi dx riêng trên các phần đó. dx không được gọi trên phân đoạn không thay đổi kể từ bản dựng gần đây nhất.

  • Truyền tệp tăng dần. Các tài nguyên Android, tệp .dex và thư viện gốc sẽ bị xoá khỏi tệp .apk chính và được lưu trữ trong một thư mục cài đặt riêng cho thiết bị di động. Nhờ đó, 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 chuyển các tệp sẽ 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ị.

  • Tải các phần của ứng dụng từ bên ngoài tệp .apk. Một ứng dụng stub nhỏ được đưa vào .apk để tải các tài nguyên Android, mã Java và mã gốc từ thư mục cài đặt trên thiết bị di động trên thiết bị, sau đó chuyển quyền kiểm soát sang ứng dụng thực tế. Điều này sẽ minh bạch đối với ứng dụng, ngoại trừ một vài trường hợp dưới đây.

Phân đoạn Dexing

Thao tác phân đoạn dex đơn giản một cách hợp lý: sau khi tạo xong tệp .jar, một công cụ sẽ phân đoạn 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 dx trên những tệp đã được thay đổi kể từ bản dựng trước. Logic xác định phân đoạn nào thành dex không dành riêng cho Android: logic này chỉ sử dụng thuật toán cắt giảm thay đổi chung của Bazel.

Phiên bản đầu tiên của thuật toán phân đoạn chỉ cần sắp xếp các tệp .class theo thứ tự 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. Tuy nhiên, điều này được chứng minh là chưa tối ưu: nếu một lớp được thêm hoặc xoá (thậm chí là một lớp được lồng hoặc ẩn danh), thì điều này sẽ khiến tất cả các lớp sau đó di chuyển theo thứ tự bảng chữ cái, dẫn đến việc tạo lại các phân đoạn đó. Do đó, chúng tôi đã quyết định phân đoạn các gói Java thay vì từng lớp riêng lẻ. Tất nhiên, việc này vẫn dẫn đến việc tạo tệp dex nhiều phân đoạn nếu một gói mới được thêm hoặc xoá, nhưng việc này ít thường xuyên 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 đoạn do tệp BUILD kiểm soát (bằng thuộc tính android_binary.dex_shards). Trong một thế giới lý tưởng, Bazel sẽ tự động xác định số lượng phân đoạn tối ưu, nhưng hiện tại Bazel phải biết tập hợp hành động (ví dụ: lệnh được thực thi trong quá trình tạo bản dựng) trước khi thực thi bất kỳ phân đoạn nào, vì vậy, Bazel không thể xác định số lượng phân đoạn 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, quá trình khởi động ứng dụng càng nhanh và càng chậm. Điểm ngọt thường là từ 10 đến 50 phân đoạn.

Truyền tệp tăng dần

Sau khi tạo ứng dụng, bước tiếp theo là cài đặt ứng dụng đó, tốt nhất là tốn ít công sức nhất có thể. Quá trình cài đặt bao gồm các bước sau:

  1. Đang cài đặt .apk (thường sử dụng adb install)
  2. Tải các tệp .dex, tài nguyên Android và thư viện gốc lên thư mục cài đặt dành cho thiết bị di động

Không có nhiều mức độ gia tăng trong bước đầu tiên: ứng dụng đã được cài đặt hay chưa. Hiện tại, Bazel dựa vào người dùng để cho biết liệu có nên thực hiện bước này thông qua tuỳ chọn dòng lệnh --incremental hay không, vì tính năng này không thể xác định trong mọi trường hợp liệu có cần thiết hay không.

Ở bước thứ hai, các tệp của ứng dụng trong bản dựng được so sánh với một tệp kê khai trên thiết bị. Tệp này liệt kê những tệp ứng dụng nào nằm trên thiết bị và giá trị tổng kiểm của những tệp đó. Mọi tệp mới sẽ được tải lên thiết bị, mọi tệp đã thay đổi và mọi tệp đã bị xoá sẽ bị xoá khỏi thiết bị. Nếu không có tệp kê khai, hệ thống sẽ xem như bạn cần tải mọi tệp lên.

Xin lưu ý rằng bạn có thể đánh lừa thuật toán cài đặt gia tăng bằng cách thay đổi một tệp trên thiết bị, nhưng không thay đổi giá trị tổng kiểm của tệp đó trong tệp kê khai. Điều này có thể được bảo vệ bằng cách tính toán tổng kiểm tra của các tệp trên thiết bị, nhưng việc này được coi là không đáng để tăng thời gian cài đặt.

Ứng dụng Stub

Ứng dụng mã giả lập là nơi bạn sẽ có thể tải các tệp dex, mã gốc và tài nguyên Android từ thư mục mobile-install trên thiết bị.

Việc tải thực tế được triển khai bằng cách phân lớp con BaseDexClassLoader và là một kỹ thuật được ghi lại hợp lý. Quá trình này diễn ra trước khi tải bất kỳ lớp nào của ứng dụng, do đó, mọi lớp ứng dụng trong tệp APK đều có thể được đặt vào thư mục mobile-install trên thiết bị để có thể cập nhật mà không cần adb install.

Việc này cần xảy ra trước khi tải bất kỳ lớp ứng dụng nào để không cần thêm lớp ứng dụng vào trong .apk, tức là bạn phải cài đặt lại hoàn toàn các thay đổi đối với các lớp đó.

Bạn có thể thực hiện việc này bằng cách thay thế lớp Application được chỉ định trong AndroidManifest.xml bằng ứng dụng mã giả lập. Phương thức này sẽ kiểm soát thời điểm ứng dụng khởi động, đồng thời điều chỉnh trình tải lớp và trình quản lý tài nguyên một cách phù hợp tại thời điểm sớm nhất (hàm khởi tạo của trình quản lý tài nguyên) bằng cách sử dụng phản chiếu Java trên nội dung bên trong khung Android.

Một chức năng khác của ứng dụng mã giả lập là sao chép các thư viện gốc được cài đặt bằng lượt cài đặt trên thiết bị di động sang một vị trí khác. Điều này là cần thiết vì trình liên kết động cần đặt bit X trên các tệp. Điều này là không thể thực hiện đối với bất kỳ vị trí nào mà adb không phải gốc có thể truy cập.

Sau khi hoàn tất những việc này, ứng dụng giả lập sẽ tạo thực thể cho lớp Application thực tế, thay đổi mọi tham chiếu đến chính ứng dụng đó thành ứng dụng thực tế trong khung Android.

Kết quả

Hiệu suất

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

Những con số sau đây đã được tính 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: quá trình biên dịch lại sau khi thay đổi 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 giả lập không hoạt động trong mọi trường hợp. Sau đây là ví dụ về những điểm mà chế độ này không hoạt động như mong đợi:

  • Khi Context được truyền tới lớp Application trong ContentProvider#onCreate(). Phương thức này được gọi trong quá trình khởi động ứng dụng trước khi chúng ta có cơ hội thay thế bản sao của lớp Application. Do đó, ContentProvider sẽ vẫn tham chiếu đến ứng dụng mã giả lập thay vì ứng dụng thực. Có thể cho rằng đây không phải là lỗi vì bạn không nên sử dụng Context như thế này, nhưng có vẻ như điều này xảy ra trong một số ứng dụng của Google.

  • Các tài nguyên do bazel mobile-install cài đặt chỉ dùng được từ bên trong ứng dụng. Nếu các ứng dụng khác truy cập vào các tài nguyên thông qua PackageManager#getApplicationResources(), các tài nguyên này sẽ được lấy từ lượt cài đặt không tăng dần gần nhất.

  • Các thiết bị hiện không chạy ART. Mặc dù ứng dụng mã giả lập hoạt động tốt trên Froyo trở lên, nhưng Dalvik có lỗi khiến ứng dụng cho rằng ứng dụng không chính xác nếu mã của ứng dụng được phân phối trên nhiều tệp .dex trong một số trường hợp nhất định, chẳng hạn như khi sử dụng chú giải Java theo cách cụ thể. Miễn là ứng dụng của bạn không vướng vào các lỗi này, ứng dụng cũng sẽ hoạt động với Dalvik (tuy nhiên, hãy lưu ý rằng việc hỗ trợ các phiên bản Android cũ không chính xác là trọng tâm của chúng tôi)