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. 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 pháp 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:
- 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 USB gỡ lỗi trên đó.
- 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
. - 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ủ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 nghi ngờ, hãy xem xét 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ó sự khác biệt giữa việc thay đổi mã và thấy mã chạy trong rồi phải đợi vài phút, đôi khi là nhiều giờ, rồi mới nhận được ý kiến phản hồi về việc liệu các thay đổi có đúng như bạn mong đợi hay không.
Thật không may, chuỗi công cụ Android truyền thống để tạo tệp .apk lại đòi hỏi nhiều bước đơn khối, tuần tự và tất cả các bước này phải được thực hiện để tạo ứng dụng Android. Tại Google, đợi 5 phút để tạo một dòng đơn thay đổi là bình 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 giữa cắt bớt thay đổi, phân đoạn công việc và thao tác thông minh
Android nội bộ, tất cả đều không phải 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 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.
Đ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 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 đã 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 ứng dụng trước khi biên dịch thay vì biên dịch kịp thời 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à 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 nó nhiều lần, nhưng dẫn đến việc phát triển chậm hơn khi ứng dụng 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
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 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. Tài nguyên Android, tệp .dex và mã gốc các thư viện sẽ bị xoá khỏi tệp .apk chính và được lưu trữ trong một tệp thư mục cài đặt trên điện thoại di động. Thao tác này giúp bạn có thể cập nhật mã và Android tài nguyên 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 mất ít thời gian hơn và chỉ mất các tệp .dex có đã thay đổi sẽ đượ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.
Phân đoạn Dexing
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 mà
xác định phân đoạn nào trong dex không dành riêng cho Android: mã 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, nhưng điều này đã chứng minh không tối ưu: nếu một lớp đã được thêm hoặc bị xoá (thậm chí là lớp được lồng hoặc ẩn danh một), thao tác này sẽ khiến tất cả các lớp theo thứ tự bảng chữ cái sau nó di chuyển 1 đơn vị, dẫn đến việc tạo lại các phân đoạn đó. Do đó, người ta quyết định phân đoạn Java thay vì từng lớp riêng lẻ. Tất nhiên, điều này vẫn dẫn đến 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 làm như vậy sẽ ít hơn thường xuyên hơn 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:
- Đang cài đặt .apk (thường sử dụ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, hệ thống sẽ giả định rằng mọi tệp đều cần được tải lên.
Lưu ý rằng 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. 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ã giả lập là nơi bạn có thể tải các tệp dex, mã gốc và
Các tài nguyên Android từ thư mục mobile-install
trên thiết bị sẽ hoạt động.
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 nhận hợp lý. Quá trình này xảy ra trước khi bất kỳ
các lớp này được tải để mọi lớp ứng dụng có trong tệp APK đều có thể
được đặt trong thư mục mobile-install
trên thiết bị để có thể cập nhật
không có 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. Chiến dịch này
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 cũng như
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) bằng cách sử dụng
Phản chiếu Java vào 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ài đặt bằng thiết bị di động đến vị trí khác. Điều này là cần thiết vì
trình liên kết động cần bit X
được đặt trên các tệp, điều này không thể
thực hiện cho bất kỳ vị trí nào có thể truy cập được bằng adb
không phải thư mục gốc.
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ỏ.
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: 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 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 tới lớpApplication
trongContentProvider#onCreate()
. Phương thức này được gọi trong quá trình áp dụng trước khi chúng ta có cơ hội thay thế bản sao củaApplication
do đó,ContentProvider
vẫn sẽ tham chiếu đến ứng dụng mã giả lập thay vì báo cáo thực. Có thể cho rằng đây không phải là lỗi vì bạn đáng lẽ sẽ giảmContext
như thế này, nhưng điều này có vẻ sẽ xảy ra sau ứng dụng tại 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 giả lập hoạt động tốt trên Froyo trở lên, Dalvik có một lỗi khiến ứng dụng nghĩ rằng không chính xác nếu mã được phân phối trên nhiều tệp .dex trong một số trường hợp, ví dụ: khi chú thích Java được sử dụng trong một cụ thể . Miễn là ứng dụng của bạn không mắc các lỗi này, ứng dụng sẽ hoạt động với Dalvik, (tuy nhiên, xin lưu ý rằng việc hỗ trợ cho các phiên bản Android cũ không chính xác là tiêu điểm)