Phát triển lặp lại nhanh cho Android
Trang này mô tả cách bazel mobile-install
giúp quá trình phát triển lặp lại cho Android nhanh hơn nhiều. Tài liệu này mô tả các 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 nhanh các thay đổi nhỏ đối với ứng dụng Android, hãy làm như sau:
- Tìm quy tắc
android_binary
của ứng dụng bạn muốn cài đặt. - Tắt Proguard bằng cách xoá thuộc tính
proguard_specs
. - Đặt thuộc tính
multidex
thànhnative
. - Đặt thuộc tính
dex_shards
thành10
. - Kết nối thiết bị chạy ART (không phải Dalvik) qua USB và bật tính năng gỡ lỗi qua USB trên thiết bị đó.
- 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. - Chỉnh sửa mã hoặc tài nguyên Android.
- Chạy
bazel mobile-install --incremental :your_target
. - Tận hưởng việc không 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ủaadb
. Một ứng dụng hữu ích của tính năng 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ị được kết nối với máy trạm:bazel mobile-install --adb_arg=-s --adb_arg=<SERIAL> :your_target
--start_app
tự động khởi động ứng dụng
Khi không chắc chắn, 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 của chuỗi công cụ của 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 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 phản hồi về việc liệu các thay đổi của bạn có mang lại kết quả như mong đợi hay không.
Rất tiếc, chuỗi công cụ Android truyền thống để tạo tệp .apk bao gồm nhiều bước tuần tự, nguyên khối và tất cả các bước này đều phải được thực hiện để tạo một ứng dụng Android. Tại Google, việc chờ 5 phút để tạo một thay đổi một dòng không phải là điều bất thường đối với các dự án lớn hơn như Google Maps.
bazel mobile-install
giúp quá trình 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 việc cắt giảm thay đổi, phân đoạn công việc và thao tác thông minh đối với các thành phần nội bộ của Android, tất cả đều không làm thay đổi bất kỳ mã nào của ứng dụng.
Vấn đề khi cài đặt ứng dụng theo cách 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, "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 từ các bản dựng trước đó: nó sẽ tạo lại tệp dex cho mọi phương thức, mặc dù chỉ 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 sẽ được tải lên, ngay cả khi chỉ có một số 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à nút thắt cổ chai chính.
Biên dịch thành mã gốc. Android L đã ra mắt ART, một môi trường thời gian chạy Android mới, biên dịch ứng dụng trước khi thực thi thay vì biên dịch ứng dụng đúng thời điểm như Dalvik. Điều này giúp ứng dụng chạy nhanh hơn nhiều nhưng thời gian cài đặt sẽ lâu hơn. Đây là một sự đánh đổi phù hợp 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 nhiều lần, nhưng dẫn đến việc phát triển chậm hơn khi một ứng dụng được cài đặt nhiều lần và mỗi phiên bản được chạy nhiều nhất là một vài lần.
Phương pháp của bazel mobile-install
bazel mobile-install
cải thiện những điểm sau:
Tạo tệp dex phân đoạn. Sau khi tạo mã Java của ứng dụng, Bazel sẽ phân đoạn các tệp lớp thành các phần có kích thước gần như bằng nhau và gọi riêng
dx
trên các phần đó.dx
không được gọi trên các mảnh không thay đổi kể từ bản dựng gần đây nhất.Chuyển tệp tăng dần. 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 dành cho thiết bị di động riêng biệt. Điều này cho phép bạn 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 tệp sẽ mất ít thời gian hơn và chỉ những 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 mô phỏng nhỏ được đưa vào tệp .apk để tải tài nguyên Android, mã Java và mã gốc từ thư mục cài đặt dành cho 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ế. Tất cả đều minh bạch với ứng dụng, ngoại trừ một số trường hợp hiếm gặp được mô tả bên dưới.
Tạo tệp dex phân đoạn
Việc tạo tệp dex phân đoạn khá đơn giản: sau khi tạo các tệp .jar, một công cụ sẽ phân đoạn các tệp đó 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 các tệp đã thay đổi kể từ bản dựng trước. Logic xác định mảnh nào sẽ được 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ỉ 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, nhưng điều này đã chứng minh là không tối ưu: nếu một lớp được thêm hoặc xoá (thậm chí là một lớp lồng nhau hoặc một lớp ẩn danh), thì lớp đó sẽ khiến tất cả các lớp theo thứ tự bảng chữ cái sau đó dịch chuyển một lớp, dẫn đến việc tạo lại các mảnh đó. Do đó, chúng tôi quyết định phân đoạn các gói Java thay vì các 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 cho nhiều mảnh nếu bạn thêm hoặc xoá một gói mới, nhưng điều này ít xảy ra hơn nhiều so với việc thêm hoặc xoá một lớp.
Số lượng phân mảnh được kiểm soát bằng tệp BUILD (sử dụng thuộc tính android_binary.dex_shards
). Trong trường hợp lý tưởng, Bazel sẽ tự động xác định số lượng mảnh tốt nhất, nhưng hiện tại, Bazel phải biết tập hợp các thao tác (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ỳ thao tác nào trong số đó, vì vậy, Bazel không thể xác định số lượng 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 có nhiều mảnh thì quá trình xây dựng và cài đặt càng nhanh, nhưng quá trình khởi động ứng dụng càng chậm, vì trình liên kết động phải làm nhiều việc hơn. Điểm ngọt thường nằm trong khoảng từ 10 đến 50 mảnh.
Chuyển tệp gia tăng
Sau khi tạo ứng dụng, bước tiếp theo là cài đặt ứng dụng, tốt nhất là với ít thao tác nhất có thể. Quá trình cài đặt bao gồm các bước sau:
- Cài đặt tệp .apk (thường là bằng
adb install
) - Tải 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
Bước đầu tiên không có nhiều điểm tăng dần: ứng dụng đã được cài đặt hay chưa. Bazel hiện dựa vào người dùng để cho biết liệu có nên thực hiện bước này hay không thông qua tuỳ chọn dòng lệnh --incremental
vì không thể xác định trong mọi trường hợp nếu cần.
Ở 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ị liệt kê những tệp ứng dụng nào có trên thiết bị và tổng kiểm của các tệp đó. Mọi tệp mới sẽ được tải lên thiết bị, mọi tệp đã thay đổi sẽ được cập nhật và mọi tệp đã bị xoá sẽ bị xoá khỏi thiết bị. Nếu không có tệp kê khai, thì hệ thống sẽ giả định rằng mọi tệp đều cần được tải lên.
Xin lưu ý rằng bạn có thể đánh lừa thuật toán cài đặt tăng dần bằng cách thay đổi một tệp trên thiết bị, nhưng không thay đổi tổng kiểm của tệp đó trong tệp kê khai. Bạn có thể bảo vệ vấn đề này bằng cách tính toán tổng kiểm 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ô phỏng là nơi diễn ra phép thuật để tải các dex, mã gốc và tài nguyên Android từ thư mục mobile-install
trên thiết bị.
Quá trình 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 nhận hợp lý. Quá trình này diễn ra trước khi bất kỳ lớp nào của ứng dụng được tải, để mọi lớp ứng dụng có trong apk đều có thể được đặt trong 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 diễn ra trước khi bất kỳ lớp nào của ứng dụng được tải, để không có lớp ứng dụng nào cần phải có trong tệp .apk, tức là các thay đổi đối với các lớp đó sẽ yêu cầu cài đặt lại toàn bộ.
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ô phỏng. Phương thức này sẽ kiểm soát khi ứng dụng khởi động và điều chỉnh trình tải lớp cũng như trình quản lý tài nguyên một cách thích hợp ngay từ thời điểm sớm nhất (hàm khởi tạo) bằng cách sử dụng tính năng phản chiếu Java trên nội bộ của khung Android.
Một việc khác mà ứng dụng mô phỏng thực hiện là sao chép các thư viện gốc được cài đặt bằng tính năng cài đặt dành cho 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 không thể thực hiện được đối với bất kỳ vị trí nào mà adb
không phải là thư mục gốc có thể truy cập.
Sau khi hoàn tất tất cả những việc này, ứng dụng mô phỏng sẽ tạo bản sao của lớp Application
thực tế, thay đổi tất cả các tệp 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 độ 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ỏ.
Chúng tôi đã tính toán các con số sau đây 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 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 thực hiện không hoạt động trong mọi trường hợp. Sau đây là các trường hợp nêu bật những trường hợp không hoạt động như mong đợi:
Khi
Context
được truyền vào lớpApplication
trongContentProvider#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ế thực thể của lớpApplication
. Do đó,ContentProvider
sẽ vẫn tham chiếu đến ứng dụng mô phỏng thay vì ứng dụng thực. Có thể nói, đây không phải là lỗi vì bạn không được phép chuyển xuốngContext
như vậy, nhưng điều này dường như 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ỉ có sẵn trong ứng dụng. Nếu các ứng dụng khác truy cập vào tài nguyên thông quaPackageManager#getApplicationResources()
, thì các tài nguyên này sẽ là từ lượt cài đặt không tăng dần gần đây nhất.Các thiết bị không chạy ART. Mặc dù ứng dụng mô phỏng hoạt động tốt trên phiên bản Froyo trở lên, nhưng Dalvik có một lỗi khiến ứng dụng này 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 chú thích Java được sử dụng theo cách cụ thể. Miễn là ứng dụng của bạn không gặp phải những lỗi này, ứng dụng đó cũng sẽ hoạt động với Dalvik (tuy nhiên, xin lưu ý rằng việc hỗ trợ các phiên bản Android cũ không phải là trọng tâm của chúng tôi)