Trước khi bạn bắt đầu, hãy xem thông tin tổng quan về Dịch vụ ART.
Kể từ Android 14, quá trình biên dịch AOT trên thiết bị cho ứng dụng (còn gọi là dexopt) sẽ do Dịch vụ ART xử lý. Dịch vụ ART là một phần của mô-đun ART và bạn có thể tuỳ chỉnh dịch vụ này thông qua các thuộc tính hệ thống và API.
Thuộc tính hệ thống
ART Service hỗ trợ tất cả các dịch vụ tuỳ chọn dex2oat.
Ngoài ra, Dịch vụ ART hỗ trợ các thuộc tính hệ thống sau:
pm.dexopt.<lý>
Đây là một tập hợp các thuộc tính hệ thống giúp xác định bộ lọc mặc định của trình biên dịch cho mọi lý do biên dịch định sẵn như mô tả trong các trường hợp sử dụng ngoại lệ.
Để biết thêm thông tin, hãy xem Bộ lọc trình biên dịch.
Các giá trị mặc định chuẩn là:
pm.dexopt.first-boot=verify
pm.dexopt.boot-after-ota=verify
pm.dexopt.boot-after-mainline-update=verify
pm.dexopt.bg-dexopt=speed-profile
pm.dexopt.inactive=verify
pm.dexopt.cmdline=verify
pm.dexopt.shared (mặc định: tốc độ)
Đây là bộ lọc trình biên dịch dự phòng cho những ứng dụng được các ứng dụng khác dùng.
Theo nguyên tắc, Dịch vụ ART thực hiện việc biên dịch theo hồ sơ (speed-profile
) cho
tất cả ứng dụng khi có thể, thường là trong quá trình dexopt ở chế độ nền. Tuy nhiên, có một số
một số ứng dụng mà các ứng dụng khác dùng (thông qua <uses-library>
hoặc đã tải
linh động bằng cách sử dụng Context#createPackageContext
với
CONTEXT_INCLUDE_CODE
). Những ứng dụng như vậy không thể sử dụng
vì lý do bảo mật.
Đối với một ứng dụng như vậy, nếu yêu cầu biên dịch theo hồ sơ, trước tiên, hãy sử dụng Dịch vụ ART
cố gắng sử dụng hồ sơ trên đám mây. Nếu hồ sơ đám mây không tồn tại, Dịch vụ ART
quay lại để sử dụng bộ lọc trình biên dịch do pm.dexopt.shared
chỉ định.
Nếu quá trình biên dịch được yêu cầu không được hướng dẫn theo hồ sơ, thì thuộc tính này sẽ không có hiệu lực.
pm.dexopt.<reason>.concurrency (mặc định: 1)
Đây là số lệnh gọi dex2oat cho quá trình biên dịch xác định trước nhất định
lý do (first-boot
, boot-after-ota
, boot-after-mainline-update
và
bg-dexopt
).
Lưu ý rằng hiệu quả của tuỳ chọn này được kết hợp với
tuỳ chọn sử dụng tài nguyên dex2oat (dalvik.vm.*dex2oat-threads
,
dalvik.vm.*dex2oat-cpu-set
và hồ sơ công việc):
dalvik.vm.*dex2oat-threads
kiểm soát số lượng luồng cho mỗi lệnh gọi dex2oat, trong khipm.dexopt.<reason>.concurrency
kiểm soát số lượng lệnh gọi dex2oat. Tức là số lượng luồng đồng thời tối đa là tích của hai tính chất hệ thống.dalvik.vm.*dex2oat-cpu-set
và các hồ sơ tác vụ luôn liên kết với lõi CPU mức sử dụng, bất kể số lượng luồng đồng thời tối đa là bao nhiêu (thảo luận ở trên).
Một lệnh gọi dex2oat duy nhất có thể không sử dụng hết tất cả các lõi CPU, bất kể
trong tổng số dalvik.vm.*dex2oat-threads
. Do đó, việc tăng số lượng lệnh gọi dex2oat (pm.dexopt.<reason>.concurrency
) có thể sử dụng các lõi CPU hiệu quả hơn, để tăng tốc tiến trình tổng thể của dexopt. Điều này đặc biệt hữu ích trong giai đoạn
khởi động.
Tuy nhiên, việc có quá nhiều lệnh gọi dex2oat có thể khiến thiết bị bị hết
bộ nhớ, mặc dù bạn có thể giảm thiểu điều này bằng cách đặt dalvik.vm.dex2oat-swap
thành
true
để cho phép sử dụng tệp hoán đổi. Quá nhiều lệnh gọi cũng có thể khiến
chuyển đổi ngữ cảnh không cần thiết. Do đó, bạn nên điều chỉnh số lượng này một cách cẩn thận theo từng sản phẩm.
pm.dexopt.hạ cấp_after_Inactive_days (mặc định: chưa đặt)
Nếu bạn đặt tùy chọn này, thì Dịch vụ ART chỉ sử dụng các ứng dụng được sử dụng trong phạm vi gần đây nhất số ngày.
Ngoài ra, nếu bộ nhớ gần hết, trong quá trình chuyển đổi dexopt ở chế độ nền, Dịch vụ ART
hạ cấp bộ lọc trình biên dịch của các ứng dụng không được dùng trong phạm vi ngày gần đây nhất
số ngày để giải phóng dung lượng. Lý do trình biên dịch là inactive
và bộ lọc trình biên dịch được xác định bởi pm.dexopt.inactive
. Ngưỡng dung lượng để kích hoạt tính năng này là ngưỡng dung lượng thấp của Trình quản lý bộ nhớ (có thể định cấu hình thông qua chế độ cài đặt chung sys_storage_threshold_percentage
và sys_storage_threshold_max_bytes
, mặc định: 500 MB) cộng với 500 MB.
Nếu bạn tuỳ chỉnh danh sách gói thông qua ArtManagerLocal#setBatchDexoptStartCallback
, thì các gói trong danh sách do BatchDexoptStartCallback
cung cấp cho bg-dexopt
sẽ không bao giờ bị hạ cấp.
pm.dexopt.disable_bg_dexopt (mặc định: false)
Nội dung này chỉ dành cho mục đích thử nghiệm. Điều này ngăn Dịch vụ ART lên lịch công việc dexopt ở chế độ nền.
Nếu công việc dexopt ở chế độ nền đã được lên lịch nhưng chưa chạy, không có tác dụng. Tức là công việc vẫn sẽ chạy.
Trình tự lệnh được đề xuất để ngăn tác vụ dexopt ở chế độ nền chạy là:
setprop pm.dexopt.disable_bg_dexopt true
pm bg-dexopt-job --disable
Dòng đầu tiên ngăn việc lên lịch công việc dexopt ở chế độ nền nếu công việc này chưa được lên lịch. Dòng thứ hai sẽ huỷ lịch biểu công việc dexopt ở chế độ nền (nếu đã lên lịch) và huỷ ngay công việc dexopt ở chế độ nền (nếu đang chạy).
API Dịch vụ ART
Dịch vụ ART hiển thị các API Java để tuỳ chỉnh. Các API được xác định trong ArtManagerLocal
. Xem Javadoc trong art/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
để biết cách sử dụng (nguồn Android 14, nguồn phát triển chưa phát hành).
ArtManagerLocal
là một singleton do LocalManagerRegistry
giữ. Hàm trợ giúp com.android.server.pm.DexOptHelper#getArtManagerLocal
giúp bạn lấy thông tin đó.
import static com.android.server.pm.DexOptHelper.getArtManagerLocal;
Hầu hết các API đều yêu cầu một thực thể của PackageManagerLocal.FilteredSnapshot
,
nơi lưu giữ thông tin của tất cả các ứng dụng. Bạn có thể nhận được bằng cách gọi điện
PackageManagerLocal#withFilteredSnapshot
, trong đó PackageManagerLocal
cũng nằm
một singleton do LocalManagerRegistry
nắm giữ và có thể lấy từ
com.android.server.pm.PackageManagerServiceUtils#getPackageManagerLocal
.
import static com.android.server.pm.PackageManagerServiceUtils.getPackageManagerLocal;
Sau đây là một số trường hợp sử dụng điển hình của các API.
Kích hoạt dexopt cho ứng dụng
Bạn có thể kích hoạt tính năng dexopt cho bất kỳ ứng dụng nào vào bất cứ lúc nào bằng cách gọi
ArtManagerLocal#dexoptPackage
.
try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
getArtManagerLocal().dexoptPackage(
snapshot,
"com.google.android.calculator",
new DexoptParams.Builder(ReasonMapping.REASON_INSTALL).build());
}
Bạn cũng có thể truyền lý do dexopt của riêng mình. Nếu bạn làm như vậy, bạn phải đặt rõ ràng lớp ưu tiên và bộ lọc trình biên dịch.
try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
getArtManagerLocal().dexoptPackage(
snapshot,
"com.google.android.calculator",
new DexoptParams.Builder("my-reason")
.setCompilerFilter("speed-profile")
.setPriorityClass(ArtFlags.PRIORITY_BACKGROUND)
.build());
}
Huỷ dexopt
Nếu một thao tác được khởi tạo bởi lệnh gọi dexoptPackage
, bạn có thể truyền một
tín hiệu huỷ, cho phép bạn huỷ hoạt động vào một thời điểm nào đó. Điều này có thể hữu ích khi bạn chạy dexopt không đồng bộ.
Executor executor = ...; // Your asynchronous executor here.
var cancellationSignal = new CancellationSignal();
executor.execute(() -> {
try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
getArtManagerLocal().dexoptPackage(
snapshot,
"com.google.android.calculator",
new DexoptParams.Builder(ReasonMapping.REASON_INSTALL).build(),
cancellationSignal);
}
});
// When you want to cancel the operation.
cancellationSignal.cancel();
Bạn cũng có thể huỷ tính năng dexopt ở chế độ nền do Dịch vụ ART khởi tạo.
getArtManagerLocal().cancelBackgroundDexoptJob();
Nhận kết quả dexopt
Nếu một thao tác được bắt đầu bằng lệnh gọi dexoptPackage
, bạn có thể lấy kết quả từ giá trị trả về.
DexoptResult result;
try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
result = getArtManagerLocal().dexoptPackage(...);
}
// Process the result here.
...
Dịch vụ ART cũng tự khởi tạo các thao tác dexopt trong nhiều trường hợp, chẳng hạn như dexopt trong nền. Để nghe tất cả kết quả dexopt, cho dù thao tác có
do lệnh gọi dexoptPackage
hoặc do Dịch vụ ART khởi tạo, hãy sử dụng
ArtManagerLocal#addDexoptDoneCallback
.
getArtManagerLocal().addDexoptDoneCallback(
false /* onlyIncludeUpdates */,
Runnable::run,
(result) -> {
// Process the result here.
...
});
Đối số đầu tiên xác định xem chỉ đưa nội dung cập nhật vào kết quả hay không. Nếu bạn chỉ muốn nghe các gói được cập nhật bằng dexopt, hãy đặt giá trị này thành true.
Đối số thứ hai là trình thực thi của lệnh gọi lại. Để thực thi lệnh gọi lại trên cùng một luồng thực hiện dexopt, hãy sử dụng Runnable::run
. Nếu bạn không muốn lệnh gọi lại chặn dexopt, hãy sử dụng trình thực thi không đồng bộ.
Bạn có thể thêm nhiều lệnh gọi lại và Dịch vụ ART sẽ thực thi tất cả các lệnh gọi lại đó tuần tự. Tất cả lệnh gọi lại sẽ vẫn hoạt động cho tất cả các lệnh gọi trong tương lai, trừ khi bạn xoá các lệnh gọi đó.
Nếu bạn muốn loại bỏ lệnh gọi lại, hãy giữ lại tham chiếu của lệnh gọi lại khi bạn
thêm dữ liệu và sử dụng ArtManagerLocal#removeDexoptDoneCallback
.
DexoptDoneCallback callback = (result) -> {
// Process the result here.
...
};
getArtManagerLocal().addDexoptDoneCallback(
false /* onlyIncludeUpdates */, Runnable::run, callback);
// When you want to remove it.
getArtManagerLocal().removeDexoptDoneCallback(callback);
Tuỳ chỉnh danh sách gói và thông số dexopt
Dịch vụ ART bắt đầu các thao tác dexopt trong quá trình khởi động và ở chế độ nền
dexopt. Để tuỳ chỉnh danh sách gói hoặc các tham số dexopt cho các thao tác đó,
sử dụng ArtManagerLocal#setBatchDexoptStartCallback
.
getArtManagerLocal().setBatchDexoptStartCallback(
Runnable::run,
(snapshot, reason, defaultPackages, builder, cancellationSignal) -> {
switch (reason) {
case ReasonMapping.REASON_BG_DEXOPT:
var myPackages = new ArrayList<String>(defaultPackages);
myPackages.add(...);
myPackages.remove(...);
myPackages.sort(...);
builder.setPackages(myPackages);
break;
default:
// Ignore unknown reasons.
}
});
Bạn có thể thêm các mục vào danh sách gói, xoá các mục khỏi danh sách, sắp xếp danh sách hoặc thậm chí sử dụng một danh sách hoàn toàn khác.
Lệnh gọi lại của bạn phải bỏ qua các nguyên nhân không xác định vì có thể có nhiều lý do khác trong lệnh gọi lại tương lai.
Bạn có thể đặt tối đa một BatchDexoptStartCallback
. Lệnh gọi lại sẽ vẫn hoạt động cho tất cả các lệnh gọi trong tương lai, trừ phi bạn xoá lệnh gọi đó.
Nếu bạn muốn xoá lệnh gọi lại, hãy sử dụng ArtManagerLocal#clearBatchDexoptStartCallback
.
getArtManagerLocal().clearBatchDexoptStartCallback();
Tuỳ chỉnh các thông số của công việc dexopt ở chế độ nền
Theo mặc định, công việc dexopt ở chế độ nền sẽ chạy một lần mỗi ngày khi thiết bị ở trạng thái rảnh và đang sạc. Bạn có thể thay đổi chế độ cài đặt này bằng cách sử dụng
ArtManagerLocal#setScheduleBackgroundDexoptJobCallback
.
getArtManagerLocal().setScheduleBackgroundDexoptJobCallback(
Runnable::run,
builder -> {
builder.setPeriodic(TimeUnit.DAYS.toMillis(2));
});
Bạn có thể đặt tối đa một ScheduleBackgroundDexoptJobCallback
. Lệnh gọi lại sẽ
vẫn hoạt động cho tất cả cuộc gọi trong tương lai trừ phi bạn xoá.
Nếu bạn muốn xoá lệnh gọi lại, hãy sử dụng
ArtManagerLocal#clearScheduleBackgroundDexoptJobCallback
.
getArtManagerLocal().clearScheduleBackgroundDexoptJobCallback();
Tạm thời vô hiệu hoá dexopt
Mọi thao tác dexopt do Dịch vụ ART khởi tạo sẽ kích hoạt BatchDexoptStartCallback
. Bạn có thể tiếp tục huỷ các thao tác để tắt dexopt một cách hiệu quả.
Nếu thao tác mà bạn huỷ là dexopt trong nền, thì thao tác đó sẽ tuân theo chính sách thử lại mặc định (30 giây, theo hàm mũ, giới hạn ở 5 giờ).
// Good example.
var shouldDisableDexopt = new AtomicBoolean(false);
getArtManagerLocal().setBatchDexoptStartCallback(
Runnable::run,
(snapshot, reason, defaultPackages, builder, cancellationSignal) -> {
if (shouldDisableDexopt.get()) {
cancellationSignal.cancel();
}
});
// Disable dexopt.
shouldDisableDexopt.set(true);
getArtManagerLocal().cancelBackgroundDexoptJob();
// Re-enable dexopt.
shouldDisableDexopt.set(false);
Bạn có thể có tối đa một BatchDexoptStartCallback
. Nếu bạn cũng muốn sử dụng
BatchDexoptStartCallback
để tuỳ chỉnh danh sách gói hoặc các tham số dexopt,
bạn phải kết hợp mã này vào một lệnh gọi lại.
// Bad example.
// Disable dexopt.
getArtManagerLocal().unscheduleBackgroundDexoptJob();
// Re-enable dexopt.
getArtManagerLocal().scheduleBackgroundDexoptJob();
Thao tác dexopt được thực hiện khi cài đặt ứng dụng không do Dịch vụ ART khởi tạo. Thay vào đó, gói này do trình quản lý gói khởi tạo thông qua
Cuộc gọi dexoptPackage
. Do đó, hàm này không kích hoạt
BatchDexoptStartCallback
. Để tắt dexopt khi cài đặt ứng dụng, hãy ngăn trình quản lý gói gọi dexoptPackage
.
Ghi đè bộ lọc trình biên dịch cho một số gói nhất định (Android 15 trở lên)
Bạn có thể ghi đè bộ lọc trình biên dịch cho một số gói nhất định bằng cách đăng ký
gọi lại thông qua setAdjustCompilerFilterCallback
. Lệnh gọi lại được gọi
bất cứ khi nào một gói được rút khỏi dex, bất kể quá trình dexopt được khởi tạo bằng
Dịch vụ ART trong khi khởi động và dexopt ở chế độ nền hoặc bằng lệnh gọi API dexoptPackage
.
Nếu một gói không cần điều chỉnh, lệnh gọi lại phải trả về originalCompilerFilter
.
getArtManagerLocal().setAdjustCompilerFilterCallback(
Runnable::run,
(packageName, originalCompilerFilter, reason) -> {
if (isVeryImportantPackage(packageName)) {
return "speed-profile";
}
return originalCompilerFilter;
});
Bạn chỉ có thể đặt một AdjustCompilerFilterCallback
. Nếu bạn muốn sử dụng
AdjustCompilerFilterCallback
để ghi đè bộ lọc trình biên dịch cho nhiều
bạn phải kết hợp mã này thành một lệnh gọi lại. Lệnh gọi lại vẫn hoạt động cho tất cả các lệnh gọi trong tương lai, trừ phi bạn xoá lệnh gọi đó.
Nếu bạn muốn xoá lệnh gọi lại, hãy sử dụng
ArtManagerLocal#clearAdjustCompilerFilterCallback
.
getArtManagerLocal().clearAdjustCompilerFilterCallback();
Các chế độ tuỳ chỉnh khác
Dịch vụ ART cũng hỗ trợ một số tuỳ chỉnh khác.
Đặt ngưỡng nhiệt cho dexopt ở chế độ nền
Việc điều khiển nhiệt của công việc dexopt ở chế độ nền do Trình lập lịch biểu công việc thực hiện.
Công việc sẽ bị huỷ ngay lập tức khi nhiệt độ đạt đến
THERMAL_STATUS_MODERATE
. Bạn có thể điều chỉnh ngưỡng của THERMAL_STATUS_MODERATE
.
Xác định xem dexopt trong nền có đang chạy hay không
Công việc dexopt ở chế độ nền do Trình lập lịch biểu công việc quản lý và mã công việc của công việc này là 27873780
. Để xác định xem công việc có đang chạy hay không, hãy sử dụng API Trình lập lịch biểu công việc.
// Good example.
var jobScheduler =
Objects.requireNonNull(mContext.getSystemService(JobScheduler.class));
int reason = jobScheduler.getPendingJobReason(27873780);
if (reason == PENDING_JOB_REASON_EXECUTING) {
// Do something when the job is running.
...
}
// Bad example.
var backgroundDexoptRunning = new AtomicBoolean(false);
getArtManagerLocal().setBatchDexoptStartCallback(
Runnable::run,
(snapshot, reason, defaultPackages, builder, cancellationSignal) -> {
if (reason.equals(ReasonMapping.REASON_BG_DEXOPT)) {
backgroundDexoptRunning.set(true);
}
});
getArtManagerLocal().addDexoptDoneCallback(
false /* onlyIncludeUpdates */,
Runnable::run,
(result) -> {
if (result.getReason().equals(ReasonMapping.REASON_BG_DEXOPT)) {
backgroundDexoptRunning.set(false);
}
});
if (backgroundDexoptRunning.get()) {
// Do something when the job is running.
...
}
Cung cấp hồ sơ cho dexopt
Để sử dụng hồ sơ nhằm hướng dẫn dexopt, hãy đặt tệp .prof
hoặc tệp .dm
bên cạnh tệp APK.
Tệp .prof
phải là tệp cấu hình ở định dạng tệp nhị phân và tên tệp phải là tên tệp của APK + .prof
. Ví dụ:
base.apk.prof
Tên tệp của tệp .dm
phải là tên tệp của tệp APK, trong đó đuôi tệp được thay thế bằng .dm
. Ví dụ:
base.dm
Để xác minh rằng hồ sơ đang được sử dụng cho dexopt, hãy chạy dexopt bằng speed-profile
và kiểm tra kết quả.
pm art clear-app-profiles <package-name>
pm compile -m speed-profile -f -v <package-name>
Dòng đầu tiên xoá tất cả hồ sơ do môi trường thời gian chạy tạo (tức là những hồ sơ trong
/data/misc/profiles
), nếu có, để đảm bảo rằng hồ sơ bên cạnh APK
hồ sơ duy nhất mà Dịch vụ ART có thể sử dụng. Dòng thứ hai chạy dexopt
bằng speed-profile
và truyền -v
để in kết quả chi tiết.
Nếu hồ sơ đang được sử dụng, bạn sẽ thấy actualCompilerFilter=speed-profile
trong kết quả. Nếu không, bạn sẽ thấy actualCompilerFilter=verify
. Ví dụ:
DexContainerFileDexoptResult{dexContainerFile=/data/app/~~QR0fTV0UbDbIP1Su7XzyPg==/com.google.android.gms-LvusF2uARKOtBbcaPHdUtQ==/base.apk, primaryAbi=true, abi=x86_64, actualCompilerFilter=speed-profile, status=PERFORMED, dex2oatWallTimeMillis=4549, dex2oatCpuTimeMillis=14550, sizeBytes=3715344, sizeBeforeBytes=3715344}
Sau đây là một số lý do phổ biến khiến Dịch vụ ART không sử dụng hồ sơ:
- Hồ sơ có tên tệp không chính xác hoặc không nằm bên cạnh tệp APK.
- Hồ sơ bị sai định dạng.
- Hồ sơ không khớp với APK. (Checksum trong hồ sơ không khớp với checksum của các tệp
.dex
trong APK.)